From ed79ba28c5ffd573a6a98a28e1d374d5d1716686 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 16 Dec 2024 17:44:16 +0700 Subject: [PATCH 01/39] update --- pkg/valueobject/chain.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/valueobject/chain.go b/pkg/valueobject/chain.go index e150db2f3..a0a99857f 100644 --- a/pkg/valueobject/chain.go +++ b/pkg/valueobject/chain.go @@ -114,6 +114,8 @@ func ToString(chainID ChainID) (string, error) { return "sonic", nil case 0: return "solana", nil + case 146: + return "sonic", nil default: return "", ErrChainUnsupported } From 188895faff840c55d8a5566c2a54168c18e6eb56 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 23 Dec 2024 10:15:43 +0700 Subject: [PATCH 02/39] add new dex --- pkg/valueobject/chain.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/valueobject/chain.go b/pkg/valueobject/chain.go index a0a99857f..e150db2f3 100644 --- a/pkg/valueobject/chain.go +++ b/pkg/valueobject/chain.go @@ -114,8 +114,6 @@ func ToString(chainID ChainID) (string, error) { return "sonic", nil case 0: return "solana", nil - case 146: - return "sonic", nil default: return "", ErrChainUnsupported } From b6eaf9722f62411f55edf90c69b6bbc0f7f71160 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 30 Dec 2024 00:25:40 +0700 Subject: [PATCH 03/39] tmp --- pkg/liquidity-source/balancer-v3/gyro/abis.go | 28 + .../gyro/abis/ComposableStablePool.json | 1422 +++++++++++++++++ .../balancer-v3/gyro/bpt_swap.go | 862 ++++++++++ .../balancer-v3/gyro/config.go | 12 + .../balancer-v3/gyro/constant.go | 38 + .../balancer-v3/gyro/embed.go | 6 + .../balancer-v3/gyro/error.go | 12 + .../balancer-v3/gyro/pool_simulator.go | 298 ++++ .../balancer-v3/gyro/pool_simulator_test.go | 1041 ++++++++++++ .../balancer-v3/gyro/pool_tracker.go | 478 ++++++ .../balancer-v3/gyro/pools_list_updater.go | 197 +++ .../balancer-v3/gyro/regular_swap.go | 206 +++ pkg/liquidity-source/balancer-v3/gyro/type.go | 107 ++ .../balancer-v3/math/constant.go | 11 + .../balancer-v3/math/fixed_point.go | 183 +++ .../balancer-v3/math/fixed_point_test.go | 25 + .../balancer-v3/math/log_exp_math.go | 389 +++++ .../balancer-v3/math/log_exp_math_test.go | 48 + pkg/liquidity-source/balancer-v3/math/math.go | 108 ++ .../balancer-v3/math/stable_math.go | 238 +++ .../balancer-v3/math/stable_math_test.go | 186 +++ .../balancer-v3/math/weighted_math.go | 200 +++ .../balancer-v3/math/weighted_math_test.go | 158 ++ .../balancer-v3/shared/abis.go | 28 + .../balancer-v3/shared/abis/Vault.json | 1179 ++++++++++++++ .../balancer-v3/shared/constant.go | 26 + .../balancer-v3/shared/embed.go | 6 + .../balancer-v3/shared/error.go | 8 + .../balancer-v3/shared/pools_list_updater.go | 99 ++ .../balancer-v3/shared/scaling_helper.go | 45 + .../balancer-v3/shared/subgraph.go | 46 + .../balancer-v3/shared/type.go | 22 + .../balancer-v3/shared/vault.go | 120 ++ .../balancer-v3/stable/abis.go | 28 + .../balancer-v3/stable/abis/StablePool.json | 1202 ++++++++++++++ .../balancer-v3/stable/config.go | 10 + .../balancer-v3/stable/constant.go | 11 + .../balancer-v3/stable/embed.go | 6 + .../balancer-v3/stable/error.go | 12 + .../balancer-v3/stable/pool_simulator.go | 333 ++++ .../balancer-v3/stable/pool_simulator_test.go | 356 +++++ .../balancer-v3/stable/pool_tracker.go | 251 +++ .../balancer-v3/stable/pools_list_updater.go | 185 +++ .../balancer-v3/stable/type.go | 68 + .../balancer-v3/weighted/abis.go | 28 + .../weighted/abis/WeightedPool.json | 1155 +++++++++++++ .../balancer-v3/weighted/config.go | 10 + .../balancer-v3/weighted/constant.go | 7 + .../balancer-v3/weighted/embed.go | 6 + .../balancer-v3/weighted/pool_simulator.go | 493 ++++++ .../weighted/pool_simulator_test.go | 767 +++++++++ .../balancer-v3/weighted/pool_tracker.go | 199 +++ .../weighted/pools_list_updater.go | 193 +++ .../balancer-v3/weighted/type.go | 47 + 54 files changed, 13199 insertions(+) create mode 100644 pkg/liquidity-source/balancer-v3/gyro/abis.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json create mode 100644 pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/config.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/constant.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/embed.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/error.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/regular_swap.go create mode 100644 pkg/liquidity-source/balancer-v3/gyro/type.go create mode 100644 pkg/liquidity-source/balancer-v3/math/constant.go create mode 100644 pkg/liquidity-source/balancer-v3/math/fixed_point.go create mode 100644 pkg/liquidity-source/balancer-v3/math/fixed_point_test.go create mode 100644 pkg/liquidity-source/balancer-v3/math/log_exp_math.go create mode 100644 pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go create mode 100644 pkg/liquidity-source/balancer-v3/math/math.go create mode 100644 pkg/liquidity-source/balancer-v3/math/stable_math.go create mode 100644 pkg/liquidity-source/balancer-v3/math/stable_math_test.go create mode 100644 pkg/liquidity-source/balancer-v3/math/weighted_math.go create mode 100644 pkg/liquidity-source/balancer-v3/math/weighted_math_test.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/abis.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/abis/Vault.json create mode 100644 pkg/liquidity-source/balancer-v3/shared/constant.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/embed.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/error.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/scaling_helper.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/subgraph.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/type.go create mode 100644 pkg/liquidity-source/balancer-v3/shared/vault.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/abis.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/abis/StablePool.json create mode 100644 pkg/liquidity-source/balancer-v3/stable/config.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/constant.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/embed.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/error.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/pool_simulator.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/pool_tracker.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go create mode 100644 pkg/liquidity-source/balancer-v3/stable/type.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/abis.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json create mode 100644 pkg/liquidity-source/balancer-v3/weighted/config.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/constant.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/embed.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/type.go diff --git a/pkg/liquidity-source/balancer-v3/gyro/abis.go b/pkg/liquidity-source/balancer-v3/gyro/abis.go new file mode 100644 index 000000000..80b4b26f7 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/abis.go @@ -0,0 +1,28 @@ +package composablestable + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var ( + poolABI abi.ABI +) + +func init() { + builder := []struct { + ABI *abi.ABI + data []byte + }{ + {&poolABI, poolJson}, + } + + for _, b := range builder { + var err error + *b.ABI, err = abi.JSON(bytes.NewReader(b.data)) + if err != nil { + panic(err) + } + } +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json b/pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json new file mode 100644 index 000000000..fa35778fc --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json @@ -0,0 +1,1422 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "contract IVault", + "name": "vault", + "type": "address" + }, + { + "internalType": "contract IProtocolFeePercentagesProvider", + "name": "protocolFeeProvider", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "contract IRateProvider[]", + "name": "rateProviders", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRateCacheDurations", + "type": "uint256[]" + }, + { + "internalType": "bool", + "name": "exemptFromYieldProtocolFeeFlag", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amplificationParameter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pauseWindowDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodDuration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "internalType": "struct ComposableStablePool.NewPoolParams", + "name": "params", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "startValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + } + ], + "name": "AmpUpdateStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "currentValue", + "type": "uint256" + } + ], + "name": "AmpUpdateStopped", + "type": "event" + }, + { + "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": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "feeType", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "protocolFeePercentage", + "type": "uint256" + } + ], + "name": "ProtocolFeePercentageCacheUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "RecoveryModeStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "SwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "name": "TokenRateCacheUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IRateProvider", + "name": "provider", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cacheDuration", + "type": "uint256" + } + ], + "name": "TokenRateProviderSet", + "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" + }, + { + "inputs": [], + "name": "DELEGATE_PROTOCOL_SWAP_FEES_SENTINEL", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "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": "amount", + "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": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "disableRecoveryMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableRecoveryMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getActionId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getActualSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAmplificationParameter", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isUpdating", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "precision", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "contract IAuthorizer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBptIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLastJoinExitData", + "outputs": [ + { + "internalType": "uint256", + "name": "lastJoinExitAmplification", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastPostJoinExitInvariant", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumBpt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getNextNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "paused", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "pauseWindowEndTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodEndTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPoolId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "feeType", + "type": "uint256" + } + ], + "name": "getProtocolFeePercentageCache", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolFeesCollector", + "outputs": [ + { + "internalType": "contract IProtocolFeesCollector", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolSwapFeeDelegation", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRateProviders", + "outputs": [ + { + "internalType": "contract IRateProvider[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getScalingFactors", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenRateCache", + "outputs": [ + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "oldRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expires", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [ + { + "internalType": "contract IVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inRecoveryMode", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isExemptFromYieldProtocolFee", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "isTokenExemptFromYieldProtocolFee", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "onExitPool", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "onJoinPool", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum IVault.SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct IPoolSwapStructs.SwapRequest", + "name": "swapRequest", + "type": "tuple" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "indexIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "indexOut", + "type": "uint256" + } + ], + "name": "onSwap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "queryExit", + "outputs": [ + { + "internalType": "uint256", + "name": "bptIn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "amountsOut", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "queryJoin", + "outputs": [ + { + "internalType": "uint256", + "name": "bptOut", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "amountsIn", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "bytes", + "name": "poolConfig", + "type": "bytes" + } + ], + "name": "setAssetManagerPoolConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "setSwapFeePercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "setTokenRateCacheDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "rawEndValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + } + ], + "name": "startAmplificationParameterUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stopAmplificationParameterUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "updateProtocolFeePercentageCache", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "updateTokenRateCache", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go b/pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go new file mode 100644 index 000000000..d61ea694f --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go @@ -0,0 +1,862 @@ +package composablestable + +import ( + "math/big" + + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/valueobject" +) + +type bptSimulator struct { + poolpkg.Pool + + bptIndex int + bptTotalSupply *uint256.Int + amp *uint256.Int + scalingFactors []*uint256.Int + lastJoinExit LastJoinExitData + rateProviders []string + tokenRateCaches []TokenRateCache + + swapFeePercentage *uint256.Int + protocolFeePercentageCache map[int]*uint256.Int + tokenExemptFromYieldProtocolFee []bool + exemptFromYieldProtocolFee bool // >= V5 + inRecoveryMode bool + + poolTypeVer int +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L301 +/** + * @dev Perform a swap involving the BPT token, equivalent to a single-token join or exit. As with the standard + * joins and swaps, we first pay any protocol fees pending from swaps that occurred since the previous join or + * exit, then perform the operation (joinSwap or exitSwap), and finally store the "post operation" invariant and + * amp, which establishes the new basis for protocol fees. + * + * At this point, the scaling factors (including rates) have been computed by the base class, but not yet applied + * to the balances. + */ +func (s *bptSimulator) _swapWithBpt( + isGivenIn bool, + swapRequestAmount *uint256.Int, + balances []*uint256.Int, + registeredIndexIn int, + registeredIndexOut int, +) (*uint256.Int, *poolpkg.TokenAmount, *SwapInfo, error) { + balances, err := _upscaleArray(balances, s.scalingFactors) + if err != nil { + return nil, nil, nil, err + } + + var swapRequestTokenIndex int + if isGivenIn { + swapRequestTokenIndex = registeredIndexIn + } else { + swapRequestTokenIndex = registeredIndexOut + } + + swapRequestAmount, err = _upscale(swapRequestAmount, s.scalingFactors[swapRequestTokenIndex]) + if err != nil { + return nil, nil, nil, err + } + + preJoinExitSupply, balances, currentAmp, preJoinExitInvariant, err := s._beforeJoinExit(balances) + if err != nil { + return nil, nil, nil, err + } + + var amountCalculated, postJoinExitSupply *uint256.Int + if registeredIndexOut == s.bptIndex { + amountCalculated, postJoinExitSupply, err = s._doJoinSwap( + isGivenIn, swapRequestAmount, balances, _skipBptIndex(registeredIndexIn, s.bptIndex), currentAmp, preJoinExitSupply, preJoinExitInvariant, + ) + } else { + amountCalculated, postJoinExitSupply, err = s._doExitSwap( + isGivenIn, swapRequestAmount, balances, _skipBptIndex(registeredIndexOut, s.bptIndex), currentAmp, preJoinExitSupply, preJoinExitInvariant, + ) + } + if err != nil { + return nil, nil, nil, err + } + + var downscaledAmountCalculated *uint256.Int + if isGivenIn { + downscaledAmountCalculated, err = _downscaleDown(amountCalculated, s.scalingFactors[registeredIndexOut]) + if err != nil { + return nil, nil, nil, err + } + } else { + downscaledAmountCalculated, err = _downscaleUp(amountCalculated, s.scalingFactors[registeredIndexIn]) + if err != nil { + return nil, nil, nil, err + } + } + + swapInfo, err := s.initSwapInfo( + currentAmp, + balances, + preJoinExitInvariant, + preJoinExitSupply, + postJoinExitSupply, + ) + if err != nil { + return nil, nil, nil, err + } + + return downscaledAmountCalculated, &poolpkg.TokenAmount{}, swapInfo, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L362 +/** + * @dev This mutates `balances` so that they become the post-joinswap balances. The StableMath interfaces + * are different depending on the swap direction, so we forward to the appropriate low-level join function. + */ +func (s *bptSimulator) _doJoinSwap( + isGivenIn bool, + amount *uint256.Int, + balances []*uint256.Int, + indexIn int, + currentAmp *uint256.Int, + actualSupply *uint256.Int, + preJoinExitInvariant *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + if isGivenIn { + return s._joinSwapExactTokenInForBptOut( + amount, + balances, + indexIn, + currentAmp, + actualSupply, + preJoinExitInvariant, + ) + } + + return s._joinSwapExactBptOutForTokenIn( + amount, + balances, + indexIn, + currentAmp, + actualSupply, + preJoinExitInvariant, + ) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L396 +/** + * @dev Since this is a join, we know the tokenOut is BPT. Since it is GivenIn, we know the tokenIn amount, + * and must calculate the BPT amount out. + * We are moving preminted BPT out of the Vault, which increases the virtual supply. + */ +func (s *bptSimulator) _joinSwapExactTokenInForBptOut( + amountIn *uint256.Int, + balances []*uint256.Int, + indexIn int, + currentAmp *uint256.Int, + actualSupply *uint256.Int, + preJoinExitInvariant *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + amountsIn := make([]*uint256.Int, len(balances)) + for i := 0; i < len(balances); i++ { + amountsIn[i] = uint256.NewInt(0) + } + amountsIn[indexIn] = amountIn + + bptOut, err := math.StableMath.CalcBptOutGivenExactTokensIn( + currentAmp, + balances, + amountsIn, + actualSupply, + preJoinExitInvariant, + s.swapFeePercentage, + ) + if err != nil { + return nil, nil, err + } + + balances[indexIn], err = math.FixedPoint.Add(balances[indexIn], amountIn) + if err != nil { + return nil, nil, err + } + + postJoinExitSupply, err := math.FixedPoint.Add(actualSupply, bptOut) + if err != nil { + return nil, nil, err + } + + return bptOut, postJoinExitSupply, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L429 +/** + * @dev Since this is a join, we know the tokenOut is BPT. Since it is GivenOut, we know the BPT amount, + * and must calculate the token amount in. + * We are moving preminted BPT out of the Vault, which increases the virtual supply. + */ +func (s *bptSimulator) _joinSwapExactBptOutForTokenIn( + bptOut *uint256.Int, + balances []*uint256.Int, + indexIn int, + currentAmp *uint256.Int, + actualSupply *uint256.Int, + preJoinExitInvariant *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + amountIn, err := math.StableMath.CalcTokenInGivenExactBptOut( + currentAmp, + balances, + indexIn, + bptOut, + actualSupply, + preJoinExitInvariant, + s.swapFeePercentage, + ) + if err != nil { + return nil, nil, err + } + + balances[indexIn], err = math.FixedPoint.Add(balances[indexIn], amountIn) + if err != nil { + return nil, nil, err + } + + postJoinExitSupply, err := math.FixedPoint.Add(actualSupply, bptOut) + if err != nil { + return nil, nil, err + } + + return amountIn, postJoinExitSupply, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L457 +/** + * @dev This mutates balances so that they become the post-exitswap balances. The StableMath interfaces are + * different depending on the swap direction, so we forward to the appropriate low-level exit function. + */ +func (s *bptSimulator) _doExitSwap( + isGivenIn bool, + amount *uint256.Int, + balances []*uint256.Int, + indexOut int, + currentAmp *uint256.Int, + actualSupply *uint256.Int, + preJoinExitInvariant *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + if isGivenIn { + return s._exitSwapExactBptInForTokenOut( + amount, + balances, + indexOut, + currentAmp, + actualSupply, + preJoinExitInvariant, + ) + } + + return s._exitSwapExactTokenOutForBptIn( + amount, + balances, + indexOut, + currentAmp, + actualSupply, + preJoinExitInvariant, + ) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L491 +/** + * @dev Since this is an exit, we know the tokenIn is BPT. Since it is GivenIn, we know the BPT amount, + * and must calculate the token amount out. + * We are moving BPT out of circulation and into the Vault, which decreases the virtual supply. + */ +func (s *bptSimulator) _exitSwapExactBptInForTokenOut( + bptAmount *uint256.Int, + balances []*uint256.Int, + indexOut int, + currentAmp *uint256.Int, + actualSupply *uint256.Int, + preJoinExitInvariant *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + amountOut, err := math.StableMath.CalcTokenOutGivenExactBptIn( + currentAmp, + balances, + indexOut, + bptAmount, + actualSupply, + preJoinExitInvariant, + s.swapFeePercentage, + ) + if err != nil { + return nil, nil, err + } + + balances[indexOut], err = math.FixedPoint.Sub(balances[indexOut], amountOut) + if err != nil { + return nil, nil, err + } + + postJoinExitSupply, err := math.FixedPoint.Sub(actualSupply, bptAmount) + if err != nil { + return nil, nil, err + } + + return amountOut, postJoinExitSupply, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L520 +/** + * @dev Since this is an exit, we know the tokenIn is BPT. Since it is GivenOut, we know the token amount out, + * and must calculate the BPT amount in. + * We are moving BPT out of circulation and into the Vault, which decreases the virtual supply. + */ +func (s *bptSimulator) _exitSwapExactTokenOutForBptIn( + amountOut *uint256.Int, + balances []*uint256.Int, + indexOut int, + currentAmp *uint256.Int, + actualSupply *uint256.Int, + preJoinExitInvariant *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + // The StableMath function was created with exits in mind, so it expects a full amounts array. We create an + // empty one and only set the amount for the token involved. + + amountsOut := make([]*uint256.Int, len(balances)) + for i := 0; i < len(balances); i++ { + amountsOut[i] = uint256.NewInt(0) + } + amountsOut[indexOut] = amountOut + + bptAmount, err := math.StableMath.CalcBptInGivenExactTokensOut( + currentAmp, + balances, + amountsOut, + actualSupply, + preJoinExitInvariant, + s.swapFeePercentage, + ) + if err != nil { + return nil, nil, err + } + + balances[indexOut], err = math.FixedPoint.Sub(balances[indexOut], amountOut) + if err != nil { + return nil, nil, err + } + + postJoinExitSupply, err := math.FixedPoint.Sub(actualSupply, bptAmount) + if err != nil { + return nil, nil, err + } + + return bptAmount, postJoinExitSupply, nil +} + +func (s *bptSimulator) _getVirtualSupply(bptBalance *uint256.Int) (*uint256.Int, error) { + cir, err := math.FixedPoint.Sub(s.bptTotalSupply, bptBalance) + if err != nil { + return nil, err + } + return cir, nil +} + +func (s *bptSimulator) _hasRateProvider(tokenIndex int) bool { + if s.rateProviders[tokenIndex] == "" || s.rateProviders[tokenIndex] == valueobject.ZeroAddress { + return false + } + return true +} + +func (s *bptSimulator) _beforeJoinExit( + registeredBalances []*uint256.Int, +) (*uint256.Int, []*uint256.Int, *uint256.Int, *uint256.Int, error) { + preJoinExitSupply, balances, oldAmpPreJoinExitInvariant, err := s._payProtocolFeesBeforeJoinExit(registeredBalances) + if err != nil { + return nil, nil, nil, nil, err + } + + var preJoinExitInvariant *uint256.Int + if s.amp.Eq(s.lastJoinExit.LastJoinExitAmplification) { + preJoinExitInvariant = oldAmpPreJoinExitInvariant + } else { + preJoinExitInvariant, err = math.StableMath.CalculateInvariantV2( + s.amp, + balances, + ) + if err != nil { + return nil, nil, nil, nil, err + } + } + + return preJoinExitSupply, balances, s.amp, preJoinExitInvariant, nil +} + +func (s *bptSimulator) _payProtocolFeesBeforeJoinExit( + registeredBalances []*uint256.Int, +) (*uint256.Int, []*uint256.Int, *uint256.Int, error) { + virtualSupply, balances, err := s._dropBptItemFromBalances(registeredBalances) + if err != nil { + return nil, nil, nil, err + } + + expectedProtocolOwnershipPercentage, currentInvariantWithLastJoinExitAmp, err := s._getProtocolPoolOwnershipPercentage(balances) + if err != nil { + return nil, nil, nil, err + } + + protocolFeeAmount, err := s.protocolFeeAmount(virtualSupply, expectedProtocolOwnershipPercentage) + if err != nil { + return nil, nil, nil, err + } + + return new(uint256.Int).Add(virtualSupply, protocolFeeAmount), + balances, + currentInvariantWithLastJoinExitAmp, + nil +} + +func (s *bptSimulator) _getProtocolPoolOwnershipPercentage( + balances []*uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + if s.poolTypeVer == poolTypeVer5 { + return s._getProtocolPoolOwnershipPercentageV2(balances) + } + return s._getProtocolPoolOwnershipPercentageV1(balances) +} + +func (s *bptSimulator) _getProtocolPoolOwnershipPercentageV2( + balances []*uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, err := s._getGrowthInvariantsV2(balances) + if err != nil { + return nil, nil, err + } + + if totalGrowthInvariant.Cmp(s.lastJoinExit.LastPostJoinExitInvariant) <= 0 { + return uint256.NewInt(0), totalGrowthInvariant, nil + } + + swapFeeGrowthInvariantDelta := new(uint256.Int).Sub( + swapFeeGrowthInvariant, s.lastJoinExit.LastPostJoinExitInvariant, + ) + + nonExemptYieldGrowthInvariantDelta := new(uint256.Int).Sub( + totalNonExemptGrowthInvariant, swapFeeGrowthInvariant, + ) + + var protocolSwapFeePercentage *uint256.Int + { + percentage := s.getProtocolFeePercentageCache(feeTypeSwap) + u, err := math.FixedPoint.DivDown(swapFeeGrowthInvariantDelta, totalGrowthInvariant) + if err != nil { + return nil, nil, err + } + u, err = math.FixedPoint.MulDown(u, percentage) + if err != nil { + return nil, nil, err + } + + protocolSwapFeePercentage = u + } + + var protocolYieldPercentage *uint256.Int + { + percentage := s.getProtocolFeePercentageCache(feeTypeYield) + u, err := math.FixedPoint.DivDown( + nonExemptYieldGrowthInvariantDelta, + totalGrowthInvariant, + ) + if err != nil { + return nil, nil, err + } + + u, err = math.FixedPoint.MulDown(u, percentage) + if err != nil { + return nil, nil, err + } + + protocolYieldPercentage = u + } + + return new(uint256.Int).Add(protocolSwapFeePercentage, protocolYieldPercentage), totalGrowthInvariant, nil +} + +func (s *bptSimulator) _getProtocolPoolOwnershipPercentageV1( + balances []*uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, err := s._getGrowthInvariantsV1(balances) + if err != nil { + return nil, nil, err + } + + swapFeeGrowthInvariantDelta := uint256.NewInt(0) + if swapFeeGrowthInvariant.Gt(s.lastJoinExit.LastPostJoinExitInvariant) { + swapFeeGrowthInvariantDelta = new(uint256.Int).Sub( + swapFeeGrowthInvariant, s.lastJoinExit.LastPostJoinExitInvariant, + ) + } + + nonExemptYieldGrowthInvariantDelta := uint256.NewInt(0) + if totalNonExemptGrowthInvariant.Gt(swapFeeGrowthInvariant) { + nonExemptYieldGrowthInvariantDelta = new(uint256.Int).Sub( + totalNonExemptGrowthInvariant, swapFeeGrowthInvariant, + ) + } + + var protocolSwapFeePercentage *uint256.Int + { + percentage := s.getProtocolFeePercentageCache(feeTypeSwap) + u, err := math.FixedPoint.DivDown(swapFeeGrowthInvariantDelta, totalGrowthInvariant) + if err != nil { + return nil, nil, err + } + u, err = math.FixedPoint.MulDown(u, percentage) + if err != nil { + return nil, nil, err + } + + protocolSwapFeePercentage = u + } + + var protocolYieldPercentage *uint256.Int + { + percentage := s.getProtocolFeePercentageCache(feeTypeYield) + u, err := math.FixedPoint.DivDown( + nonExemptYieldGrowthInvariantDelta, + totalGrowthInvariant, + ) + if err != nil { + return nil, nil, err + } + + u, err = math.FixedPoint.MulDown(u, percentage) + if err != nil { + return nil, nil, err + } + + protocolYieldPercentage = u + } + + return new(uint256.Int).Add(protocolSwapFeePercentage, protocolYieldPercentage), totalGrowthInvariant, nil +} + +func (s *bptSimulator) _isTokenExemptFromYieldProtocolFee(registeredTokenIndex int) bool { + return s.tokenExemptFromYieldProtocolFee[registeredTokenIndex] +} + +func (s *bptSimulator) _getGrowthInvariantsV1( + balances []*uint256.Int, +) (*uint256.Int, *uint256.Int, *uint256.Int, error) { + var ( + swapFeeGrowthInvariant *uint256.Int + totalNonExemptGrowthInvariant *uint256.Int + totalGrowthInvariant *uint256.Int + err error + ) + + adjustedBalances, err := s._getAdjustedBalanceV1(balances, true) + if err != nil { + return nil, nil, nil, err + } + + swapFeeGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, + adjustedBalances, + ) + if err != nil { + return nil, nil, nil, err + } + + if s._areNoTokensExempt() { + totalNonExemptGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, + balances, + ) + if err != nil { + return nil, nil, nil, err + } + + totalGrowthInvariant = totalNonExemptGrowthInvariant + } else if s._areAllTokensExempt() { + totalNonExemptGrowthInvariant = swapFeeGrowthInvariant + totalGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, balances, + ) + if err != nil { + return nil, nil, nil, err + } + } else { + adjustedBalances, err := s._getAdjustedBalanceV1(balances, false) + if err != nil { + return nil, nil, nil, err + } + + totalNonExemptGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, + adjustedBalances, + ) + if err != nil { + return nil, nil, nil, err + } + + totalGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, + balances, + ) + if err != nil { + return nil, nil, nil, err + } + } + + return swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, nil +} + +func (s *bptSimulator) _getAdjustedBalanceV1( + balances []*uint256.Int, + ignoreExemptFlags bool, +) ([]*uint256.Int, error) { + totalTokensWithoutBpt := len(balances) + adjustedBalances := make([]*uint256.Int, totalTokensWithoutBpt) + + for i := 0; i < totalTokensWithoutBpt; i++ { + skipBptIndex := i + if i >= s.bptIndex { + skipBptIndex++ + } + + if s._isTokenExemptFromYieldProtocolFee(skipBptIndex) || + (ignoreExemptFlags && s._hasRateProvider(skipBptIndex)) { + var err error + adjustedBalances[i], err = _adjustedBalance(balances[i], s.tokenRateCaches[skipBptIndex]) + if err != nil { + return nil, err + } + + continue + } + + adjustedBalances[i] = balances[i] + } + + return adjustedBalances, nil +} + +func (s *bptSimulator) _getGrowthInvariantsV2( + balances []*uint256.Int, +) (*uint256.Int, *uint256.Int, *uint256.Int, error) { + var ( + swapFeeGrowthInvariant *uint256.Int + totalNonExemptGrowthInvariant *uint256.Int + totalGrowthInvariant *uint256.Int + err error + ) + + totalGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, + balances, + ) + if err != nil { + return nil, nil, nil, err + } + + if totalGrowthInvariant.Cmp(s.lastJoinExit.LastPostJoinExitInvariant) <= 0 { + return totalGrowthInvariant, totalGrowthInvariant, totalGrowthInvariant, nil + } + + adjustedBalances, err := s._getAdjustedBalanceV2(balances) + if err != nil { + return nil, nil, nil, err + } + swapFeeGrowthInvariant, err = math.StableMath.CalculateInvariantV2( + s.lastJoinExit.LastJoinExitAmplification, + adjustedBalances, + ) + if err != nil { + return nil, nil, nil, err + } + + swapFeeGrowthInvariant = math.Math.Min(totalGrowthInvariant, swapFeeGrowthInvariant) + swapFeeGrowthInvariant = math.Math.Max(s.lastJoinExit.LastPostJoinExitInvariant, swapFeeGrowthInvariant) + + if s.isExemptFromYieldProtocolFee() { + totalNonExemptGrowthInvariant = swapFeeGrowthInvariant + } else { + totalNonExemptGrowthInvariant = totalGrowthInvariant + } + + return swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, nil +} + +func (s *bptSimulator) _getAdjustedBalanceV2(balances []*uint256.Int) ([]*uint256.Int, error) { + totalTokensWithoutBpt := len(balances) + adjustedBalances := make([]*uint256.Int, totalTokensWithoutBpt) + + for i := 0; i < totalTokensWithoutBpt; i++ { + skipBptIndex := i + if i >= s.bptIndex { + skipBptIndex++ + } + + if s._hasRateProvider(skipBptIndex) { + var err error + adjustedBalances[i], err = _adjustedBalance(balances[i], s.tokenRateCaches[skipBptIndex]) + if err != nil { + return nil, err + } + continue + } + + adjustedBalances[i] = balances[i] + } + + return adjustedBalances, nil +} + +func (s *bptSimulator) isExemptFromYieldProtocolFee() bool { + return s.exemptFromYieldProtocolFee +} + +func (s *bptSimulator) _areAllTokensExempt() bool { + for _, exempt := range s.tokenExemptFromYieldProtocolFee { + if !exempt { + return false + } + } + return true +} + +func (s *bptSimulator) _areNoTokensExempt() bool { + for _, exempt := range s.tokenExemptFromYieldProtocolFee { + if exempt { + return false + } + } + return true +} + +func (s *bptSimulator) getProtocolFeePercentageCache(feeType int) *uint256.Int { + if s.inRecoveryMode { + return uint256.NewInt(0) + } + + return s.protocolFeePercentageCache[feeType] +} + +func (s *bptSimulator) protocolFeeAmount( + totalSupply *uint256.Int, + poolOwnershipPercentage *uint256.Int, +) (*uint256.Int, error) { + if s.poolTypeVer == poolTypeVer1 { + return s._calculateAdjustedProtocolFeeAmount(totalSupply, poolOwnershipPercentage) + } + + return s.bptForPoolOwnershipPercentage(totalSupply, poolOwnershipPercentage) +} + +func (s *bptSimulator) _calculateAdjustedProtocolFeeAmount( + totalSupply *uint256.Int, + poolOwnershipPercentage *uint256.Int, +) (*uint256.Int, error) { + u, err := math.FixedPoint.MulDown(totalSupply, poolOwnershipPercentage) + if err != nil { + return nil, err + } + + u, err = math.FixedPoint.DivDown(u, math.FixedPoint.Complement(poolOwnershipPercentage)) + if err != nil { + return nil, err + } + + return u, nil +} + +func (s *bptSimulator) bptForPoolOwnershipPercentage( + totalSupply *uint256.Int, + poolOwnershipPercentage *uint256.Int, +) (*uint256.Int, error) { + u, err := math.Math.Mul(totalSupply, poolOwnershipPercentage) + if err != nil { + return nil, err + } + u, err = math.Math.DivDown(u, math.FixedPoint.Complement(poolOwnershipPercentage)) + if err != nil { + return nil, err + } + return u, nil +} + +func (s *bptSimulator) _dropBptItemFromBalances( + registeredBalances []*uint256.Int, +) (*uint256.Int, []*uint256.Int, error) { + virtualSupply, err := s._getVirtualSupply(registeredBalances[s.bptIndex]) + if err != nil { + return nil, nil, err + } + + balances := _dropBptItem(registeredBalances, s.bptIndex) + + return virtualSupply, balances, nil +} + +func (s *bptSimulator) initSwapInfo( + currentAmp *uint256.Int, + balances []*uint256.Int, + preJoinExitInvariant *uint256.Int, + preJoinExitSupply *uint256.Int, + postJoinExitSupply *uint256.Int, +) (*SwapInfo, error) { + postJoinExitInvariant, err := math.StableMath.CalculateInvariantV2(currentAmp, balances) + if err != nil { + return nil, err + } + + swapInfo := &SwapInfo{ + LastJoinExitData: LastJoinExitData{ + LastJoinExitAmplification: currentAmp, + LastPostJoinExitInvariant: postJoinExitInvariant, + }, + } + + return swapInfo, nil +} + +func (s *bptSimulator) updateBalance(params poolpkg.UpdateBalanceParams) { + for idx, token := range s.Info.Tokens { + // update reserves + + if token == params.TokenAmountIn.Token { + s.Info.Reserves[idx] = new(big.Int).Add( + s.Info.Reserves[idx], + params.TokenAmountIn.Amount, + ) + } + + if token == params.TokenAmountOut.Token { + s.Info.Reserves[idx] = new(big.Int).Sub( + s.Info.Reserves[idx], + params.TokenAmountOut.Amount, + ) + } + + // update rates + + if s._hasRateProvider(idx) { + s.tokenRateCaches[idx].OldRate = s.tokenRateCaches[idx].Rate + } + } + + swapInfo, ok := params.SwapInfo.(*SwapInfo) + if !ok { + return + } + s.lastJoinExit = swapInfo.LastJoinExitData +} + +func _adjustedBalance(balance *uint256.Int, cache TokenRateCache) (*uint256.Int, error) { + u, err := math.Math.Mul(balance, cache.OldRate) + if err != nil { + return nil, err + } + return math.Math.DivDown(u, cache.Rate) +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/config.go b/pkg/liquidity-source/balancer-v3/gyro/config.go new file mode 100644 index 000000000..628dd94f2 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/config.go @@ -0,0 +1,12 @@ +package composablestable + +import ( + "net/http" +) + +type Config struct { + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/constant.go b/pkg/liquidity-source/balancer-v3/gyro/constant.go new file mode 100644 index 000000000..0263f1063 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/constant.go @@ -0,0 +1,38 @@ +package composablestable + +const ( + DexType = "balancer-v2-composable-stable" +) + +var ( + DefaultGas = Gas{Swap: 80000} +) + +const ( + poolTypeComposableStable = "ComposableStable" + + poolTypeVer1 = 1 + poolTypeVer5 = 5 + + poolMethodGetSwapFeePercentage = "getSwapFeePercentage" + poolMethodGetPausedState = "getPausedState" + poolMethodGetAmplificationParameter = "getAmplificationParameter" + poolMethodGetBptIndex = "getBptIndex" + poolMethodTotalSupply = "totalSupply" + poolMethodGetLastJoinExitData = "getLastJoinExitData" + poolMethodGetRateProviders = "getRateProviders" + poolMethodGetTokenRateCache = "getTokenRateCache" + poolMethodGetProtocolFeePercentageCache = "getProtocolFeePercentageCache" + poolMethodIsTokenExemptFromYieldProtocolFee = "isTokenExemptFromYieldProtocolFee" + poolMethodInRecoveryMode = "inRecoveryMode" + poolMethodIsExemptFromYieldProtocolFee = "isExemptFromYieldProtocolFee" + poolMethodGetRate = "getRate" + poolMethodGetVault = "getVault" + + unknownInt = -1 + + feeTypeSwap = 0 + feeTypeYield = 2 + + zeroAddress = "0x0000000000000000000000000000000000000000" +) diff --git a/pkg/liquidity-source/balancer-v3/gyro/embed.go b/pkg/liquidity-source/balancer-v3/gyro/embed.go new file mode 100644 index 000000000..0dc4c79e5 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/embed.go @@ -0,0 +1,6 @@ +package composablestable + +import _ "embed" + +//go:embed abis/ComposableStablePool.json +var poolJson []byte diff --git a/pkg/liquidity-source/balancer-v3/gyro/error.go b/pkg/liquidity-source/balancer-v3/gyro/error.go new file mode 100644 index 000000000..9cf0531d8 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/error.go @@ -0,0 +1,12 @@ +package composablestable + +import "errors" + +var ( + ErrOverflow = errors.New("overflow") + ErrUnknownToken = errors.New("unknown token") + ErrInvalidReserve = errors.New("invalid reserve") + ErrReserveNotFound = errors.New("reserve not found") + ErrPoolPaused = errors.New("pool is paused") + ErrBeforeSwapJoinExit = errors.New("before swap join exit") +) diff --git a/pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go b/pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go new file mode 100644 index 000000000..6c2d7a107 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go @@ -0,0 +1,298 @@ +package composablestable + +import ( + "math/big" + + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" +) + +type PoolSimulator struct { + poolpkg.Pool + + paused bool + canNotUpdateTokenRates bool + + regularSimulator *regularSimulator + bptSimulator *bptSimulator + + vault string + poolID string + poolTypeVer int +} + +func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { + var ( + extra Extra + staticExtra StaticExtra + + tokens = make([]string, len(entityPool.Tokens)) + reserves = make([]*big.Int, len(entityPool.Tokens)) + ) + + if err := json.Unmarshal([]byte(entityPool.Extra), &extra); err != nil { + return nil, err + } + + if err := json.Unmarshal([]byte(entityPool.StaticExtra), &staticExtra); err != nil { + return nil, err + } + + for idx := 0; idx < len(entityPool.Tokens); idx++ { + tokens[idx] = entityPool.Tokens[idx].Address + reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) + } + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: entityPool.Address, + Exchange: entityPool.Exchange, + Type: entityPool.Type, + Tokens: tokens, + Reserves: reserves, + Checked: true, + BlockNumber: entityPool.BlockNumber, + }, + } + + regularSimulator := regularSimulator{ + Pool: pool, + bptIndex: staticExtra.BptIndex, + scalingFactors: extra.ScalingFactors, + amp: extra.Amp, + swapFeePercentage: extra.SwapFeePercentage, + } + + bptSimulator := bptSimulator{ + Pool: pool, + bptIndex: staticExtra.BptIndex, + bptTotalSupply: extra.BptTotalSupply, + amp: extra.Amp, + scalingFactors: extra.ScalingFactors, + lastJoinExit: extra.LastJoinExit, + rateProviders: extra.RateProviders, + tokenRateCaches: extra.TokenRateCaches, + swapFeePercentage: extra.SwapFeePercentage, + protocolFeePercentageCache: extra.ProtocolFeePercentageCache, + tokenExemptFromYieldProtocolFee: extra.IsTokenExemptFromYieldProtocolFee, + exemptFromYieldProtocolFee: extra.IsExemptFromYieldProtocolFee, + inRecoveryMode: extra.InRecoveryMode, + + poolTypeVer: staticExtra.PoolTypeVer, + } + + return &PoolSimulator{ + Pool: pool, + paused: extra.Paused, + canNotUpdateTokenRates: extra.CanNotUpdateTokenRates, + regularSimulator: ®ularSimulator, + bptSimulator: &bptSimulator, + vault: staticExtra.Vault, + poolID: staticExtra.PoolID, + poolTypeVer: staticExtra.PoolTypeVer, + }, nil +} + +func (s *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { + if s.paused { + return nil, ErrPoolPaused + } + + if s.canNotUpdateTokenRates { + return nil, ErrBeforeSwapJoinExit + } + + tokenAmountIn := params.TokenAmountIn + tokenOut := params.TokenOut + + indexIn := s.GetTokenIndex(tokenAmountIn.Token) + indexOut := s.GetTokenIndex(tokenOut) + if indexIn == unknownInt || indexOut == unknownInt { + return nil, ErrUnknownToken + } + + amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) + if overflow { + return nil, ErrOverflow + } + + balances := make([]*uint256.Int, len(s.Info.Reserves)) + for i, reserve := range s.Info.Reserves { + r, overflow := uint256.FromBig(reserve) + if overflow { + return nil, ErrOverflow + } + balances[i] = r + } + + var ( + amountOut *uint256.Int + fee *poolpkg.TokenAmount + swapInfo *SwapInfo + err error + ) + if tokenAmountIn.Token == s.Info.Address || tokenOut == s.Info.Address { + amountOut, fee, swapInfo, err = s.bptSimulator._swapWithBpt(true, amountIn, balances, indexIn, indexOut) + } else { + amountOut, fee, swapInfo, err = s.regularSimulator._swapGivenIn(amountIn, balances, indexIn, indexOut) + } + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountOutResult{ + TokenAmountOut: &poolpkg.TokenAmount{ + Token: tokenOut, + Amount: amountOut.ToBig(), + }, + Fee: fee, + Gas: DefaultGas.Swap, + SwapInfo: swapInfo, + }, nil +} + +func (s *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { + if s.paused { + return nil, ErrPoolPaused + } + + if s.canNotUpdateTokenRates { + return nil, ErrBeforeSwapJoinExit + } + + tokenAmountOut := params.TokenAmountOut + tokenIn := params.TokenIn + + indexIn := s.GetTokenIndex(tokenIn) + indexOut := s.GetTokenIndex(tokenAmountOut.Token) + if indexIn == unknownInt || indexOut == unknownInt { + return nil, ErrUnknownToken + } + + amountIn, overflow := uint256.FromBig(tokenAmountOut.Amount) + if overflow { + return nil, ErrOverflow + } + + balances := make([]*uint256.Int, len(s.Info.Reserves)) + for i, reserve := range s.Info.Reserves { + r, overflow := uint256.FromBig(reserve) + if overflow { + return nil, ErrOverflow + } + balances[i] = r + } + + var ( + amountOut *uint256.Int + fee *poolpkg.TokenAmount + swapInfo *SwapInfo + err error + ) + if tokenAmountOut.Token == s.Info.Address || tokenIn == s.Info.Address { + amountOut, fee, swapInfo, err = s.bptSimulator._swapWithBpt(false, amountIn, balances, indexIn, indexOut) + } else { + amountOut, fee, swapInfo, err = s.regularSimulator._swapGivenOut(amountIn, balances, indexIn, indexOut) + } + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: tokenIn, + Amount: amountOut.ToBig(), + }, + Fee: fee, + Gas: DefaultGas.Swap, + SwapInfo: swapInfo, + }, nil +} + +func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { + return PoolMetaInfo{ + Vault: s.vault, + PoolID: s.poolID, + TokenOutIndex: s.GetTokenIndex(tokenOut), + BlockNumber: s.Info.BlockNumber, + } +} + +func (s *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { + if params.TokenAmountIn.Token == s.Info.Address || params.TokenAmountOut.Token == s.Info.Address { + s.bptSimulator.updateBalance(params) + return + } + + s.regularSimulator.updateBalance(params) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L696 +/** + * @dev Reverses the `scalingFactor` applied to `amount`, resulting in a smaller or equal value depending on + * whether it needed scaling or not. The result is rounded down. + */ +func _downscaleDown(amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { + return math.FixedPoint.DivDown(amount, scalingFactor) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L717 +/** + * @dev Reverses the `scalingFactor` applied to `amount`, resulting in a smaller or equal value depending on + * whether it needed scaling or not. The result is rounded up. + */ +func _downscaleUp(amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { + return math.FixedPoint.DivUp(amount, scalingFactor) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L683 +/** + * @dev Same as `_upscale`, but for an entire array. This function does not return anything, but instead *mutates* + * the `amounts` array. + */ +func _upscaleArray(balances []*uint256.Int, scalingFactors []*uint256.Int) ([]*uint256.Int, error) { + upscaled := make([]*uint256.Int, len(balances)) + for i, balance := range balances { + upscaledI, err := _upscale(balance, scalingFactors[i]) + if err != nil { + return nil, err + } + upscaled[i] = upscaledI + } + return upscaled, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L671 +/** + * @dev Applies `scalingFactor` to `amount`, resulting in a larger or equal value depending on whether it needed + * scaling or not. + */ +func _upscale(amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { + return math.FixedPoint.MulDown(amount, scalingFactor) +} + +func _dropBptItem(amounts []*uint256.Int, bptIndex int) []*uint256.Int { + amountsWithoutBpt := make([]*uint256.Int, len(amounts)-1) + + for i := 0; i < len(amountsWithoutBpt); i++ { + if i < bptIndex { + amountsWithoutBpt[i] = amounts[i] + continue + } + amountsWithoutBpt[i] = amounts[i+1] + } + + return amountsWithoutBpt +} + +func _skipBptIndex(index int, bptIndex int) int { + if index < bptIndex { + return index + } + return index - 1 +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go new file mode 100644 index 000000000..b40a57e99 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go @@ -0,0 +1,1041 @@ +package composablestable + +import ( + "math/big" + "testing" + + "github.com/goccy/go-json" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" +) + +func TestRegularSwap(t *testing.T) { + t.Run("1. Should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("2596148429267407814265248164610048", 10) + reserve1, _ := new(big.Int).SetString("6999791779383984752", 10) + reserve2, _ := new(big.Int).SetString("3000000000000000000", 10) + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Tokens: []string{ + "0x00C2A4be503869Fa751c2DbcB7156cc970b5a8dA", + "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399", + "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + }, + }, + } + + regularSimulator := ®ularSimulator{ + Pool: pool, + swapFeePercentage: uint256.NewInt(100000000000000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000052057863883934), + uint256.NewInt(1000000000000000000), + }, + + bptIndex: 0, + amp: uint256.NewInt(1500000), + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + regularSimulator: regularSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399", + Amount: big.NewInt(999791779383984752), + } + tokenOut := "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA" + + // expected + expectedAmountOut := "998507669837625986" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("2. Should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("2596148429267407814265248164610048", 10) + reserve1, _ := new(big.Int).SetString("6999791779383984752", 10) + reserve2, _ := new(big.Int).SetString("3000000000000000000", 10) + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Tokens: []string{ + "0x00C2A4be503869Fa751c2DbcB7156cc970b5a8dA", + "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399", + "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + }, + }, + } + + regularSimulator := ®ularSimulator{ + Pool: pool, + swapFeePercentage: uint256.NewInt(100000000000000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(10000000000), + uint256.NewInt(10000520578), + uint256.NewInt(10000000000), + }, + + bptIndex: 0, + amp: uint256.NewInt(1500000), + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + regularSimulator: regularSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA", + Amount: big.NewInt(23142175917219494), + } + tokenOut := "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399" + + // expected + expectedAmountOut := "23155810259460675" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) +} + +func TestBptSwap(t *testing.T) { + t.Run("1. Join swap pool type ver 1 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("414101427485347", 10) + reserve1, _ := new(big.Int).SetString("2596148429267348622595662702661260", 10) + reserve2, _ := new(big.Int).SetString("1170046233780600", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + Tokens: []string{ + "0x0000000000085d4780B73119b644AE5ecd22b376", + "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer1, + bptIndex: 1, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(600000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + uint256.NewInt(366332019912307), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(600000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + { + Rate: uint256.MustFromDecimal("1003857034775170156"), + OldRate: uint256.MustFromDecimal("1000977462514719154"), + Duration: uint256.NewInt(1000), + Expires: uint256.NewInt(1677904371), + }, + }, + swapFeePercentage: uint256.NewInt(100000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(0), + feeTypeYield: uint256.NewInt(0), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, true, + }, + inRecoveryMode: true, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xA13a9247ea42D743238089903570127DdA72fE44", + Amount: big.NewInt(170046233780600), + } + tokenOut := "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0" + + // expected + expectedAmountOut := "22005850083674" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("2. Join swap pool type ver 1 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("1414101427485347", 10) + reserve1, _ := new(big.Int).SetString("1596148429267348622595662702661260", 10) + reserve2, _ := new(big.Int).SetString("2170046233780600", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + Tokens: []string{ + "0x0000000000085d4780B73119b644AE5ecd22b376", + "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer1, + bptIndex: 1, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(600000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + uint256.NewInt(366332019912307), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(600000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + { + Rate: uint256.MustFromDecimal("1003857034775170156"), + OldRate: uint256.MustFromDecimal("1000977462514719154"), + Duration: uint256.NewInt(1000), + Expires: uint256.NewInt(1677904371), + }, + }, + swapFeePercentage: uint256.NewInt(100000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(0), + feeTypeYield: uint256.NewInt(0), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, true, + }, + inRecoveryMode: true, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x0000000000085d4780B73119b644AE5ecd22b376", + Amount: big.NewInt(214101427485347), + } + tokenOut := "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0" + + // expected + expectedAmountOut := "128189688116719916203223884786015" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("3. Join swap pool type ver 5 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) + reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) + reserve2, _ := new(big.Int).SetString("10406089385", 10) + reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + Tokens: []string{ + "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + reserve3, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer5, + bptIndex: 0, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(200000), + scalingFactors: []*uint256.Int{ + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000000000000000"), + uint256.MustFromDecimal("1008208139884891050"), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(200000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xd8689E8740C23d73136744817347fd6aC464E842", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + {}, + { + Rate: uint256.MustFromDecimal("1008130755672919714"), + OldRate: uint256.MustFromDecimal("1008130755672919714"), + Duration: uint256.NewInt(10800), + Expires: uint256.NewInt(1700764235), + }, + }, + swapFeePercentage: uint256.NewInt(500000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(500000000000000000), + feeTypeYield: uint256.NewInt(500000000000000000), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, false, false, + }, + exemptFromYieldProtocolFee: false, + inRecoveryMode: false, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + Amount: big.NewInt(2040500000000000), + } + tokenOut := "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c" + + // expected + expectedAmountOut := "72153658150470669505066070" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("4. Join swap pool type ver 5 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) + reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) + reserve2, _ := new(big.Int).SetString("10406089385", 10) + reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + Tokens: []string{ + "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + reserve3, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer5, + bptIndex: 0, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(200000), + scalingFactors: []*uint256.Int{ + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000000000000000"), + uint256.MustFromDecimal("1008208139884891050"), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(200000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xd8689E8740C23d73136744817347fd6aC464E842", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + {}, + { + Rate: uint256.MustFromDecimal("1008130755672919714"), + OldRate: uint256.MustFromDecimal("1008130755672919714"), + Duration: uint256.NewInt(10800), + Expires: uint256.NewInt(1700764235), + }, + }, + swapFeePercentage: uint256.NewInt(500000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(500000000000000000), + feeTypeYield: uint256.NewInt(500000000000000000), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, false, false, + }, + exemptFromYieldProtocolFee: false, + inRecoveryMode: false, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", + Amount: big.NewInt(4048384348048588331), + } + tokenOut := "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c" + + // expected + expectedAmountOut := "4071333855617864209" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("1. Exit swap pool type ver 1 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("414101427485347", 10) + reserve1, _ := new(big.Int).SetString("2596148429267348622595662702661260", 10) + reserve2, _ := new(big.Int).SetString("1170046233780600", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + Tokens: []string{ + "0x0000000000085d4780B73119b644AE5ecd22b376", + "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer1, + bptIndex: 1, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(600000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + uint256.NewInt(366332019912307), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(600000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + { + Rate: uint256.MustFromDecimal("1003857034775170156"), + OldRate: uint256.MustFromDecimal("1000977462514719154"), + Duration: uint256.NewInt(1000), + Expires: uint256.NewInt(1677904371), + }, + }, + swapFeePercentage: uint256.NewInt(100000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(0), + feeTypeYield: uint256.NewInt(0), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, true, + }, + inRecoveryMode: true, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + amountIn, _ := new(big.Int).SetString("95662702661260", 10) + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + Amount: amountIn, + } + tokenOut := "0xA13a9247ea42D743238089903570127DdA72fE44" + + // expected + expectedAmountOut := "473156052715491" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("2. Exit swap pool type ver 1 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("414101427485347", 10) + reserve1, _ := new(big.Int).SetString("2596148429267348622595662702661260", 10) + reserve2, _ := new(big.Int).SetString("1170046233780600", 10) + // 414101427485347,2596148429267348622595662702661260,1170046233780600 + + bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + Tokens: []string{ + "0x0000000000085d4780B73119b644AE5ecd22b376", + "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer1, + bptIndex: 1, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(600000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + uint256.NewInt(366332019912307), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(600000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xA13a9247ea42D743238089903570127DdA72fE44", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + { + Rate: uint256.MustFromDecimal("1003857034775170156"), + OldRate: uint256.MustFromDecimal("1000977462514719154"), + Duration: uint256.NewInt(1000), + Expires: uint256.NewInt(1677904371), + }, + }, + swapFeePercentage: uint256.NewInt(100000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(0), + feeTypeYield: uint256.NewInt(0), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, true, + }, + inRecoveryMode: true, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", + Amount: big.NewInt(59566270266126), + } + tokenOut := "0x0000000000085d4780B73119b644AE5ecd22b376" + + // expected + expectedAmountOut := "17329834826337" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("3. Exit swap pool type ver 5 should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) + reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) + reserve2, _ := new(big.Int).SetString("10406089385", 10) + reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + Tokens: []string{ + "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + reserve3, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer5, + bptIndex: 0, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(200000), + scalingFactors: []*uint256.Int{ + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000000000000000"), + uint256.MustFromDecimal("1008208139884891050"), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(200000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xd8689E8740C23d73136744817347fd6aC464E842", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + {}, + { + Rate: uint256.MustFromDecimal("1008130755672919714"), + OldRate: uint256.MustFromDecimal("1008130755672919714"), + Duration: uint256.NewInt(10800), + }, + }, + swapFeePercentage: uint256.NewInt(500000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(500000000000000000), + feeTypeYield: uint256.NewInt(500000000000000000), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, false, false, + }, + exemptFromYieldProtocolFee: false, + inRecoveryMode: false, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + Amount: big.NewInt(2040500000000000), + } + tokenOut := "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09" + + // expected + expectedAmountOut := "2027780845478092" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("4. Exit swap pool type ver 5 swap should return OK", func(t *testing.T) { + // data + reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) + reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) + reserve2, _ := new(big.Int).SetString("10406089385", 10) + reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) + + bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") + + pool := poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + Tokens: []string{ + "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + reserve2, + reserve3, + }, + }, + } + + bptSimulator := &bptSimulator{ + poolTypeVer: poolTypeVer5, + bptIndex: 0, + bptTotalSupply: bptTotalSupply, + amp: uint256.NewInt(200000), + scalingFactors: []*uint256.Int{ + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000"), + uint256.MustFromDecimal("1000000000000000000000000000000"), + uint256.MustFromDecimal("1008208139884891050"), + }, + lastJoinExit: LastJoinExitData{ + LastJoinExitAmplification: uint256.NewInt(200000), + LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), + }, + rateProviders: []string{ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0xd8689E8740C23d73136744817347fd6aC464E842", + }, + tokenRateCaches: []TokenRateCache{ + {}, + {}, + {}, + { + Rate: uint256.MustFromDecimal("1008130755672919714"), + OldRate: uint256.MustFromDecimal("1008130755672919714"), + Duration: uint256.NewInt(10800), + Expires: uint256.NewInt(1700764235), + }, + }, + swapFeePercentage: uint256.NewInt(500000000000000), + protocolFeePercentageCache: map[int]*uint256.Int{ + feeTypeSwap: uint256.NewInt(500000000000000000), + feeTypeYield: uint256.NewInt(500000000000000000), + }, + tokenExemptFromYieldProtocolFee: []bool{ + false, false, false, false, + }, + exemptFromYieldProtocolFee: false, + inRecoveryMode: false, + } + + poolSimulator := &PoolSimulator{ + Pool: pool, + bptSimulator: bptSimulator, + } + + // input + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", + Amount: big.NewInt(4048384348048588331), + } + tokenOut := "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09" + + // expected + expectedAmountOut := "4023147984636196801" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) +} + +func TestPoolSimulator_CalcAmountIn(t *testing.T) { + amountOutTest2, _ := new(big.Int).SetString("100000000", 10) + expectedAmountInTest2, _ := new(big.Int).SetString("99981105484344981876", 10) + amountOutTest3, _ := new(big.Int).SetString("100000000000000000000", 10) + expectedAmountInTest3, _ := new(big.Int).SetString("100018917", 10) + + type fields struct { + poolStr string + } + + tests := []struct { + name string + fields fields + params poolpkg.CalcAmountInParams + want *poolpkg.CalcAmountInResult + wantErr error + }{ + { + name: "1. should return error ErrPoolPaused", + fields: fields{ + poolStr: `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-composable-stable", + "type": "balancer-v2-composable-stable", + "timestamp": 1703667290, + "reserves": [ + "9999991000000000000", + "99999910000000000056", + "8897791020011100123456" + ], + "tokens": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + } + ], + "extra": "{\"amp\":\"0x1388\",\"swapFeePercentage\":\"0x2D79883D2000\",\"scalingFactors\":[\"100\",\"1\",\"100\"],\"paused\":true}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"Stable\",\"poolTypeVersion\":1,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: big.NewInt(999999100000), + }, + TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + }, + want: nil, + wantErr: ErrPoolPaused, + }, + { + name: "2. should return OK", + fields: fields{ + poolStr: `{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","reserveUsd":1143324.9804121545,"amplifiedTvl":1143324.9804121545,"exchange":"balancer-v2-composable-stable","type":"balancer-v2-composable-stable","timestamp":1712718393,"reserves":["279496786025154287762267","2596148429569910245264763596342291","253647851077","610180343310"],"tokens":[{"address":"0x6b175474e89094c44da98b954eedeac495271d0f","swappable":true},{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","swappable":true},{"address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","swappable":true},{"address":"0xdac17f958d2ee523a2206206994597c13d831ec7","swappable":true}],"extra":"{\"canNotUpdateTokenRates\":false,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"bptTotalSupply\":\"2596148430699200833573624981511145\",\"amp\":\"5000000\",\"lastJoinExit\":{\"lastJoinExitAmplification\":\"5000000\",\"lastPostJoinExitInvariant\":\"1143300320131453789392387\"},\"rateProviders\":[\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\"],\"tokenRateCaches\":[{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null}],\"swapFeePercentage\":\"100000000000000\",\"protocolFeePercentageCache\":{\"0\":\"0\",\"2\":\"0\"},\"isTokenExemptFromYieldProtocolFee\":[false,false,false,false],\"isExemptFromYieldProtocolFee\":false,\"inRecoveryMode\":false,\"paused\":false}","staticExtra":"{\"poolId\":\"0x79c58f70905f734641735bc61e45c19dd9ad60bc0000000000000000000004e7\",\"poolType\":\"ComposableStable\",\"poolTypeVer\":3,\"bptIndex\":1,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}","blockNumber":19622438}`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: amountOutTest2, + }, + TokenIn: "0x6b175474e89094c44da98b954eedeac495271d0f", + }, + want: &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: "0x6b175474e89094c44da98b954eedeac495271d0f", + Amount: expectedAmountInTest2, + }, + }, + wantErr: nil, + }, + { + name: "3. should return OK", + fields: fields{ + poolStr: `{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","reserveUsd":1143324.9804121545,"amplifiedTvl":1143324.9804121545,"exchange":"balancer-v2-composable-stable","type":"balancer-v2-composable-stable","timestamp":1712718393,"reserves":["279496786025154287762267","2596148429569910245264763596342291","253647851077","610180343310"],"tokens":[{"address":"0x6b175474e89094c44da98b954eedeac495271d0f","swappable":true},{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","swappable":true},{"address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","swappable":true},{"address":"0xdac17f958d2ee523a2206206994597c13d831ec7","swappable":true}],"extra":"{\"canNotUpdateTokenRates\":false,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"bptTotalSupply\":\"2596148430699200833573624981511145\",\"amp\":\"5000000\",\"lastJoinExit\":{\"lastJoinExitAmplification\":\"5000000\",\"lastPostJoinExitInvariant\":\"1143300320131453789392387\"},\"rateProviders\":[\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\"],\"tokenRateCaches\":[{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null}],\"swapFeePercentage\":\"100000000000000\",\"protocolFeePercentageCache\":{\"0\":\"0\",\"2\":\"0\"},\"isTokenExemptFromYieldProtocolFee\":[false,false,false,false],\"isExemptFromYieldProtocolFee\":false,\"inRecoveryMode\":false,\"paused\":false}","staticExtra":"{\"poolId\":\"0x79c58f70905f734641735bc61e45c19dd9ad60bc0000000000000000000004e7\",\"poolType\":\"ComposableStable\",\"poolTypeVer\":3,\"bptIndex\":1,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}","blockNumber":19622438}`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0x6b175474e89094c44da98b954eedeac495271d0f", + Amount: amountOutTest3, + }, + TokenIn: "0xdac17f958d2ee523a2206206994597c13d831ec7", + }, + want: &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: expectedAmountInTest3, + }, + }, + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var pool entity.Pool + err := json.Unmarshal([]byte(tt.fields.poolStr), &pool) + assert.Nil(t, err) + + simulator, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + got, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return simulator.CalcAmountIn(tt.params) + }) + if err != nil { + assert.ErrorIsf(t, err, tt.wantErr, "PoolSimulator.CalcAmountIn() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equalf(t, tt.want.TokenAmountIn.Token, got.TokenAmountIn.Token, "tokenIn = %v, want %v", got.TokenAmountIn.Token, tt.want.TokenAmountIn.Token) + assert.Equalf(t, tt.want.TokenAmountIn.Amount, got.TokenAmountIn.Amount, "amountIn = %v, want %v", got.TokenAmountIn.Amount.String(), tt.want.TokenAmountIn.Amount.String()) + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go b/pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go new file mode 100644 index 000000000..6de4a591e --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go @@ -0,0 +1,478 @@ +package composablestable + +import ( + "context" + "math/big" + "strings" + "time" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/shared" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/ethereum/go-ethereum/ethclient/gethclient" +) + +type PoolTracker struct { + config *Config + ethrpcClient *ethrpc.Client +} + +func NewPoolTracker( + config *Config, + ethrpcClient *ethrpc.Client, +) (*PoolTracker, error) { + return &PoolTracker{ + config: config, + ethrpcClient: ethrpcClient, + }, nil +} + +func (t *PoolTracker) GetNewPoolState( + ctx context.Context, + p entity.Pool, + params poolpkg.GetNewPoolStateParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, params, nil) +} + +func (t *PoolTracker) GetNewPoolStateWithOverrides( + ctx context.Context, + p entity.Pool, + params poolpkg.GetNewPoolStateWithOverridesParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, poolpkg.GetNewPoolStateParams{Logs: params.Logs}, params.Overrides) +} + +func (t *PoolTracker) getNewPoolState( + ctx context.Context, + p entity.Pool, + _ poolpkg.GetNewPoolStateParams, + overrides map[common.Address]gethclient.OverrideAccount, +) (entity.Pool, error) { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Info("Start updating state ...") + + defer func() { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Info("Finish updating state.") + }() + + var staticExtra StaticExtra + if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + // call RPC + rpcRes, err := t.queryRPC( + ctx, + p.Address, + common.HexToHash(staticExtra.PoolID), + staticExtra.PoolTypeVer, + p.Tokens, + staticExtra.Vault, + overrides, + ) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + // update pool + + reserves, err := t.initReserves(ctx, p.Tokens, rpcRes.PoolTokens) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + extra, err := t.initExtra(ctx, rpcRes, staticExtra) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + extraBytes, err := json.Marshal(extra) + if err != nil { + return p, err + } + + p.BlockNumber = rpcRes.BlockNumber + p.Timestamp = time.Now().Unix() + p.Reserves = reserves + p.Extra = string(extraBytes) + p.BlockNumber = rpcRes.BlockNumber + + return p, nil +} + +func (t *PoolTracker) queryRPC( + ctx context.Context, + poolAddress string, + poolID common.Hash, + poolTypeVer int, + tokens []*entity.PoolToken, + vault string, + overrides map[common.Address]gethclient.OverrideAccount, +) (*rpcRes, error) { + var ( + tokenNbr = len(tokens) + + poolTokens PoolTokensResp + bptTotalSupply *big.Int + ampParams AmplificationParameterResp + lastJoinExit LastJoinExitResp + rateProviders = make([]common.Address, tokenNbr) + tokenRateCaches = make([]TokenRateCacheResp, tokenNbr) + swapFeePercentage *big.Int + protocolFeePercentageCache = make(map[int]*big.Int) + isTokenExemptFromYieldProtocolFee = make([]bool, tokenNbr) + isExemptFromYieldProtocolFee bool + inRecoveryMode bool + pausedState PausedStateResp + + blockNbr *big.Int + + feeTypes = []int{feeTypeSwap, feeTypeYield} + ) + + /* + Call 1 get: + - poolTokens + - bptTotalSupply + - ampParams + - lastJoinExit + - rateProviders + - tokenRateCaches + - swapFeePercentage + - protocolFeePercentageCache + - isTokenExemptFromYieldProtocolFee + - isExemptFromYieldProtocolFee + - inRecoveryMode + - pausedState + */ + + req := t.ethrpcClient.R().SetContext(ctx) + if overrides != nil { + req.SetOverrides(overrides) + } + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultABI, + Target: vault, + Method: shared.VaultMethodGetPoolTokens, + Params: []interface{}{poolID}, + }, []interface{}{&poolTokens}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodTotalSupply, + }, []interface{}{&bptTotalSupply}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetAmplificationParameter, + }, []interface{}{&Params}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetLastJoinExitData, + }, []interface{}{&lastJoinExit}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetRateProviders, + }, []interface{}{&rateProviders}) + + for i, token := range tokens { + tokenAddr := common.HexToAddress(token.Address) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetTokenRateCache, + Params: []interface{}{tokenAddr}, + }, []interface{}{&tokenRateCaches[i]}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodIsTokenExemptFromYieldProtocolFee, + Params: []interface{}{tokenAddr}, + }, []interface{}{&isTokenExemptFromYieldProtocolFee[i]}) + } + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetSwapFeePercentage, + }, []interface{}{&swapFeePercentage}) + + for _, feeType := range feeTypes { + value := big.NewInt(0) + protocolFeePercentageCache[feeType] = value + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetProtocolFeePercentageCache, + Params: []interface{}{big.NewInt(int64(feeType))}, + }, []interface{}{&value}) + } + + if poolTypeVer >= poolTypeVer5 { + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodIsExemptFromYieldProtocolFee, + }, []interface{}{&isExemptFromYieldProtocolFee}) + } + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodInRecoveryMode, + }, []interface{}{&inRecoveryMode}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetPausedState, + }, []interface{}{&pausedState}) + + res, err := req.TryBlockAndAggregate() + if err != nil { + return nil, err + } + + blockNbr = res.BlockNumber + + /* + Update token rate + */ + + canNotUpdateTokenRates := false + req = t.ethrpcClient.R().SetContext(ctx).SetBlockNumber(blockNbr) + if overrides != nil { + req.SetOverrides(overrides) + } + + rateUpdatedTokenIndexes := []int{} + updatedRate := make([]*big.Int, tokenNbr) + for i, token := range tokens { + if token.Address == poolAddress || + rateProviders[i].Hex() == zeroAddress || + time.Now().Unix() < tokenRateCaches[i].Expires.Int64() { + continue + } + + rateUpdatedTokenIndexes = append(rateUpdatedTokenIndexes, i) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: rateProviders[i].Hex(), + Method: poolMethodGetRate, + }, []interface{}{&updatedRate[i]}) + } + if len(rateUpdatedTokenIndexes) > 0 { + if _, err := req.Aggregate(); err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": poolAddress, + }).Warnf("can not update token rates: %s", err.Error()) + + canNotUpdateTokenRates = true + } + + for _, i := range rateUpdatedTokenIndexes { + if updatedRate[i] == nil { + continue + } + tokenRateCaches[i].Rate = updatedRate[i] + tokenRateCaches[i].Expires = big.NewInt(time.Now().Unix() + tokenRateCaches[i].Duration.Int64()) + } + } + + return &rpcRes{ + CanNotUpdateTokenRates: canNotUpdateTokenRates, + PoolTokens: poolTokens, + BptTotalSupply: bptTotalSupply, + Amp: ampParams.Value, + LastJoinExit: lastJoinExit, + RateProviders: rateProviders, + TokenRateCaches: tokenRateCaches, + SwapFeePercentage: swapFeePercentage, + ProtocolFeePercentageCache: protocolFeePercentageCache, + IsTokenExemptFromYieldProtocolFee: isTokenExemptFromYieldProtocolFee, + IsExemptFromYieldProtocolFee: isExemptFromYieldProtocolFee, + InRecoveryMode: inRecoveryMode, + PausedState: pausedState, + BlockNumber: res.BlockNumber.Uint64(), + }, nil +} + +func (t *PoolTracker) initExtra( + ctx context.Context, + rpcRes *rpcRes, + staticExtra StaticExtra, +) (*Extra, error) { + scalingFactors := make([]*uint256.Int, len(staticExtra.ScalingFactors)) + for i, scalingFactor := range staticExtra.ScalingFactors { + var rate *uint256.Int + if i == staticExtra.BptIndex || rpcRes.RateProviders[i].Hex() == zeroAddress { + rate = number.Number_1e18 + } else { + rate, _ = uint256.FromBig(rpcRes.TokenRateCaches[i].Rate) + } + + var err error + scalingFactors[i], err = math.FixedPoint.MulDown(scalingFactor, rate) + if err != nil { + return nil, err + } + } + + bptTotalSupply, overflow := uint256.FromBig(rpcRes.BptTotalSupply) + if overflow { + return nil, ErrOverflow + } + + amp, overflow := uint256.FromBig(rpcRes.Amp) + if overflow { + return nil, ErrOverflow + } + + var lastJoinExit LastJoinExitData + lastJoinExit.LastJoinExitAmplification, _ = uint256.FromBig( + rpcRes.LastJoinExit.LastJoinExitAmplification, + ) + lastJoinExit.LastPostJoinExitInvariant, _ = uint256.FromBig( + rpcRes.LastJoinExit.LastPostJoinExitInvariant, + ) + + rateProviders := make([]string, len(rpcRes.RateProviders)) + for i, rateProvider := range rpcRes.RateProviders { + rateProviders[i] = strings.ToLower(rateProvider.Hex()) + } + + tokenRateCaches := make([]TokenRateCache, len(rpcRes.TokenRateCaches)) + for i, tokenRateCache := range rpcRes.TokenRateCaches { + rate, _ := uint256.FromBig(tokenRateCache.Rate) + oldRate, _ := uint256.FromBig(tokenRateCache.OldRate) + duration, _ := uint256.FromBig(tokenRateCache.Duration) + expires, _ := uint256.FromBig(tokenRateCache.Expires) + tokenRateCaches[i] = TokenRateCache{ + Rate: rate, + OldRate: oldRate, + Duration: duration, + Expires: expires, + } + } + + swapFeePercentage, _ := uint256.FromBig(rpcRes.SwapFeePercentage) + + protocolFeePercentageCache := make(map[int]*uint256.Int) + for feeType, value := range rpcRes.ProtocolFeePercentageCache { + protocolFeePercentageCache[feeType], _ = uint256.FromBig(value) + } + + isTokenExemptFromYieldProtocolFee := rpcRes.IsTokenExemptFromYieldProtocolFee + + isExemptFromYieldProtocolFee := rpcRes.IsExemptFromYieldProtocolFee + + inRecoveryMode := rpcRes.InRecoveryMode + + paused := !isNotPaused(rpcRes.PausedState) + + canNotUpdateTokenRates := rpcRes.CanNotUpdateTokenRates + + extra := Extra{ + CanNotUpdateTokenRates: canNotUpdateTokenRates, + ScalingFactors: scalingFactors, + BptTotalSupply: bptTotalSupply, + Amp: amp, + LastJoinExit: lastJoinExit, + RateProviders: rateProviders, + TokenRateCaches: tokenRateCaches, + SwapFeePercentage: swapFeePercentage, + ProtocolFeePercentageCache: protocolFeePercentageCache, + IsTokenExemptFromYieldProtocolFee: isTokenExemptFromYieldProtocolFee, + IsExemptFromYieldProtocolFee: isExemptFromYieldProtocolFee, + InRecoveryMode: inRecoveryMode, + Paused: paused, + } + + return &extra, nil +} + +func (t *PoolTracker) initReserves( + ctx context.Context, + tokens []*entity.PoolToken, + poolTokens PoolTokensResp, +) ([]string, error) { + reserveByToken := make(map[string]*big.Int) + for idx, token := range poolTokens.Tokens { + addr := strings.ToLower(token.Hex()) + reserveByToken[addr] = poolTokens.Balances[idx] + } + + reserves := make([]string, len(tokens)) + for idx, token := range tokens { + r, ok := reserveByToken[token.Address] + if !ok { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": token.Address, + }).Error("can not get reserve") + + return nil, ErrReserveNotFound + } + + reserves[idx] = r.String() + } + + return reserves, nil +} + +func isNotPaused(pausedState PausedStateResp) bool { + return time.Now().Unix() > pausedState.BufferPeriodEndTime.Int64() || !pausedState.Paused +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go new file mode 100644 index 000000000..95d5fad21 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go @@ -0,0 +1,197 @@ +package composablestable + +import ( + "context" + "math/big" + "strings" + "time" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/shared" +) + +type PoolsListUpdater struct { + config *Config + ethrpcClient *ethrpc.Client + sharedUpdater *shared.PoolsListUpdater +} + +func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsListUpdater { + sharedUpdater := shared.NewPoolsListUpdater(&shared.Config{ + DexID: config.DexID, + SubgraphAPI: config.SubgraphAPI, + SubgraphHeaders: config.SubgraphHeaders, + NewPoolLimit: config.NewPoolLimit, + PoolTypes: []string{poolTypeComposableStable}, + }) + + return &PoolsListUpdater{ + config: config, + ethrpcClient: ethrpcClient, + sharedUpdater: sharedUpdater, + } +} + +func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte) ([]entity.Pool, []byte, error) { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Infof("Start updating pools list ...") + defer func() { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Infof("Finish updating pools list.") + }() + + subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) + if err != nil { + return nil, nil, err + } + + vaults, err := u.getVaults(ctx, subgraphPools) + if err != nil { + return nil, nil, err + } + + bptIndexes, err := u.getBptIndex(ctx, subgraphPools) + if err != nil { + return nil, nil, err + } + + pools, err := u.initPools(ctx, subgraphPools, bptIndexes, vaults) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Error(err.Error()) + + return nil, nil, err + } + + return pools, newMetadataBytes, nil +} + +func (u *PoolsListUpdater) getVaults(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]string, error) { + vaultAddresses := make([]common.Address, len(subgraphPools)) + vaults := make([]string, len(subgraphPools)) + + req := u.ethrpcClient.R() + for idx, subgraphPool := range subgraphPools { + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: subgraphPool.Address, + Method: poolMethodGetVault, + }, []interface{}{&vaultAddresses[idx]}) + } + if _, err := req.Aggregate(); err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Error(err.Error()) + return nil, err + } + + for idx, addr := range vaultAddresses { + vaults[idx] = strings.ToLower(addr.Hex()) + } + + return vaults, nil +} + +func (u *PoolsListUpdater) getBptIndex(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]*big.Int, error) { + bptIndexes := make([]*big.Int, len(subgraphPools)) + + req := u.ethrpcClient.R().SetContext(ctx) + for i, p := range subgraphPools { + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: p.Address, + Method: poolMethodGetBptIndex, + }, []interface{}{&bptIndexes[i]}) + } + + if _, err := req.Aggregate(); err != nil { + return nil, err + } + + return bptIndexes, nil +} + +func (u *PoolsListUpdater) initPools( + ctx context.Context, + subgraphPools []*shared.SubgraphPool, + bptIndexes []*big.Int, + vaults []string, +) ([]entity.Pool, error) { + pools := make([]entity.Pool, 0, len(subgraphPools)) + for idx := range subgraphPools { + pool, err := u.initPool(ctx, subgraphPools[idx], bptIndexes[idx], vaults[idx]) + if err != nil { + return nil, err + } + + pools = append(pools, pool) + } + + return pools, nil +} + +func (u *PoolsListUpdater) initPool( + ctx context.Context, + subgraphPool *shared.SubgraphPool, + bptIndex *big.Int, + vault string, +) (entity.Pool, error) { + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + scalingFactors = make([]*uint256.Int, len(subgraphPool.Tokens)) + + err error + ) + + for j, token := range subgraphPool.Tokens { + poolTokens[j] = &entity.PoolToken{ + Address: strings.ToLower(token.Address), + Swappable: true, + } + + reserves[j] = "0" + + scalingFactors[j] = new(uint256.Int).Mul( + number.TenPow(18-uint8(token.Decimals)), + number.Number_1e18, + ) + } + + staticExtra := StaticExtra{ + PoolID: subgraphPool.ID, + PoolType: subgraphPool.PoolType, + PoolTypeVer: int(subgraphPool.PoolTypeVersion.Int64()), + BptIndex: int(bptIndex.Int64()), + ScalingFactors: scalingFactors, + Vault: vault, + } + staticExtraBytes, err := json.Marshal(staticExtra) + if err != nil { + return entity.Pool{}, err + } + + return entity.Pool{ + Address: strings.ToLower(subgraphPool.Address), + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + }, nil +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/regular_swap.go b/pkg/liquidity-source/balancer-v3/gyro/regular_swap.go new file mode 100644 index 000000000..7e154d96e --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/regular_swap.go @@ -0,0 +1,206 @@ +package composablestable + +import ( + "math/big" + + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" +) + +type regularSimulator struct { + poolpkg.Pool + + bptIndex int + scalingFactors []*uint256.Int + amp *uint256.Int + swapFeePercentage *uint256.Int +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L184 +// It calls `super._swapGivenIn`, which is the code below: +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F11#L49 +func (s *regularSimulator) _swapGivenIn( + amountIn *uint256.Int, + balances []*uint256.Int, + indexIn int, + indexOut int, +) (*uint256.Int, *poolpkg.TokenAmount, *SwapInfo, error) { + feeAmount, err := math.FixedPoint.MulUp(amountIn, s.swapFeePercentage) + if err != nil { + return nil, nil, nil, err + } + amountInAfterFee, err := math.FixedPoint.Sub(amountIn, feeAmount) + if err != nil { + return nil, nil, nil, err + } + + balances, err = _upscaleArray(balances, s.scalingFactors) + if err != nil { + return nil, nil, nil, err + } + + upScaledAmountInAfterFee, err := _upscale(amountInAfterFee, s.scalingFactors[indexIn]) + if err != nil { + return nil, nil, nil, err + } + + upscaledAmountOut, err := s._onSwapGivenIn(upScaledAmountInAfterFee, balances, indexIn, indexOut) + if err != nil { + return nil, nil, nil, err + } + + amountOut, err := _downscaleDown(upscaledAmountOut, s.scalingFactors[indexOut]) + if err != nil { + return nil, nil, nil, err + } + + fee := poolpkg.TokenAmount{ + Token: s.Info.Tokens[indexIn], + Amount: feeAmount.ToBig(), + } + + return amountOut, &fee, &SwapInfo{}, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L215 +// It calls `super._swapGivenOut`, which is the code below: +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F11#L68 +func (s *regularSimulator) _swapGivenOut( + amountOut *uint256.Int, + balances []*uint256.Int, + indexIn int, + indexOut int, +) (*uint256.Int, *poolpkg.TokenAmount, *SwapInfo, error) { + balances, err := _upscaleArray(balances, s.scalingFactors) + if err != nil { + return nil, nil, nil, err + } + + upScaledAmountOut, err := _upscale(amountOut, s.scalingFactors[indexOut]) + if err != nil { + return nil, nil, nil, err + } + + upscaledAmountIn, err := s._onSwapGivenOut(upScaledAmountOut, balances, indexIn, indexOut) + if err != nil { + return nil, nil, nil, err + } + + amountIn, err := _downscaleUp(upscaledAmountIn, s.scalingFactors[indexIn]) + if err != nil { + return nil, nil, nil, err + } + + // Fees are added after scaling happens, to reduce the complexity of the rounding direction analysis. + amountInAfterFee, err := s._addSwapFeeAmount(amountIn) + if err != nil { + return nil, nil, nil, err + } + + feeAmount, err := math.FixedPoint.Sub(amountInAfterFee, amountIn) + if err != nil { + return nil, nil, nil, err + } + + fee := poolpkg.TokenAmount{ + Token: s.Info.Tokens[indexIn], + Amount: feeAmount.ToBig(), + } + + return amountIn, &fee, &SwapInfo{}, nil +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L229 +func (s *regularSimulator) _onSwapGivenIn( + amountIn *uint256.Int, + balances []*uint256.Int, + indexIn int, + indexOut int, +) (*uint256.Int, error) { + return s._onRegularSwap( + true, // given in + amountIn, + balances, + indexIn, + indexOut, + ) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L250 +func (s *regularSimulator) _onSwapGivenOut( + amountIn *uint256.Int, + balances []*uint256.Int, + indexIn int, + indexOut int, +) (*uint256.Int, error) { + return s._onRegularSwap( + false, // given out + amountIn, + balances, + indexIn, + indexOut, + ) +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L270 +func (s *regularSimulator) _onRegularSwap( + isGivenIn bool, + amountGiven *uint256.Int, + registeredBalances []*uint256.Int, + registeredIndexIn int, + registeredIndexOut int, +) (*uint256.Int, error) { + balances := _dropBptItem(registeredBalances, s.bptIndex) + indexIn, indexOut := _skipBptIndex(registeredIndexIn, s.bptIndex), _skipBptIndex(registeredIndexOut, s.bptIndex) + + invariant, err := math.StableMath.CalculateInvariantV2(s.amp, balances) + if err != nil { + return nil, err + } + + if isGivenIn { + return math.StableMath.CalcOutGivenIn( + invariant, + s.amp, + amountGiven, + balances, + indexIn, + indexOut, + ) + } + + return math.StableMath.CalcInGivenOut( + invariant, + s.amp, + amountGiven, + balances, + indexIn, + indexOut, + ) +} + +func (s *regularSimulator) updateBalance(params poolpkg.UpdateBalanceParams) { + for idx, token := range s.Info.Tokens { + if token == params.TokenAmountIn.Token { + s.Info.Reserves[idx] = new(big.Int).Add( + s.Info.Reserves[idx], + params.TokenAmountIn.Amount, + ) + } + + if token == params.TokenAmountOut.Token { + s.Info.Reserves[idx] = new(big.Int).Sub( + s.Info.Reserves[idx], + params.TokenAmountOut.Amount, + ) + } + } +} + +// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L609 +func (s *regularSimulator) _addSwapFeeAmount(amount *uint256.Int) (*uint256.Int, error) { + // This returns amount + fee amount, so we round up (favoring a higher fee amount). + return math.FixedPoint.DivUp(amount, math.FixedPoint.Complement(s.swapFeePercentage)) +} diff --git a/pkg/liquidity-source/balancer-v3/gyro/type.go b/pkg/liquidity-source/balancer-v3/gyro/type.go new file mode 100644 index 000000000..aaf1fb930 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/gyro/type.go @@ -0,0 +1,107 @@ +package composablestable + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" +) + +type PoolMetaInfo struct { + Vault string `json:"vault"` + PoolID string `json:"poolId"` + TokenOutIndex int `json:"tokenOutIndex"` + BlockNumber uint64 `json:"blockNumber"` +} + +type SwapInfo struct { + LastJoinExitData LastJoinExitData `json:"-"` +} + +type LastJoinExitData struct { + LastJoinExitAmplification *uint256.Int `json:"lastJoinExitAmplification"` + LastPostJoinExitInvariant *uint256.Int `json:"lastPostJoinExitInvariant"` +} + +type TokenRateCache struct { + Rate *uint256.Int `json:"rate"` + OldRate *uint256.Int `json:"oldRate"` + Duration *uint256.Int `json:"duration"` + Expires *uint256.Int `json:"expires"` +} + +type Gas struct { + Swap int64 +} + +type Extra struct { + CanNotUpdateTokenRates bool `json:"canNotUpdateTokenRates"` + ScalingFactors []*uint256.Int `json:"scalingFactors"` + BptTotalSupply *uint256.Int `json:"bptTotalSupply"` + Amp *uint256.Int `json:"amp"` + LastJoinExit LastJoinExitData `json:"lastJoinExit"` + RateProviders []string `json:"rateProviders"` + TokenRateCaches []TokenRateCache `json:"tokenRateCaches"` + SwapFeePercentage *uint256.Int `json:"swapFeePercentage"` + ProtocolFeePercentageCache map[int]*uint256.Int `json:"protocolFeePercentageCache"` + IsTokenExemptFromYieldProtocolFee []bool `json:"isTokenExemptFromYieldProtocolFee"` + IsExemptFromYieldProtocolFee bool `json:"isExemptFromYieldProtocolFee"` + InRecoveryMode bool `json:"inRecoveryMode"` + Paused bool `json:"paused"` +} + +type StaticExtra struct { + PoolID string `json:"poolId"` + PoolType string `json:"poolType"` + PoolTypeVer int `json:"poolTypeVer"` + BptIndex int `json:"bptIndex"` + ScalingFactors []*uint256.Int `json:"scalingFactors"` + Vault string `json:"vault"` +} + +type AmplificationParameterResp struct { + Value *big.Int + IsUpdating bool + Precision *big.Int +} + +type LastJoinExitResp struct { + LastJoinExitAmplification *big.Int + LastPostJoinExitInvariant *big.Int +} + +type TokenRateCacheResp struct { + Rate *big.Int + OldRate *big.Int + Duration *big.Int + Expires *big.Int +} + +type PoolTokensResp struct { + Tokens []common.Address + Balances []*big.Int + LastChangeBlock *big.Int +} + +type PausedStateResp struct { + Paused bool + PauseWindowEndTime *big.Int + BufferPeriodEndTime *big.Int +} + +type rpcRes struct { + CanNotUpdateTokenRates bool + PoolTokens PoolTokensResp + BptTotalSupply *big.Int + Amp *big.Int + LastJoinExit LastJoinExitResp + RateProviders []common.Address + TokenRateCaches []TokenRateCacheResp + SwapFeePercentage *big.Int + ProtocolFeePercentageCache map[int]*big.Int + IsTokenExemptFromYieldProtocolFee []bool + IsExemptFromYieldProtocolFee bool + InRecoveryMode bool + PausedState PausedStateResp + BlockNumber uint64 +} diff --git a/pkg/liquidity-source/balancer-v3/math/constant.go b/pkg/liquidity-source/balancer-v3/math/constant.go new file mode 100644 index 000000000..229780742 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/constant.go @@ -0,0 +1,11 @@ +package math + +import "github.com/holiman/uint256" + +var ( + ZERO = uint256.NewInt(0) + ONE = uint256.NewInt(1) + TWO = uint256.NewInt(2) + + ONE_E18 = uint256.NewInt(1e18) +) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point.go b/pkg/liquidity-source/balancer-v3/math/fixed_point.go new file mode 100644 index 000000000..da1b9700a --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point.go @@ -0,0 +1,183 @@ +package math + +import ( + "errors" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/holiman/uint256" +) + +var ( + ErrAddOverflow = errors.New("ADD_OVERFLOW") + ErrSubOverflow = errors.New("SUB_OVERFLOW") + ErrZeroDivision = errors.New("ZERO_DIVISION") + ErrDivInternal = errors.New("DIV_INTERNAL") + ErrMulOverflow = errors.New("MUL_OVERFLOW") +) + +var FixedPoint *fixedPoint + +type fixedPoint struct { + ZERO *uint256.Int + ONE *uint256.Int + TWO *uint256.Int + FOUR *uint256.Int + MAX_POW_RELATIVE_ERROR *uint256.Int +} + +func init() { + zero := uint256.NewInt(0) + one := number.Number_1e18 + two := new(uint256.Int).Mul(number.Number_2, one) + four := new(uint256.Int).Mul(number.Number_4, one) + + FixedPoint = &fixedPoint{ + ZERO: zero, + ONE: one, + TWO: two, + FOUR: four, + MAX_POW_RELATIVE_ERROR: uint256.NewInt(10000), + } +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L34 +func (l *fixedPoint) Add(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c := new(uint256.Int).Add(a, b) + + if c.Lt(a) { + return nil, ErrAddOverflow + } + + return c, nil +} + +func (l *fixedPoint) Sub(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + if a.Lt(b) { + return nil, ErrSubOverflow + } + + return new(uint256.Int).Sub(a, b), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L83 +func (l *fixedPoint) DivUp(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + + aInflated := new(uint256.Int).Mul(a, l.ONE) + + if !(a.IsZero() || new(uint256.Int).Div(aInflated, a).Eq(l.ONE)) { + return nil, ErrDivInternal + } + + if aInflated.IsZero() { + return number.Zero, nil + } + + return new(uint256.Int).Add(new(uint256.Int).Div(new(uint256.Int).Sub(aInflated, number.Number_1), b), number.Number_1), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L74 +func (l *fixedPoint) DivDown(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + + aInflated := new(uint256.Int).Mul(a, l.ONE) + + if !(a.IsZero() || new(uint256.Int).Div(aInflated, a).Eq(l.ONE)) { + return nil, ErrDivInternal + } + + return new(uint256.Int).Div(aInflated, b), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L132 +func (l *fixedPoint) PowUp(x *uint256.Int, y *uint256.Int) (*uint256.Int, error) { + if y.Eq(l.ONE) { + return x, nil + } + if y.Eq(l.TWO) { + return l.MulUp(x, x) + } + if y.Eq(l.FOUR) { + square, err := l.MulUp(x, x) + if err != nil { + return nil, err + } + + return l.MulUp(square, square) + } + + raw, err := LogExpMath.Pow(x, y) + if err != nil { + return nil, err + } + + mulUpRawAndMaxPow, err := l.MulUp(raw, l.MAX_POW_RELATIVE_ERROR) + if err != nil { + return nil, err + } + + maxError, err := l.Add(mulUpRawAndMaxPow, number.Number_1) + if err != nil { + return nil, err + } + + return l.Add(raw, maxError) +} + +func (l *fixedPoint) PowUpV1(x *uint256.Int, y *uint256.Int) (*uint256.Int, error) { + raw, err := LogExpMath.Pow(x, y) + if err != nil { + return nil, err + } + + mulUpRawAndMaxPow, err := l.MulUp(raw, l.MAX_POW_RELATIVE_ERROR) + if err != nil { + return nil, err + } + + maxError, err := l.Add(mulUpRawAndMaxPow, number.Number_1) + if err != nil { + return nil, err + } + + return l.Add(raw, maxError) +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L50 +func (l *fixedPoint) MulDown(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + product := new(uint256.Int).Mul(a, b) + + if !(a.IsZero() || new(uint256.Int).Div(product, a).Eq(b)) { + return nil, ErrMulOverflow + } + + return new(uint256.Int).Div(product, l.ONE), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L57 +func (l *fixedPoint) MulUp(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + product := new(uint256.Int).Mul(a, b) + + if !(a.IsZero() || new(uint256.Int).Div(product, a).Eq(b)) { + return nil, ErrMulOverflow + } + + if product.IsZero() { + return number.Zero, nil + } + + return new(uint256.Int).Add(new(uint256.Int).Div(new(uint256.Int).Sub(product, number.Number_1), l.ONE), number.Number_1), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L156 +func (l *fixedPoint) Complement(x *uint256.Int) *uint256.Int { + if x.Lt(l.ONE) { + return new(uint256.Int).Sub(l.ONE, x) + } + + return number.Zero +} diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_test.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_test.go new file mode 100644 index 000000000..d43285ff1 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point_test.go @@ -0,0 +1,25 @@ +package math + +import ( + "testing" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/stretchr/testify/assert" +) + +func TestFixedPoint_MulDown(t *testing.T) { + t.Run("it should return correct result", func(t *testing.T) { + result, err := FixedPoint.MulDown(number.NewUint256("25925243203807071591361"), number.NewUint256("176891139771667")) + + assert.Nil(t, err) + assert.Zero(t, result.Cmp(number.NewUint256("4585945819179096677"))) + }) +} + +func TestFixedPoint_Complement(t *testing.T) { + t.Run("it should return correct result", func(t *testing.T) { + result := FixedPoint.Complement(number.NewUint256("999823108860228333")) + + assert.Zero(t, result.Cmp(number.NewUint256("176891139771667"))) + }) +} diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go new file mode 100644 index 000000000..88a1a111f --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go @@ -0,0 +1,389 @@ +package math + +import ( + "errors" + "math/big" + + "github.com/KyberNetwork/blockchain-toolkit/integer" + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" +) + +var ( + ErrXOutOfBounds = errors.New("X_OUT_OF_BOUNDS") + ErrYOutOfBounds = errors.New("Y_OUT_OF_BOUNDS") + ErrProductOutOfBounds = errors.New("PRODUCT_OUT_OF_BOUNDS") + ErrInvalidExponent = errors.New("INVALID_EXPONENT") +) + +var LogExpMath *logExpMath + +func init() { + one_18 := integer.TenPow(18) + one_20 := integer.TenPow(20) + one_36 := integer.TenPow(36) + + LogExpMath = &logExpMath{ + ONE_18: one_18, + ONE_20: one_20, + ONE_36: one_36, + MILD_EXPONENT_BOUND: new(uint256.Int).Div(new(uint256.Int).Exp(number.Number_2, uint256.NewInt(254)), number.TenPow(20)), + LN_36_LOWER_BOUND: new(big.Int).Sub(one_18, integer.TenPow(17)), + LN_36_UPPER_BOUND: new(big.Int).Add(one_18, integer.TenPow(17)), + MIN_NATURAL_EXPONENT: new(big.Int).Mul(big.NewInt(-41), one_18), + MAX_NATURAL_EXPONENT: new(big.Int).Mul(big.NewInt(130), one_18), + x0: bignumber.NewBig10("128000000000000000000"), + a0: bignumber.NewBig10("38877084059945950922200000000000000000000000000000000000"), + x1: bignumber.NewBig10("64000000000000000000"), + a1: bignumber.NewBig10("6235149080811616882910000000"), + x2: bignumber.NewBig10("3200000000000000000000"), + a2: bignumber.NewBig10("7896296018268069516100000000000000"), + x3: bignumber.NewBig10("1600000000000000000000"), + a3: bignumber.NewBig10("888611052050787263676000000"), + x4: bignumber.NewBig10("800000000000000000000"), + a4: bignumber.NewBig10("298095798704172827474000"), + x5: bignumber.NewBig10("400000000000000000000"), + a5: bignumber.NewBig10("5459815003314423907810"), + x6: bignumber.NewBig10("200000000000000000000"), + a6: bignumber.NewBig10("738905609893065022723"), + x7: bignumber.NewBig10("100000000000000000000"), + a7: bignumber.NewBig10("271828182845904523536"), + x8: bignumber.NewBig10("50000000000000000000"), + a8: bignumber.NewBig10("164872127070012814685"), + x9: bignumber.NewBig10("25000000000000000000"), + a9: bignumber.NewBig10("128402541668774148407"), + x10: bignumber.NewBig10("12500000000000000000"), + a10: bignumber.NewBig10("113314845306682631683"), + x11: bignumber.NewBig10("6250000000000000000"), + a11: bignumber.NewBig10("106449445891785942956"), + } +} + +type logExpMath struct { + ONE_18 *big.Int + ONE_20 *big.Int + ONE_36 *big.Int + MILD_EXPONENT_BOUND *uint256.Int + LN_36_LOWER_BOUND *big.Int + LN_36_UPPER_BOUND *big.Int + MIN_NATURAL_EXPONENT *big.Int + MAX_NATURAL_EXPONENT *big.Int + x0 *big.Int + a0 *big.Int + x1 *big.Int + a1 *big.Int + x2 *big.Int + a2 *big.Int + x3 *big.Int + a3 *big.Int + x4 *big.Int + a4 *big.Int + x5 *big.Int + a5 *big.Int + x6 *big.Int + a6 *big.Int + x7 *big.Int + a7 *big.Int + x8 *big.Int + a8 *big.Int + x9 *big.Int + a9 *big.Int + x10 *big.Int + a10 *big.Int + x11 *big.Int + a11 *big.Int +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L93 +func (l *logExpMath) Pow(x *uint256.Int, y *uint256.Int) (*uint256.Int, error) { + if y.IsZero() { + return number.Number_1e18, nil + } + + if x.IsZero() { + return number.Zero, nil + } + + if !new(uint256.Int).Rsh(x, 255).IsZero() { + return nil, ErrXOutOfBounds + } + x_int256 := x.ToBig() + + if y.Cmp(l.MILD_EXPONENT_BOUND) >= 0 { + return nil, ErrYOutOfBounds + } + y_int256 := y.ToBig() + + var logXTimesY *big.Int + + if l.LN_36_LOWER_BOUND.Cmp(x_int256) < 0 && x_int256.Cmp(l.LN_36_UPPER_BOUND) < 0 { + ln36X := l._ln_36(x_int256) + logXTimesY = new(big.Int).Add( + new(big.Int).Mul(new(big.Int).Quo(ln36X, l.ONE_18), y_int256), + new(big.Int).Quo(new(big.Int).Mul(new(big.Int).Rem(ln36X, l.ONE_18), y_int256), l.ONE_18), + ) + } else { + logXTimesY = new(big.Int).Mul(l._ln(x_int256), y_int256) + } + + logXTimesY = new(big.Int).Quo(logXTimesY, l.ONE_18) + + if l.MIN_NATURAL_EXPONENT.Cmp(logXTimesY) > 0 || logXTimesY.Cmp(l.MAX_NATURAL_EXPONENT) > 0 { + return nil, ErrProductOutOfBounds + } + + result, err := l.Exp(logXTimesY) + if err != nil { + return nil, err + } + + return uint256.MustFromBig(result), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L146 +func (l *logExpMath) Exp(x *big.Int) (*big.Int, error) { + if x.Cmp(l.MIN_NATURAL_EXPONENT) < 0 || x.Cmp(l.MAX_NATURAL_EXPONENT) > 0 { + return nil, ErrInvalidExponent + } + + if x.Cmp(integer.Zero()) < 0 { + negativeXExp, err := l.Exp(new(big.Int).Neg(x)) + if err != nil { + return nil, err + } + + return new(big.Int).Quo(new(big.Int).Mul(l.ONE_18, l.ONE_18), negativeXExp), nil + } + + var firstAN *big.Int + if x.Cmp(l.x0) >= 0 { + x = new(big.Int).Sub(x, l.x0) + firstAN = l.a0 + } else if x.Cmp(l.x1) >= 0 { + x = new(big.Int).Sub(x, l.x1) + firstAN = l.a1 + } else { + firstAN = integer.One() + } + + x = new(big.Int).Mul(x, big.NewInt(100)) + product := new(big.Int).Set(l.ONE_20) + + if x.Cmp(l.x2) >= 0 { + x = new(big.Int).Sub(x, l.x2) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a2), l.ONE_20) + } + + if x.Cmp(l.x3) >= 0 { + x = new(big.Int).Sub(x, l.x3) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a3), l.ONE_20) + } + + if x.Cmp(l.x4) >= 0 { + x = new(big.Int).Sub(x, l.x4) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a4), l.ONE_20) + } + + if x.Cmp(l.x5) >= 0 { + x = new(big.Int).Sub(x, l.x5) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a5), l.ONE_20) + } + + if x.Cmp(l.x6) >= 0 { + x = new(big.Int).Sub(x, l.x6) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a6), l.ONE_20) + } + + if x.Cmp(l.x7) >= 0 { + x = new(big.Int).Sub(x, l.x7) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a7), l.ONE_20) + } + + if x.Cmp(l.x8) >= 0 { + x = new(big.Int).Sub(x, l.x8) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a8), l.ONE_20) + } + + if x.Cmp(l.x9) >= 0 { + x = new(big.Int).Sub(x, l.x9) + product = new(big.Int).Quo(new(big.Int).Mul(product, l.a9), l.ONE_20) + } + + seriesSum := new(big.Int).Set(l.ONE_20) + + term := new(big.Int).Set(x) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(2)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(3)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(4)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(5)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(6)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(7)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(8)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(9)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(10)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(11)) + seriesSum = new(big.Int).Add(seriesSum, term) + + term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(12)) + seriesSum = new(big.Int).Add(seriesSum, term) + + return new(big.Int).Quo(new(big.Int).Mul(new(big.Int).Quo(new(big.Int).Mul(product, seriesSum), l.ONE_20), firstAN), big.NewInt(100)), nil +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L466 +func (l *logExpMath) _ln_36(x *big.Int) *big.Int { + x = new(big.Int).Mul(x, l.ONE_18) + + z := new(big.Int).Quo( + new(big.Int).Mul( + new(big.Int).Sub(x, l.ONE_36), + l.ONE_36, + ), + new(big.Int).Add(x, l.ONE_36), + ) + zSquared := new(big.Int).Quo(new(big.Int).Mul(z, z), l.ONE_36) + + num := new(big.Int).Set(z) + seriesSum := new(big.Int).Set(num) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(3))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(5))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(7))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(9))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(11))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(13))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(15))) + + return new(big.Int).Mul(seriesSum, integer.Two()) +} + +// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L326 +func (l *logExpMath) _ln(a *big.Int) *big.Int { + if a.Cmp(l.ONE_18) < 0 { + return new(big.Int).Neg(l._ln(new(big.Int).Quo(new(big.Int).Mul(l.ONE_18, l.ONE_18), a))) + } + + sum := integer.Zero() + if a.Cmp(new(big.Int).Mul(l.a0, l.ONE_18)) >= 0 { + a = new(big.Int).Quo(a, l.a0) + sum = new(big.Int).Add(sum, l.x0) + } + + if a.Cmp(new(big.Int).Mul(l.a1, l.ONE_18)) >= 0 { + a = new(big.Int).Quo(a, l.a1) + sum = new(big.Int).Add(sum, l.x1) + } + + sum = new(big.Int).Mul(sum, big.NewInt(100)) + a = new(big.Int).Mul(a, big.NewInt(100)) + + if a.Cmp(l.a2) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a2) + sum = new(big.Int).Add(sum, l.x2) + } + + if a.Cmp(l.a3) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a3) + sum = new(big.Int).Add(sum, l.x3) + } + + if a.Cmp(l.a4) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a4) + sum = new(big.Int).Add(sum, l.x4) + } + + if a.Cmp(l.a5) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a5) + sum = new(big.Int).Add(sum, l.x5) + } + + if a.Cmp(l.a6) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a6) + sum = new(big.Int).Add(sum, l.x6) + } + + if a.Cmp(l.a7) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a7) + sum = new(big.Int).Add(sum, l.x7) + } + + if a.Cmp(l.a8) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a8) + sum = new(big.Int).Add(sum, l.x8) + } + + if a.Cmp(l.a9) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a9) + sum = new(big.Int).Add(sum, l.x9) + } + + if a.Cmp(l.a10) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a10) + sum = new(big.Int).Add(sum, l.x10) + } + + if a.Cmp(l.a11) >= 0 { + a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a11) + sum = new(big.Int).Add(sum, l.x11) + } + + z := new(big.Int).Quo(new(big.Int).Mul(new(big.Int).Sub(a, l.ONE_20), l.ONE_20), new(big.Int).Add(a, l.ONE_20)) + zSquared := new(big.Int).Quo(new(big.Int).Mul(z, z), l.ONE_20) + + num := z + seriesSum := num + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(3))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(5))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(7))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(9))) + + num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) + seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(11))) + + seriesSum = new(big.Int).Mul(seriesSum, integer.Two()) + + return new(big.Int).Quo(new(big.Int).Add(sum, seriesSum), big.NewInt(100)) +} diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go new file mode 100644 index 000000000..5b3fa10e0 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go @@ -0,0 +1,48 @@ +package math + +import ( + "fmt" + "testing" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/stretchr/testify/assert" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" +) + +func TestLogExpMath_Pow(t *testing.T) { + t.Run("it should return correct result", func(t *testing.T) { + result, err := LogExpMath.Pow(number.NewUint256("999955774281269788"), number.NewUint256("4000000000000000000")) + + fmt.Printf("result: %s\n", result.ToBig().String()) + + assert.Nil(t, err) + assert.Zero(t, result.Cmp(number.NewUint256("999823108860218333"))) + }) + + t.Run("it should return correct result", func(t *testing.T) { + result, err := LogExpMath.Pow(number.NewUint256("877904152923774542"), number.NewUint256("250000000000000000")) + + fmt.Printf("result: %s\n", result.ToBig().String()) + + assert.Nil(t, err) + assert.Zero(t, result.Cmp(number.NewUint256("967969728761424232"))) + }) +} + +func TestLogExpMath_Exp(t *testing.T) { + t.Run("should be return correct result", func(t *testing.T) { + result, err := LogExpMath.Exp(bignumber.NewBig10("-176906786864581")) + + assert.Nil(t, err) + assert.Zero(t, result.Cmp(bignumber.NewBig10("999823108860218333"))) + }) +} + +func TestLogExpMath_ln_36(t *testing.T) { + t.Run("it should return correct result", func(t *testing.T) { + result := LogExpMath._ln_36(bignumber.NewBig10("999955774281269788")) + + assert.Zero(t, result.Cmp(bignumber.NewBig10("-44226696716145462061506341909418"))) + }) +} diff --git a/pkg/liquidity-source/balancer-v3/math/math.go b/pkg/liquidity-source/balancer-v3/math/math.go new file mode 100644 index 000000000..6e4f56ebc --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/math.go @@ -0,0 +1,108 @@ +package math + +import ( + "github.com/holiman/uint256" +) + +func MulDivUp(a, b, c *uint256.Int) (*uint256.Int, error) { + if c.IsZero() { + return nil, ErrZeroDivision + } + + product, err := Mul(a, b) + if err != nil { + return nil, err + } + + // Equivalent to: + // result = a == 0 ? 0 : (a * b - 1) / c + 1 + if product.IsZero() { + return ZERO, nil + } + + product.Sub(product, ONE) + product.Div(product, c) + product.Add(product, ONE) + + return product, nil +} + +func MulUp(a, b *uint256.Int) (*uint256.Int, error) { + product, err := Mul(a, b) + if err != nil { + return nil, ErrMulOverflow + } + + // Equivalent to: + // result = product == 0 ? 0 : ((product - 1) / FixedPoint.ONE) + 1 + if product.IsZero() { + return ZERO, nil + } + + product.Sub(product, ONE) + product.Div(product, ONE_E18) + product.Add(product, ONE) + + return product, nil +} + +func MulDown(a, b *uint256.Int) (*uint256.Int, error) { + product, err := Mul(a, b) + if err != nil { + return nil, ErrMulOverflow + } + + return product.Div(product, ONE_E18), nil +} + +func DivUp(a, b *uint256.Int) (*uint256.Int, error) { + return MulDivUp(a, ONE_E18, b) +} + +func DivDown(a, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + + aInflated, err := Mul(a, ONE_E18) + if err != nil { + return nil, err + } + + return aInflated.Div(aInflated, b), nil +} + +func Complement(x *uint256.Int) *uint256.Int { + // Equivalent to: + // result = (x < ONE) ? (ONE - x) : 0 + result := new(uint256.Int).Set(ZERO) + if x.Lt(ONE_E18) { + result.Sub(ONE_E18, x) + } + + return result +} + +func Add(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c, overflow := new(uint256.Int).AddOverflow(a, b) + if overflow { + return nil, ErrAddOverflow + } + return c, nil +} + +func Sub(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c, overflow := new(uint256.Int).SubOverflow(a, b) + if overflow { + return nil, ErrSubOverflow + } + return c, nil +} + +func Mul(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c, overflow := new(uint256.Int).MulOverflow(a, b) + if overflow { + return nil, ErrMulOverflow + } + return c, nil +} diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go new file mode 100644 index 000000000..f663a5eca --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -0,0 +1,238 @@ +package math + +import ( + "errors" + + "github.com/holiman/uint256" +) + +var ( + ErrStableInvariantDidNotConverge = errors.New("stable invariant didn't converge") + ErrStableComputeBalanceDidNotConverge = errors.New("stable computeBalance didn't converge") + + _AMP_PRECISION = uint256.NewInt(1e3) +) + +var StableMath *stableMath + +type stableMath struct{} + +func init() { + StableMath = &stableMath{} +} + +func (s *stableMath) ComputeOutGivenExactIn( + amplificationParameter *uint256.Int, + balances []*uint256.Int, + tokenIndexIn, tokenIndexOut int, + tokenAmountIn, invariant *uint256.Int, +) (*uint256.Int, error) { + /************************************************************************************************************** + // outGivenExactIn token x for y - polynomial equation to solve // + // ay = amount out to calculate // + // by = balance token out // + // y = by - ay (finalBalanceOut) // + // D = invariant D D^(n+1) // + // A = amplification coefficient y^2 + ( S + ---------- - D) * y - ------------- = 0 // + // n = number of tokens (A * n^n) A * n^2n * P // + // S = sum of final balances but y // + // P = product of final balances but y // + **************************************************************************************************************/ + + balances[tokenIndexIn].Add(balances[tokenIndexIn], tokenAmountIn) + + finalBalanceOut, err := s.ComputeBalance(amplificationParameter, balances, invariant, tokenIndexOut) + if err != nil { + return nil, err + } + + balances[tokenIndexIn].Sub(balances[tokenIndexIn], tokenAmountIn) + + amountOut, err := Sub(balances[tokenIndexOut], finalBalanceOut) + if err != nil { + return nil, err + } + + return amountOut.SubUint64(amountOut, 1), nil +} + +func (s *stableMath) ComputeInGivenExactOut( + amplificationParameter *uint256.Int, + balances []*uint256.Int, + tokenIndexIn, tokenIndexOut int, + tokenAmountOut, invariant *uint256.Int, +) (*uint256.Int, error) { + /************************************************************************************************************** + // inGivenExactOut token x for y - polynomial equation to solve // + // ax = amount in to calculate // + // bx = balance token in // + // x = bx + ax (finalBalanceIn) // + // D = invariant D D^(n+1) // + // A = amplification coefficient x^2 + ( S + ---------- - D) * x - ------------- = 0 // + // n = number of tokens (A * n^n) A * n^2n * P // + // S = sum of final balances but x // + // P = product of final balances but x // + **************************************************************************************************************/ + + balances[tokenIndexOut].Sub(balances[tokenIndexOut], tokenAmountOut) + + finalBalanceIn, err := s.ComputeBalance(amplificationParameter, balances, invariant, tokenIndexIn) + if err != nil { + return nil, err + } + + balances[tokenIndexOut].Add(balances[tokenIndexOut], tokenAmountOut) + + amountOut, err := Sub(finalBalanceIn, balances[tokenIndexIn]) + if err != nil { + return nil, err + } + + return amountOut.AddUint64(amountOut, 1), nil +} + +func (s *stableMath) ComputeBalance( + amplificationParameter *uint256.Int, + balances []*uint256.Int, + invariant *uint256.Int, + tokenIndex int, +) (*uint256.Int, error) { + numTokens := uint256.NewInt(uint64(len(balances))) + + // A * n + ampTimesN := new(uint256.Int).Mul(amplificationParameter, numTokens) + + sumBalances := new(uint256.Int).Set(balances[0]) + balanceProduct := new(uint256.Int).Mul(balances[0], numTokens) + + // (P_D * x_j * n) / D + mulResult := new(uint256.Int) + for j := 1; j < len(balances); j++ { + mulResult.Mul(balanceProduct, balances[j]) + mulResult.Mul(mulResult, numTokens) + balanceProduct.Div(mulResult, invariant) + sumBalances.Add(sumBalances, balances[j]) + } + + sumBalances.Sub(sumBalances, balances[tokenIndex]) + + invariantSquared := new(uint256.Int).Mul(invariant, invariant) + + // c = (D^2 * AP)/(An * P_D) * x_i + numerator := new(uint256.Int).Mul(invariantSquared, _AMP_PRECISION) + denominator := new(uint256.Int).Mul(ampTimesN, balanceProduct) + c := new(uint256.Int).Div(numerator, denominator) + c.Mul(c, balances[tokenIndex]) + + // b = S + (D * AP)/An + b := new(uint256.Int).Mul(invariant, _AMP_PRECISION) + b.Div(b, ampTimesN) + b.Add(b, sumBalances) + + // y = (D^2 + c)/(D + b) + numerator.Add(invariantSquared, c) + denominator.Add(invariant, b) + tokenBalance := new(uint256.Int).Div(numerator, denominator) + + prevTokenBalance := new(uint256.Int) + for i := 0; i < 255; i++ { + prevTokenBalance.Set(tokenBalance) + + // y = (y^2 + c)/(2y + b - D) + numerator.Mul(tokenBalance, tokenBalance) + numerator.Add(numerator, c) + + denominator.Mul(tokenBalance, TWO) + denominator.Add(denominator, b) + denominator.Sub(denominator, invariant) + + tokenBalance.Div(numerator, denominator) + + if tokenBalance.Gt(prevTokenBalance) { + mulResult.Sub(tokenBalance, prevTokenBalance) + if mulResult.Cmp(ONE) <= 0 { + return tokenBalance, nil + } + } else { + mulResult.Sub(prevTokenBalance, tokenBalance) + if mulResult.Cmp(ONE) <= 0 { + return tokenBalance, nil + } + } + } + + return nil, ErrStableComputeBalanceDidNotConverge +} + +func (s *stableMath) ComputeInvariant(amplificationParameter *uint256.Int, balances []*uint256.Int) (*uint256.Int, error) { + /********************************************************************************************** + // invariant // + // D = invariant D^(n+1) // + // A = amplification coefficient A n^n S + D = A D n^n + ----------- // + // S = sum of balances n^n P // + // P = product of balances // + // n = number of tokens // + **********************************************************************************************/ + + numTokens := uint256.NewInt(uint64(len(balances))) + sum := uint256.NewInt(0) + + for _, balance := range balances { + sum.Add(sum, balance) + } + + if sum.IsZero() { + return sum, nil + } + + prevInvariant := new(uint256.Int) // Dprev in the Curve version + invariant := new(uint256.Int).Set(sum) // D in the Curve version + ampTimesN := new(uint256.Int).Mul(amplificationParameter, numTokens) // Ann in the Curve version + + tmp := new(uint256.Int) + D_P := new(uint256.Int) + numer := new(uint256.Int) + denom := new(uint256.Int) + diff := new(uint256.Int) + + for i := 0; i < 255; i++ { + prevInvariant.Set(invariant) + + // D_P = D^(n+1)/(n^n * P) + D_P.Set(invariant) + for _, balance := range balances { + // D_P = D_P * D / (x_i * n) + tmp.Mul(balance, numTokens) + D_P.Mul(D_P, invariant) + D_P.Div(D_P, tmp) + } + + // (A * n * S / AP + D_P * n) * D + numer.Mul(ampTimesN, sum) + numer.Div(numer, _AMP_PRECISION) + tmp.Mul(D_P, numTokens) + numer.Add(numer, tmp) + numer.Mul(numer, invariant) + + // ((A * n - AP) * D / AP + (n + 1) * D_P) + denom.Sub(ampTimesN, _AMP_PRECISION) + denom.Mul(denom, invariant) + denom.Div(denom, _AMP_PRECISION) + tmp.AddUint64(numTokens, 1) + tmp.Mul(tmp, D_P) + denom.Add(denom, tmp) + + invariant.Div(numer, denom) + + if invariant.Gt(prevInvariant) { + diff.Sub(invariant, prevInvariant) + } else { + diff.Sub(prevInvariant, invariant) + } + if diff.IsUint64() && diff.Uint64() <= 1 { + return invariant, nil + } + } + + return nil, ErrStableInvariantDidNotConverge +} diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go new file mode 100644 index 000000000..450a94238 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go @@ -0,0 +1,186 @@ +package math + +import ( + "testing" + + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" +) + +func TestStableMath_ComputeInvariant(t *testing.T) { + tests := []struct { + name string + amp *uint256.Int + balances []*uint256.Int + expectedInvariant *uint256.Int + expectedErr error + }{ + { + name: "Basic 2 token pool", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.NewInt(1000000), + uint256.NewInt(1000000), + }, + expectedInvariant: uint256.NewInt(2000000), + expectedErr: nil, + }, + { + name: "Imbalanced 2 token pool", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.MustFromDecimal("150946570888469782860"), + uint256.MustFromDecimal("249100103697704706751"), + }, + expectedInvariant: uint256.MustFromDecimal("400033876069185101475"), + expectedErr: nil, + }, + { + name: "3 token pool", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.NewInt(1000000), + uint256.NewInt(1000000), + uint256.NewInt(1000000), + }, + expectedInvariant: uint256.NewInt(3000000), + expectedErr: nil, + }, + { + name: "Zero balances", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.NewInt(0), + uint256.NewInt(0), + }, + expectedInvariant: uint256.NewInt(0), + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + invariant, err := StableMath.ComputeInvariant(tt.amp, tt.balances) + + if tt.expectedErr != nil { + assert.Equal(t, tt.expectedErr, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedInvariant, invariant) + } + }) + } +} + +func TestStableMath_ComputeBalance(t *testing.T) { + tests := []struct { + name string + amp *uint256.Int + balances []*uint256.Int + invariant *uint256.Int + tokenIndex int + expectedBalance *uint256.Int + expectedErr error + }{ + { + name: "Balanced 2 token pool", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.NewInt(1000000), + uint256.NewInt(1000000), + }, + invariant: uint256.NewInt(2000000), + tokenIndex: 0, + expectedBalance: uint256.NewInt(1000000), + expectedErr: nil, + }, + { + name: "Imbalanced 2 token pool", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.MustFromDecimal("150946570888469782860"), + uint256.MustFromDecimal("249100103697704706751"), + }, + invariant: uint256.MustFromDecimal("400033876069185101476"), + tokenIndex: 1, + expectedBalance: uint256.MustFromDecimal("249100103697704706751"), + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + balance, err := StableMath.ComputeBalance(tt.amp, tt.balances, tt.invariant, tt.tokenIndex) + + if tt.expectedErr != nil { + assert.Equal(t, tt.expectedErr, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedBalance, balance) + } + }) + } +} + +func TestStableMath_ComputeOutGivenExactIn(t *testing.T) { + tests := []struct { + name string + amp *uint256.Int + balances []*uint256.Int + tokenIndexIn int + tokenIndexOut int + tokenAmountIn *uint256.Int + invariant *uint256.Int + expectedAmount *uint256.Int + expectedErr error + }{ + { + name: "Equal pool swap", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.NewInt(1000000), + uint256.NewInt(1000000), + }, + tokenIndexIn: 0, + tokenIndexOut: 1, + tokenAmountIn: uint256.NewInt(100), + invariant: uint256.NewInt(2000000), + expectedAmount: uint256.NewInt(100), + expectedErr: nil, + }, + { + name: "Imbalanced pool swap", + amp: uint256.NewInt(100), + balances: []*uint256.Int{ + uint256.NewInt(1500000), + uint256.NewInt(500000), + }, + tokenIndexIn: 0, + tokenIndexOut: 1, + tokenAmountIn: uint256.NewInt(100), + invariant: uint256.NewInt(1000000), + expectedAmount: uint256.NewInt(352455), + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + amount, err := StableMath.ComputeOutGivenExactIn( + tt.amp, + tt.balances, + tt.tokenIndexIn, + tt.tokenIndexOut, + tt.tokenAmountIn, + tt.invariant, + ) + + if tt.expectedErr != nil { + assert.Equal(t, tt.expectedErr, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedAmount, amount) + } + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math.go b/pkg/liquidity-source/balancer-v3/math/weighted_math.go new file mode 100644 index 000000000..473eec182 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math.go @@ -0,0 +1,200 @@ +package math + +import ( + "errors" + + "github.com/holiman/uint256" +) + +var ( + ErrMaxInRatio = errors.New("MAX_IN_RATIO") + ErrMaxOutRatio = errors.New("MAX_OUT_RATIO") + + _MAX_IN_RATIO = uint256.NewInt(0.3e18) + _MAX_OUT_RATIO = uint256.NewInt(0.3e18) +) + +var WeightedMath *weightedMath + +type weightedMath struct { +} + +func init() { + WeightedMath = &weightedMath{} +} + +// https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L78 +func (l *weightedMath) CalcOutGivenIn( + balanceIn *uint256.Int, + weightIn *uint256.Int, + balanceOut *uint256.Int, + weightOut *uint256.Int, + amountIn *uint256.Int, +) (*uint256.Int, error) { + maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) + if err != nil { + return nil, err + } + + if amountIn.Gt(maxIn) { + return nil, ErrMaxInRatio + } + + denominator, err := FixedPoint.Add(balanceIn, amountIn) + if err != nil { + return nil, err + } + + base, err := FixedPoint.DivUp(balanceIn, denominator) + if err != nil { + return nil, err + } + + exponent, err := FixedPoint.DivDown(weightIn, weightOut) + if err != nil { + return nil, err + } + + power, err := FixedPoint.PowUp(base, exponent) + if err != nil { + return nil, err + } + + return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) +} + +// https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L69 +func (l *weightedMath) CalcOutGivenInV1( + balanceIn *uint256.Int, + weightIn *uint256.Int, + balanceOut *uint256.Int, + weightOut *uint256.Int, + amountIn *uint256.Int, +) (*uint256.Int, error) { + maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) + if err != nil { + return nil, err + } + + if amountIn.Gt(maxIn) { + return nil, ErrMaxInRatio + } + + denominator, err := FixedPoint.Add(balanceIn, amountIn) + if err != nil { + return nil, err + } + + base, err := FixedPoint.DivUp(balanceIn, denominator) + if err != nil { + return nil, err + } + + exponent, err := FixedPoint.DivDown(weightIn, weightOut) + if err != nil { + return nil, err + } + + power, err := FixedPoint.PowUpV1(base, exponent) + if err != nil { + return nil, err + } + + return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) +} + +// https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L113 +func (l *weightedMath) CalcInGivenOut( + balanceIn *uint256.Int, + weightIn *uint256.Int, + balanceOut *uint256.Int, + weightOut *uint256.Int, + amountOut *uint256.Int, +) (*uint256.Int, error) { + maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) + if err != nil { + return nil, err + } + + // Cannot exceed maximum out ratio + if amountOut.Gt(maxOut) { + return nil, ErrMaxOutRatio + } + + remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) + if err != nil { + return nil, err + } + + base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) + if err != nil { + return nil, err + } + + exponent, err := FixedPoint.DivUp(weightOut, weightIn) + if err != nil { + return nil, err + } + + power, err := FixedPoint.PowUp(base, exponent) + if err != nil { + return nil, err + } + + // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so + // the following subtraction should never revert. + ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) + if err != nil { + return nil, err + } + + return FixedPoint.MulUp(balanceIn, ratio) +} + +// https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L104 +func (l *weightedMath) CalcInGivenOutV1( + balanceIn *uint256.Int, + weightIn *uint256.Int, + balanceOut *uint256.Int, + weightOut *uint256.Int, + amountOut *uint256.Int, +) (*uint256.Int, error) { + maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) + if err != nil { + return nil, err + } + + // Cannot exceed maximum out ratio + if amountOut.Gt(maxOut) { + return nil, ErrMaxOutRatio + } + + remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) + if err != nil { + return nil, err + } + + base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) + if err != nil { + return nil, err + } + + exponent, err := FixedPoint.DivUp(weightOut, weightIn) + if err != nil { + return nil, err + } + + power, err := FixedPoint.PowUpV1(base, exponent) + if err != nil { + return nil, err + } + + // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so + // the following subtraction should never revert. + ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) + if err != nil { + return nil, err + } + + return FixedPoint.MulUp(balanceIn, ratio) +} diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go new file mode 100644 index 000000000..ed5050a4e --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go @@ -0,0 +1,158 @@ +package math + +import ( + "testing" + + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" +) + +func TestCalcOutGivenIn(t *testing.T) { + t.Run("1.should return OK", func(t *testing.T) { + // input + balanceIn := uint256.MustFromDecimal("2133741937219414819371293") + weightIn := uint256.MustFromDecimal("10") + balanceOut := uint256.MustFromDecimal("548471973423647283412313") + weightOut := uint256.MustFromDecimal("20") + amountIn := uint256.MustFromDecimal("21481937129313123729") + + // expected + expected := "2760912942840907991" + + // calculation + result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + + // assert + assert.Nil(t, err) + assert.Equal(t, expected, result.Dec()) + }) + + t.Run("2.should return OK", func(t *testing.T) { + // input + balanceIn := uint256.MustFromDecimal("92174932794319461529478329") + weightIn := uint256.MustFromDecimal("15") + balanceOut := uint256.MustFromDecimal("2914754379179427149231562") + weightOut := uint256.MustFromDecimal("5") + amountIn := uint256.MustFromDecimal("14957430248210") + + // expected + expected := "1389798609308" + + // calculation + result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + + // assert + assert.Nil(t, err) + assert.Equal(t, expected, result.Dec()) + }) + + t.Run("3.should return OK", func(t *testing.T) { + // input + balanceIn := uint256.MustFromDecimal("28430120665864259131432") + weightIn := uint256.MustFromDecimal("100000000000000000") + balanceOut := uint256.MustFromDecimal("10098902157921113397") + weightOut := uint256.MustFromDecimal("30000000000000000") + amountIn := uint256.MustFromDecimal("6125185803357185587126") + + // expected + expected := "4828780052665314529" + + // calculation + result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + + // assert + assert.Nil(t, err) + assert.Equal(t, expected, result.Dec()) + }) + + t.Run("4.should return error exceed amount in ratio", func(t *testing.T) { + // input + balanceIn := uint256.MustFromDecimal("92174932794319461529478329") + weightIn := uint256.MustFromDecimal("15") + balanceOut := uint256.MustFromDecimal("2914754379179427149231562") + weightOut := uint256.MustFromDecimal("5") + amountIn := uint256.MustFromDecimal("92174932794319461529478329") + + // calculation + _, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + + // assert + assert.ErrorIs(t, err, ErrMaxInRatio) + }) +} + +func Test_weightedMath_CalcInGivenOut(t *testing.T) { + type args struct { + balanceIn *uint256.Int + weightIn *uint256.Int + balanceOut *uint256.Int + weightOut *uint256.Int + amountOut *uint256.Int + } + tests := []struct { + name string + args args + want *uint256.Int + wantErr error + }{ + { + name: "1. should return OK", + args: args{ + balanceIn: uint256.MustFromDecimal("2133741937219414819371293"), + weightIn: uint256.MustFromDecimal("10"), + balanceOut: uint256.MustFromDecimal("548471973423647283412313"), + weightOut: uint256.MustFromDecimal("20"), + amountOut: uint256.MustFromDecimal("21481937129313123729"), + }, + want: uint256.MustFromDecimal("167153858139050441751"), + wantErr: nil, + }, + { + name: "2. should return OK", + args: args{ + balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), + weightIn: uint256.MustFromDecimal("15"), + balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), + weightOut: uint256.MustFromDecimal("5"), + amountOut: uint256.MustFromDecimal("14957430248210"), + }, + want: uint256.MustFromDecimal("158591027569670"), + wantErr: nil, + }, + { + name: "3. should return OK", + args: args{ + balanceIn: uint256.MustFromDecimal("28430120665864259131432"), + weightIn: uint256.MustFromDecimal("100000000000000000"), + balanceOut: uint256.MustFromDecimal("100989021579211133970000"), + weightOut: uint256.MustFromDecimal("30000000000000000"), + amountOut: uint256.MustFromDecimal("6125185803357185587126"), + }, + want: uint256.MustFromDecimal("538695515311227973058"), + wantErr: nil, + }, + { + name: "4. should return error exceed amount out ratio", + args: args{ + balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), + weightIn: uint256.MustFromDecimal("15"), + balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), + weightOut: uint256.MustFromDecimal("5"), + amountOut: uint256.MustFromDecimal("2914754379179427149231562"), + }, + want: nil, + wantErr: ErrMaxOutRatio, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + l := &weightedMath{} + got, err := l.CalcInGivenOut(tt.args.balanceIn, tt.args.weightIn, tt.args.balanceOut, tt.args.weightOut, tt.args.amountOut) + if err != nil { + assert.ErrorIsf(t, err, tt.wantErr, "weightedMath.CalcInGivenOut() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equalf(t, tt.want, got, "weightedMath.CalcInGivenOut() = %v, want %v", got, tt.want) + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/shared/abis.go b/pkg/liquidity-source/balancer-v3/shared/abis.go new file mode 100644 index 000000000..8cfd7d3f9 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/abis.go @@ -0,0 +1,28 @@ +package shared + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var ( + VaultABI abi.ABI +) + +func init() { + builder := []struct { + ABI *abi.ABI + data []byte + }{ + {&VaultABI, vaultJson}, + } + + for _, b := range builder { + var err error + *b.ABI, err = abi.JSON(bytes.NewReader(b.data)) + if err != nil { + panic(err) + } + } +} diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json new file mode 100644 index 000000000..ccaba5181 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json @@ -0,0 +1,1179 @@ +[ + { + "inputs": [ + { + "internalType": "contract IAuthorizer", + "name": "authorizer", + "type": "address" + }, + { + "internalType": "contract IWETH", + "name": "weth", + "type": "address" + }, + { + "internalType": "uint256", + "name": "pauseWindowDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodDuration", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "AuthorizerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ExternalBalanceTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IFlashLoanRecipient", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "feeAmount", + "type": "uint256" + } + ], + "name": "FlashLoan", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "delta", + "type": "int256" + } + ], + "name": "InternalBalanceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "int256[]", + "name": "deltas", + "type": "int256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "protocolFeeAmounts", + "type": "uint256[]" + } + ], + "name": "PoolBalanceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "assetManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "cashDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "managedDelta", + "type": "int256" + } + ], + "name": "PoolBalanceManaged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "poolAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum IVault.PoolSpecialization", + "name": "specialization", + "type": "uint8" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "RelayerApprovalChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "TokensDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "assetManagers", + "type": "address[]" + } + ], + "name": "TokensRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "contract IWETH", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum IVault.SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "assetInIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "assetOutIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct IVault.BatchSwapStep[]", + "name": "swaps", + "type": "tuple[]" + }, + { + "internalType": "contract IAsset[]", + "name": "assets", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bool", + "name": "fromInternalBalance", + "type": "bool" + }, + { + "internalType": "address payable", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "toInternalBalance", + "type": "bool" + } + ], + "internalType": "struct IVault.FundManagement", + "name": "funds", + "type": "tuple" + }, + { + "internalType": "int256[]", + "name": "limits", + "type": "int256[]" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "batchSwap", + "outputs": [ + { + "internalType": "int256[]", + "name": "assetDeltas", + "type": "int256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "deregisterTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address payable", + "name": "recipient", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IAsset[]", + "name": "assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "minAmountsOut", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "toInternalBalance", + "type": "bool" + } + ], + "internalType": "struct IVault.ExitPoolRequest", + "name": "request", + "type": "tuple" + } + ], + "name": "exitPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IFlashLoanRecipient", + "name": "recipient", + "type": "address" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "flashLoan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getActionId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "contract IAuthorizer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "getInternalBalance", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getNextNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "paused", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "pauseWindowEndTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodEndTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getPool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "enum IVault.PoolSpecialization", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getPoolTokenInfo", + "outputs": [ + { + "internalType": "uint256", + "name": "cash", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "address", + "name": "assetManager", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getPoolTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolFeesCollector", + "outputs": [ + { + "internalType": "contract ProtocolFeesCollector", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "hasApprovedRelayer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IAsset[]", + "name": "assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "maxAmountsIn", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "fromInternalBalance", + "type": "bool" + } + ], + "internalType": "struct IVault.JoinPoolRequest", + "name": "request", + "type": "tuple" + } + ], + "name": "joinPool", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum IVault.PoolBalanceOpKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct IVault.PoolBalanceOp[]", + "name": "ops", + "type": "tuple[]" + } + ], + "name": "managePoolBalance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum IVault.UserBalanceOpKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "contract IAsset", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address payable", + "name": "recipient", + "type": "address" + } + ], + "internalType": "struct IVault.UserBalanceOp[]", + "name": "ops", + "type": "tuple[]" + } + ], + "name": "manageUserBalance", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum IVault.SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "assetInIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "assetOutIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct IVault.BatchSwapStep[]", + "name": "swaps", + "type": "tuple[]" + }, + { + "internalType": "contract IAsset[]", + "name": "assets", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bool", + "name": "fromInternalBalance", + "type": "bool" + }, + { + "internalType": "address payable", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "toInternalBalance", + "type": "bool" + } + ], + "internalType": "struct IVault.FundManagement", + "name": "funds", + "type": "tuple" + } + ], + "name": "queryBatchSwap", + "outputs": [ + { + "internalType": "int256[]", + "name": "", + "type": "int256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum IVault.PoolSpecialization", + "name": "specialization", + "type": "uint8" + } + ], + "name": "registerPool", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "assetManagers", + "type": "address[]" + } + ], + "name": "registerTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "setAuthorizer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "setPaused", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setRelayerApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "enum IVault.SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "contract IAsset", + "name": "assetIn", + "type": "address" + }, + { + "internalType": "contract IAsset", + "name": "assetOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct IVault.SingleSwap", + "name": "singleSwap", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bool", + "name": "fromInternalBalance", + "type": "bool" + }, + { + "internalType": "address payable", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "toInternalBalance", + "type": "bool" + } + ], + "internalType": "struct IVault.FundManagement", + "name": "funds", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "uint256", + "name": "amountCalculated", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/shared/constant.go b/pkg/liquidity-source/balancer-v3/shared/constant.go new file mode 100644 index 000000000..843531f32 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/constant.go @@ -0,0 +1,26 @@ +package shared + +import "github.com/holiman/uint256" + +const ( + VaultMethodGetPoolTokens = "getPoolTokens" + PoolMethodGetSwapFeePercentage = "getSwapFeePercentage" + PoolMethodGetPausedState = "getPausedState" + PoolMethodGetAmplificationParameter = "getAmplificationParameter" + PoolMethodGetVault = "getVault" + PoolMethodVersion = "version" + + PoolVersion1 = 1 + PoolVersion2 = 2 +) + +type Rounding int + +const ( + ROUND_UP Rounding = iota + ROUND_DOWN +) + +var ( + MINIMUM_TRADE_AMOUNT = uint256.NewInt(1000000) // to be more general, this value should be queried from the VaultAdmin contract +) diff --git a/pkg/liquidity-source/balancer-v3/shared/embed.go b/pkg/liquidity-source/balancer-v3/shared/embed.go new file mode 100644 index 000000000..ee4150e76 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/embed.go @@ -0,0 +1,6 @@ +package shared + +import _ "embed" + +//go:embed abis/Vault.json +var vaultJson []byte diff --git a/pkg/liquidity-source/balancer-v3/shared/error.go b/pkg/liquidity-source/balancer-v3/shared/error.go new file mode 100644 index 000000000..e421487ad --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/error.go @@ -0,0 +1,8 @@ +package shared + +import "errors" + +var ( + ErrTradeAmountTooSmall = errors.New("trade amount is too small") + ErrProtocolFeesExceedTotalCollected = errors.New("protocolFees exceed totalCollected") +) diff --git a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go new file mode 100644 index 000000000..8494f5c22 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go @@ -0,0 +1,99 @@ +package shared + +import ( + "context" + "math/big" + "net/http" + "time" + + "github.com/goccy/go-json" + "github.com/machinebox/graphql" + + graphqlpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/graphql" +) + +type ( + PoolsListUpdater struct { + config *Config + graphqlClient *graphql.Client + } + + Config struct { + DexID string + SubgraphAPI string + SubgraphHeaders http.Header + NewPoolLimit int + } + + Metadata struct { + LastBlockTimestamp *big.Int `json:"lastBlockTimestamp"` + } +) + +const graphQLRequestTimeout = 20 * time.Second + +func NewPoolsListUpdater(config *Config) *PoolsListUpdater { + graphqlClient := graphqlpkg.New(graphqlpkg.Config{ + Url: config.SubgraphAPI, + Header: config.SubgraphHeaders, + Timeout: graphQLRequestTimeout, + }) + + return &PoolsListUpdater{ + config: config, + graphqlClient: graphqlClient, + } +} + +func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte) ([]*SubgraphPool, []byte, error) { + var metadata Metadata + if len(metadataBytes) > 0 { + if err := json.Unmarshal(metadataBytes, &metadata); err != nil { + return nil, nil, err + } + } + if metadata.LastBlockTimestamp == nil { + metadata.LastBlockTimestamp = big.NewInt(0) + } + + pools, lastBlockTimestamp, err := u.querySubgraph(ctx, metadata.LastBlockTimestamp) + if err != nil { + return nil, nil, err + } + + if len(pools) == 0 { + return nil, metadataBytes, nil + } + + newMetadataBytes, err := json.Marshal(Metadata{ + LastBlockTimestamp: lastBlockTimestamp, + }) + if err != nil { + return nil, nil, err + } + + return pools, newMetadataBytes, nil +} + +func (u *PoolsListUpdater) querySubgraph(ctx context.Context, lastBlockTimestamp *big.Int) ([]*SubgraphPool, *big.Int, error) { + var response struct { + Pools []*SubgraphPool `json:"pools"` + } + + query := BuildSubgraphPoolsQuery( + lastBlockTimestamp, + u.config.NewPoolLimit, + 0, + ) + req := graphql.NewRequest(query) + + if err := u.graphqlClient.Run(ctx, req, &response); err != nil { + return nil, nil, err + } + + if len(response.Pools) != 0 { + lastBlockTimestamp = response.Pools[len(response.Pools)-1].CreateTime + } + + return response.Pools, lastBlockTimestamp, nil +} diff --git a/pkg/liquidity-source/balancer-v3/shared/scaling_helper.go b/pkg/liquidity-source/balancer-v3/shared/scaling_helper.go new file mode 100644 index 000000000..6f1a26786 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/scaling_helper.go @@ -0,0 +1,45 @@ +package shared + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/holiman/uint256" +) + +func toScaled18ApplyRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + scaledAmount := new(uint256.Int).Mul(amount, scalingFactor) + return math.MulUp(scaledAmount, tokenRate) +} + +func toScaled18ApplyRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + scaledAmount := new(uint256.Int).Mul(amount, scalingFactor) + return math.MulDown(scaledAmount, tokenRate) +} + +func computeRateRoundUp(rate *uint256.Int) *uint256.Int { + div := new(uint256.Int).Div(rate, math.ONE_E18) + div.Mul(div, math.ONE_E18) + + if div.Eq(rate) { + return new(uint256.Int).Set(rate) + } + + return new(uint256.Int).Add(rate, math.ONE) +} + +func toRawUndoRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + divisor, err := math.Mul(scalingFactor, tokenRate) + if err != nil { + return nil, err + } + + return math.DivDown(amount, divisor) +} + +func toRawUndoRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + divisor, err := math.Mul(scalingFactor, tokenRate) + if err != nil { + return nil, err + } + + return math.DivUp(amount, divisor) +} diff --git a/pkg/liquidity-source/balancer-v3/shared/subgraph.go b/pkg/liquidity-source/balancer-v3/shared/subgraph.go new file mode 100644 index 000000000..8694fd63a --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/subgraph.go @@ -0,0 +1,46 @@ +package shared + +import ( + "fmt" + "math/big" +) + +type SubgraphPool struct { + ID string `json:"id"` + Address string `json:"address"` + CreateTime *big.Int `json:"blockTimestamp"` + Tokens []struct { + Address string `json:"address"` + Decimals int `json:"decimals"` + ScalingFactor string `json:"scalingFactor"` + } `json:"tokens"` +} + +func BuildSubgraphPoolsQuery( + lastBlockTimestamp *big.Int, + first int, + skip int, +) string { + q := `{ + pools( + where : { + blockTimestamp_gte: %v + }, + first: %d, + skip: %d, + orderBy: blockTimestamp, + orderDirection: asc, + ) { + id + address + blockTimestamp + tokens { + address + decimals + scalingFactor + } + } + }` + + return fmt.Sprintf(q, lastBlockTimestamp, first, skip) +} diff --git a/pkg/liquidity-source/balancer-v3/shared/type.go b/pkg/liquidity-source/balancer-v3/shared/type.go new file mode 100644 index 000000000..2c9c22160 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/type.go @@ -0,0 +1,22 @@ +package shared + +import "github.com/holiman/uint256" + +type PoolInfo struct { + Name string `json:"-"` + Version int `json:"version"` + Deployment string `json:"-"` +} + +type VaultSwapParams struct { + IsExactIn bool + IndexIn int + IndexOut int + AmountGiven *uint256.Int + DecimalScalingFactor *uint256.Int + TokenRate *uint256.Int + AmplificationParameter *uint256.Int + SwapFeePercentage *uint256.Int + AggregateSwapFeePercentage *uint256.Int + BalancesLiveScaled18 []*uint256.Int +} diff --git a/pkg/liquidity-source/balancer-v3/shared/vault.go b/pkg/liquidity-source/balancer-v3/shared/vault.go new file mode 100644 index 000000000..da5c000fd --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/vault.go @@ -0,0 +1,120 @@ +package shared + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/holiman/uint256" +) + +func Swap( + param VaultSwapParams, + onSwap func(_ bool, _, _ int, _ *uint256.Int) (*uint256.Int, error), +) (*uint256.Int, *uint256.Int, *uint256.Int, error) { + amountGivenScaled18, err := ComputeAmountGivenScaled18(true, param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) + if err != nil { + return nil, nil, nil, err + } + + swapFeeScaled18, err := math.MulUp(amountGivenScaled18, param.SwapFeePercentage) + if err != nil { + return nil, nil, nil, err + } + + amountGivenScaled18, err = math.Sub(amountGivenScaled18, swapFeeScaled18) + if err != nil { + return nil, nil, nil, err + } + + if amountGivenScaled18.Lt(MINIMUM_TRADE_AMOUNT) { + return nil, nil, nil, ErrTradeAmountTooSmall + } + + amountCalculatedScaled18, err := onSwap(param.IsExactIn, param.IndexIn, param.IndexOut, amountGivenScaled18) + if err != nil { + return nil, nil, nil, err + } + + if amountCalculatedScaled18.Lt(MINIMUM_TRADE_AMOUNT) { + return nil, nil, nil, ErrTradeAmountTooSmall + } + + amountCalculated, err := ComputeAmountCalculatedRaw(true, amountGivenScaled18, param.SwapFeePercentage, param.DecimalScalingFactor, param.TokenRate) + if err != nil { + return nil, nil, nil, err + } + + totalSwapFee, aggregateFee, err := ComputeAggregateSwapFees(true, swapFeeScaled18, param.AggregateSwapFeePercentage, + param.DecimalScalingFactor, param.TokenRate) + if err != nil { + return nil, nil, nil, err + } + + return amountCalculated, totalSwapFee, aggregateFee, nil +} + +func ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + if isExactIn { + return toScaled18ApplyRateRoundDown(amountGiven, decimalScalingFactor, tokenRate) + } + + return toScaled18ApplyRateRoundUp(amountGiven, decimalScalingFactor, computeRateRoundUp(tokenRate)) +} + +func ComputeAmountCalculatedRaw( + isExactIn bool, + amountCalculatedScaled18, swapFeePercentage, + decimalScalingFactor, tokenRate *uint256.Int, +) (*uint256.Int, error) { + if isExactIn { + return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, computeRateRoundUp(tokenRate)) + } + + totalSwapFeeAmountScaled18, err := math.MulDivUp(amountCalculatedScaled18, swapFeePercentage, math.Complement(swapFeePercentage)) + if err != nil { + return nil, err + } + + amountCalculatedScaled18, err = math.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) + if err != nil { + return nil, err + } + + return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, tokenRate) +} + +func ComputeAggregateSwapFees( + isExactIn bool, + totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, + decimalScalingFactor, tokenRate *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + if totalSwapFeeAmountScaled18.IsZero() { + return math.ZERO, math.ZERO, nil + } + + totalSwapFeeAmountRaw, err := toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) + if err != nil { + return nil, nil, err + } + + // should check if pool is in Recovery Mode + aggregateFeeAmountRaw, err := math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) + if err != nil { + return nil, nil, err + } + + if aggregateFeeAmountRaw.Gt(totalSwapFeeAmountRaw) { + return nil, nil, ErrProtocolFeesExceedTotalCollected + } + + return totalSwapFeeAmountRaw, aggregateFeeAmountRaw, nil +} + +func UpdateLiveBalance( + param VaultSwapParams, + rounding Rounding, +) (*uint256.Int, error) { + if rounding == ROUND_UP { + return toScaled18ApplyRateRoundUp(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) + } + + return toScaled18ApplyRateRoundDown(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) +} diff --git a/pkg/liquidity-source/balancer-v3/stable/abis.go b/pkg/liquidity-source/balancer-v3/stable/abis.go new file mode 100644 index 000000000..119698e95 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/abis.go @@ -0,0 +1,28 @@ +package stable + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var ( + poolABI abi.ABI +) + +func init() { + builder := []struct { + ABI *abi.ABI + data []byte + }{ + {&poolABI, poolJson}, + } + + for _, b := range builder { + var err error + *b.ABI, err = abi.JSON(bytes.NewReader(b.data)) + if err != nil { + panic(err) + } + } +} diff --git a/pkg/liquidity-source/balancer-v3/stable/abis/StablePool.json b/pkg/liquidity-source/balancer-v3/stable/abis/StablePool.json new file mode 100644 index 000000000..ddc45b28a --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/abis/StablePool.json @@ -0,0 +1,1202 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amplificationParameter", + "type": "uint256" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "internalType": "struct StablePool.NewPoolParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "contract IVault", + "name": "vault", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AmpUpdateAlreadyStarted", + "type": "error" + }, + { + "inputs": [], + "name": "AmpUpdateDurationTooShort", + "type": "error" + }, + { + "inputs": [], + "name": "AmpUpdateNotStarted", + "type": "error" + }, + { + "inputs": [], + "name": "AmpUpdateRateTooFast", + "type": "error" + }, + { + "inputs": [], + "name": "AmplificationFactorTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "AmplificationFactorTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "ECDSAInvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "ECDSAInvalidSignatureS", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "ERC2612ExpiredSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC2612InvalidSigner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "currentNonce", + "type": "uint256" + } + ], + "name": "InvalidAccountNonce", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidShortString", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "bits", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintDowncast", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderIsNotVault", + "type": "error" + }, + { + "inputs": [], + "name": "SenderNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "StableComputeBalanceDidNotConverge", + "type": "error" + }, + { + "inputs": [], + "name": "StableInvariantDidNotConverge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "str", + "type": "string" + } + ], + "name": "StringTooLong", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDivision", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "startValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + } + ], + "name": "AmpUpdateStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "currentValue", + "type": "uint256" + } + ], + "name": "AmpUpdateStopped", + "type": "event" + }, + { + "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": [], + "name": "EIP712DomainChanged", + "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" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "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": "amount", + "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": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "tokenInIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "invariantRatio", + "type": "uint256" + } + ], + "name": "computeBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "enum Rounding", + "name": "rounding", + "type": "uint8" + } + ], + "name": "computeInvariant", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "emitApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "emitTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getActionId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAggregateFeePercentages", + "outputs": [ + { + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAmplificationParameter", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isUpdating", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "precision", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAmplificationState", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "startValue", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "endValue", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "startTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "endTime", + "type": "uint32" + } + ], + "internalType": "struct AmplificationState", + "name": "amplificationState", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "precision", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentLiveBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumInvariantRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumInvariantRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStablePoolDynamicData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "staticSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bptRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amplificationParameter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endValue", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "startTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "endTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isAmpUpdating", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolPaused", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInRecoveryMode", + "type": "bool" + } + ], + "internalType": "struct StablePoolDynamicData", + "name": "data", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStablePoolImmutableData", + "outputs": [ + { + "components": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "amplificationParameterPrecision", + "type": "uint256" + } + ], + "internalType": "struct StablePoolImmutableData", + "name": "data", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStaticSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTokenInfo", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "lastBalancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [ + { + "internalType": "contract IVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "incrementNonce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amountGivenScaled18", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "balancesScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "indexIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "indexOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct PoolSwapParams", + "name": "request", + "type": "tuple" + } + ], + "name": "onSwap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "rawEndValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + } + ], + "name": "startAmplificationParameterUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stopAmplificationParameterUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "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": "amount", + "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": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/stable/config.go b/pkg/liquidity-source/balancer-v3/stable/config.go new file mode 100644 index 000000000..79a3b7b69 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/config.go @@ -0,0 +1,10 @@ +package stable + +import "net/http" + +type Config struct { + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` +} diff --git a/pkg/liquidity-source/balancer-v3/stable/constant.go b/pkg/liquidity-source/balancer-v3/stable/constant.go new file mode 100644 index 000000000..5f941008a --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/constant.go @@ -0,0 +1,11 @@ +package stable + +const ( + DexType = "balancer-v2-stable" + + PoolType = "StablePool" +) + +var ( + defaultGas = Gas{Swap: 80000} +) diff --git a/pkg/liquidity-source/balancer-v3/stable/embed.go b/pkg/liquidity-source/balancer-v3/stable/embed.go new file mode 100644 index 000000000..4ab200018 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/embed.go @@ -0,0 +1,6 @@ +package stable + +import _ "embed" + +//go:embed abis/StablePool.json +var poolJson []byte diff --git a/pkg/liquidity-source/balancer-v3/stable/error.go b/pkg/liquidity-source/balancer-v3/stable/error.go new file mode 100644 index 000000000..f41960d1b --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/error.go @@ -0,0 +1,12 @@ +package stable + +import "errors" + +var ( + ErrTokenNotRegistered = errors.New("TOKEN_NOT_REGISTERED") + ErrInvalidReserve = errors.New("invalid reserve") + ErrInvalidAmountIn = errors.New("invalid amount in") + ErrInvalidAmountOut = errors.New("invalid amount out") + ErrInvalidPoolType = errors.New("invalid pool type") + ErrInvalidPoolID = errors.New("invalid pool id") +) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go new file mode 100644 index 000000000..f4de77088 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -0,0 +1,333 @@ +package stable + +import ( + "errors" + "math/big" + + "github.com/goccy/go-json" + "github.com/holiman/uint256" + "github.com/samber/lo" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" + "github.com/KyberNetwork/logger" +) + +var ( + ErrInvalidSwapFeePercentage = errors.New("invalid swap fee percentage") + ErrPoolIsPaused = errors.New("pool is paused") + ErrInvalidAmp = errors.New("invalid amp") + ErrNotTwoTokens = errors.New("not two tokens") + + ErrTradeAmountTooSmall = errors.New("trade amount is too small") + + ErrVaultIsLocked = errors.New("vault is locked") +) + +type PoolSimulator struct { + poolpkg.Pool + + swapFeePercentage *uint256.Int + aggregateSwapFeePercentage *uint256.Int + + amplificationParameter *uint256.Int + balancesLiveScaled18 []*uint256.Int + decimalScalingFactors []*uint256.Int + tokenRates []*uint256.Int + + isVaultLocked bool + isPaused bool + + vault string + + poolType string + poolVersion int +} + +func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { + var ( + extra Extra + staticExtra StaticExtra + + tokens = make([]string, len(entityPool.Tokens)) + reserves = make([]*big.Int, len(entityPool.Tokens)) + ) + + if err := json.Unmarshal([]byte(entityPool.Extra), &extra); err != nil { + return nil, err + } + + if err := json.Unmarshal([]byte(entityPool.StaticExtra), &staticExtra); err != nil { + return nil, err + } + + for idx := 0; idx < len(entityPool.Tokens); idx++ { + tokens[idx] = entityPool.Tokens[idx].Address + reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) + } + + poolInfo := poolpkg.PoolInfo{ + Address: entityPool.Address, + Exchange: entityPool.Exchange, + Type: entityPool.Type, + Tokens: tokens, + Reserves: reserves, + Checked: true, + BlockNumber: entityPool.BlockNumber, + } + + return &PoolSimulator{ + Pool: poolpkg.Pool{Info: poolInfo}, + isPaused: extra.IsPaused, + isVaultLocked: extra.IsVaultLocked, + swapFeePercentage: extra.SwapFeePercentage, + aggregateSwapFeePercentage: extra.AggregateSwapFeePercentage, + amplificationParameter: extra.AmplificationParameter, + balancesLiveScaled18: extra.BalancesLiveScaled18, + tokenRates: extra.TokenRates, + decimalScalingFactors: extra.DecimalScalingFactors, + vault: staticExtra.Vault, + poolType: staticExtra.PoolType, + poolVersion: staticExtra.PoolVersion, + }, nil +} + +func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { + if p.isVaultLocked { + return nil, ErrVaultIsLocked + } + + if p.isPaused { + return nil, ErrPoolIsPaused + } + + tokenAmountOut, tokenIn := params.TokenAmountOut, params.TokenIn + + indexIn, indexOut := p.GetTokenIndex(tokenIn), p.GetTokenIndex(tokenAmountOut.Token) + if indexIn < 0 || indexOut < 0 { + return nil, ErrTokenNotRegistered + } + + amountOut, overflow := uint256.FromBig(tokenAmountOut.Amount) + if overflow { + return nil, ErrInvalidAmountOut + } + + amountIn, totalSwapFee, aggregateSwapFee, err := shared.Swap(shared.VaultSwapParams{ + IsExactIn: false, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGiven: amountOut, + DecimalScalingFactor: p.decimalScalingFactors[indexOut], + TokenRate: p.decimalScalingFactors[indexOut], + AmplificationParameter: p.amplificationParameter, + SwapFeePercentage: p.swapFeePercentage, + AggregateSwapFeePercentage: p.aggregateSwapFeePercentage, + BalancesLiveScaled18: p.balancesLiveScaled18, + }, p.OnSwap) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: tokenIn, + Amount: amountIn.ToBig(), + }, + Fee: &poolpkg.TokenAmount{ + Token: tokenIn, + Amount: totalSwapFee.ToBig(), + }, + SwapInfo: SwapInfo{ + AggregateFee: aggregateSwapFee.ToBig(), + }, + Gas: defaultGas.Swap, + }, nil +} + +func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { + return PoolMetaInfo{ + Vault: s.vault, + PoolType: s.poolType, + PoolVersion: s.poolVersion, + TokenOutIndex: s.GetTokenIndex(tokenOut), + BlockNumber: s.Info.BlockNumber, + } +} + +func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { + tokenIndexIn := p.GetTokenIndex(params.TokenAmountIn.Token) + tokenIndexOut := p.GetTokenIndex(params.TokenAmountOut.Token) + + swapInfo, ok := params.SwapInfo.(SwapInfo) + if !ok { + return + } + + updatedRawBalanceIn := new(big.Int) + updatedRawBalanceIn.Add(p.Info.Reserves[tokenIndexIn], params.TokenAmountIn.Amount) + updatedRawBalanceIn.Sub(updatedRawBalanceIn, swapInfo.AggregateFee) + p.Info.Reserves[tokenIndexIn] = updatedRawBalanceIn + + vaultParams := shared.VaultSwapParams{ + AmountGiven: uint256.MustFromBig(updatedRawBalanceIn), + DecimalScalingFactor: p.decimalScalingFactors[tokenIndexIn], + TokenRate: p.tokenRates[tokenIndexIn], + } + + updatedLiveBalanceIn, err := shared.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + if err != nil { + logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + return + } + p.balancesLiveScaled18[tokenIndexIn] = updatedLiveBalanceIn + + updatedRawBalanceOut := new(big.Int) + updatedRawBalanceOut.Sub(p.Info.Reserves[tokenIndexOut], params.TokenAmountOut.Amount) + p.Info.Reserves[tokenIndexOut] = updatedRawBalanceOut + + vaultParams.AmountGiven.SetFromBig(updatedRawBalanceOut) + vaultParams.DecimalScalingFactor = p.decimalScalingFactors[tokenIndexOut] + vaultParams.TokenRate = p.tokenRates[tokenIndexOut] + + updatedLiveBalanceOut, err := shared.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + if err != nil { + logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + return + } + p.balancesLiveScaled18[tokenIndexOut] = updatedLiveBalanceOut +} + +func (p *PoolSimulator) computeBalance(tokenInIndex int, invariantRatio *uint256.Int) (*uint256.Int, error) { + invariant, err := p.computeInvariant(shared.ROUND_UP) + if err != nil { + return nil, err + } + + newInvariant, err := math.MulUp(invariant, invariantRatio) + if err != nil { + return nil, err + } + + return math.StableMath.ComputeBalance(p.amplificationParameter, p.balancesLiveScaled18, newInvariant, tokenInIndex) +} + +func (p *PoolSimulator) OnSwap(isExactIn bool, indexIn, indexOut int, amountInScaled18 *uint256.Int) (*uint256.Int, error) { + invariant, err := p.computeInvariant(shared.ROUND_DOWN) + if err != nil { + return nil, err + } + + var amountOutScaled18 *uint256.Int + if isExactIn { + amountOutScaled18, err = math.StableMath.ComputeOutGivenExactIn( + p.amplificationParameter, + p.balancesLiveScaled18, + indexIn, + indexOut, + amountInScaled18, + invariant, + ) + } else { + amountOutScaled18, err = math.StableMath.ComputeInGivenExactOut( + p.amplificationParameter, + p.balancesLiveScaled18, + indexIn, + indexOut, + amountInScaled18, + invariant, + ) + } + if err != nil { + return nil, err + } + + return amountOutScaled18, nil +} + +func (p *PoolSimulator) computeInvariant(rounding shared.Rounding) (*uint256.Int, error) { + invariant, err := math.StableMath.ComputeInvariant(p.amplificationParameter, p.balancesLiveScaled18) + if err != nil { + return nil, err + } + + if invariant.Sign() > 0 && rounding == shared.ROUND_UP { + return invariant.AddUint64(invariant, 1), nil + } + + return invariant, nil +} + +func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { + if p.isVaultLocked { + return nil, ErrVaultIsLocked + } + + if p.isPaused { + return nil, ErrPoolIsPaused + } + + tokenAmountIn, tokenOut := params.TokenAmountIn, params.TokenOut + + indexIn, indexOut := p.GetTokenIndex(tokenAmountIn.Token), p.GetTokenIndex(tokenOut) + if indexIn < 0 || indexOut < 0 { + return nil, ErrTokenNotRegistered + } + + amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) + if overflow { + return nil, ErrInvalidAmountIn + } + + amountOut, totalSwapFee, aggregateFee, err := shared.Swap(shared.VaultSwapParams{ + IsExactIn: true, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGiven: amountIn, + DecimalScalingFactor: p.decimalScalingFactors[indexIn], + TokenRate: p.decimalScalingFactors[indexIn], + AmplificationParameter: p.amplificationParameter, + SwapFeePercentage: p.swapFeePercentage, + AggregateSwapFeePercentage: p.aggregateSwapFeePercentage, + BalancesLiveScaled18: p.balancesLiveScaled18, + }, p.OnSwap) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountOutResult{ + TokenAmountOut: &poolpkg.TokenAmount{ + Token: tokenOut, + Amount: amountOut.ToBig(), + }, + Fee: &poolpkg.TokenAmount{ + Token: tokenAmountIn.Token, + Amount: totalSwapFee.ToBig(), + }, + SwapInfo: SwapInfo{ + AggregateFee: aggregateFee.ToBig(), + }, + Gas: defaultGas.Swap, + }, nil +} + +func (p *PoolSimulator) CloneState() pool.IPoolSimulator { + cloned := *p + cloned.swapFeePercentage = p.swapFeePercentage.Clone() + cloned.aggregateSwapFeePercentage = p.aggregateSwapFeePercentage.Clone() + cloned.amplificationParameter = p.amplificationParameter.Clone() + cloned.balancesLiveScaled18 = lo.Map(p.balancesLiveScaled18, func(v *uint256.Int, _ int) *uint256.Int { + return new(uint256.Int).Set(v) + }) + cloned.decimalScalingFactors = lo.Map(p.decimalScalingFactors, func(v *uint256.Int, _ int) *uint256.Int { + return new(uint256.Int).Set(v) + }) + cloned.tokenRates = lo.Map(p.tokenRates, func(v *uint256.Int, _ int) *uint256.Int { + return new(uint256.Int).Set(v) + }) + return &cloned +} diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go new file mode 100644 index 000000000..3a3c80593 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -0,0 +1,356 @@ +package stable + +import ( + "math/big" + "testing" + + "github.com/goccy/go-json" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" +) + +func TestCalcAmountOut(t *testing.T) { + t.Run("1. should return error balance didnt converge", func(t *testing.T) { + reserves := make([]*big.Int, 3) + reserves[0], _ = new(big.Int).SetString("9999991000000000000", 10) + reserves[1], _ = new(big.Int).SetString("99999910000000000056", 10) + reserves[2], _ = new(big.Int).SetString("8897791020011100123456", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0xdac17f958d2ee523a2206206994597c13d831ec7", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0x6b175474e89094c44da98b954eedeac495271d0f", + }, + }, + }, + swapFeePercentage: uint256.NewInt(50000000000000), + aggregateSwapFeePercentage: uint256.NewInt(500000000000000000), + amplificationParameter: uint256.NewInt(5000), + tokenRates: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1), uint256.NewInt(100)}, + decimalScalingFactors: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1), uint256.NewInt(100)}, + balancesLiveScaled18: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1), uint256.NewInt(100)}, + + poolType: PoolType, + poolVersion: shared.PoolVersion1, + } + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: new(big.Int).SetUint64(99999910000000), + } + tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + assert.ErrorIs(t, err, math.ErrStableComputeBalanceDidNotConverge) + }) + + t.Run("2. should return OK", func(t *testing.T) { + // input + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("151090057727415359409", 10) + reserves[1], _ = new(big.Int).SetString("249356634133584290044", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x5F9D59db355b4A60501544637b00e94082cA575b", + "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8", + }, + }, + }, + swapFeePercentage: uint256.NewInt(30000000000000), + aggregateSwapFeePercentage: uint256.NewInt(500000000000000000), + amplificationParameter: uint256.NewInt(1000000), + tokenRates: []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, + decimalScalingFactors: []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, + balancesLiveScaled18: []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, + + poolType: PoolType, + poolVersion: shared.PoolVersion1, + } + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x5F9D59db355b4A60501544637b00e94082cA575b", + Amount: new(big.Int).SetUint64(10000000), + } + tokenOut := "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8" + + expectedAmountOut := "9999700" + expectedSwapFee := "300" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + assert.Nil(t, err) + + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) + + t.Run("3. should return OK", func(t *testing.T) { + // input + reserves := make([]*big.Int, 3) + reserves[0], _ = new(big.Int).SetString("9999991000000000013314124321", 10) + reserves[1], _ = new(big.Int).SetString("9999991000000123120010005613", 10) + reserves[2], _ = new(big.Int).SetString("1328897131447911102200123456", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0xdac17f958d2ee523a2206206994597c13d831ec7", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0x6b175474e89094c44da98b954eedeac495271d0f", + }, + }, + }, + swapFeePercentage: uint256.NewInt(53332221119995), + amplificationParameter: uint256.NewInt(1390000), + decimalScalingFactors: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1000), uint256.NewInt(100)}, + + poolType: PoolType, + poolVersion: 1, + } + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: new(big.Int).SetUint64(12111222333444555666), + } + tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" + + // expected + expected := "590000000000000000" + + // actual + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + assert.Nil(t, err) + + // assert + assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) + }) + + t.Run("4. should return OK", func(t *testing.T) { + poolStr := `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-stable", + "type": "balancer-v2-stable", + "timestamp": 1703667290, + "reserves": [ + "1152882153159026494", + "873225053252443292" + ], + "tokens": [ + { + "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + } + ], + "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }` + var pool entity.Pool + err := json.Unmarshal([]byte(poolStr), &pool) + assert.Nil(t, err) + + s, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Amount: big.NewInt(73183418984294781), + } + tokenOut := "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0" + + // expected + expected := "63551050657042642" + + // actual + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + assert.Nil(t, err) + + // assert + assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) + + }) +} + +func TestPoolSimulator_CalcAmountIn(t *testing.T) { + type fields struct { + poolStr string + } + + tests := []struct { + name string + fields fields + params poolpkg.CalcAmountInParams + want *poolpkg.CalcAmountInResult + wantErr error + }{ + { + name: "1. should return error ErrStableGetBalanceDidntConverge", + fields: fields{ + poolStr: `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-stable", + "type": "balancer-v2-stable", + "timestamp": 1703667290, + "reserves": [ + "9999991000000000000", + "99999910000000000056", + "8897791020011100123456" + ], + "tokens": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + } + ], + "extra": "{\"amp\":\"0x1388\",\"swapFeePercentage\":\"0x2D79883D2000\",\"scalingFactors\":[\"100\",\"1\",\"100\"],\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"Stable\",\"poolVersionsion\":1,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: big.NewInt(999999100000), + }, + TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + }, + want: nil, + wantErr: math.ErrStableComputeBalanceDidNotConverge, + }, + { + name: "2. should return OK", + fields: fields{ + poolStr: `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-stable", + "type": "balancer-v2-stable", + "timestamp": 1703667290, + "reserves": [ + "1152882153159026494", + "873225053252443292" + ], + "tokens": [ + { + "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + } + ], + "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + Amount: big.NewInt(63551050657042642), + }, + TokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + }, + want: &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Amount: big.NewInt(73154145616700748), + }, + }, + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var pool entity.Pool + err := json.Unmarshal([]byte(tt.fields.poolStr), &pool) + assert.Nil(t, err) + + simulator, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + got, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return simulator.CalcAmountIn(tt.params) + }) + if err != nil { + assert.ErrorIsf(t, err, tt.wantErr, "PoolSimulator.CalcAmountIn() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equalf(t, tt.want.TokenAmountIn.Token, got.TokenAmountIn.Token, "tokenIn = %v, want %v", got.TokenAmountIn.Token, tt.want.TokenAmountIn.Token) + assert.Equalf(t, tt.want.TokenAmountIn.Amount, got.TokenAmountIn.Amount, "amountIn = %v, want %v", got.TokenAmountIn.Amount.String(), tt.want.TokenAmountIn.Amount.String()) + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go new file mode 100644 index 000000000..2160f72d0 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -0,0 +1,251 @@ +package stable + +import ( + "context" + "errors" + "math/big" + "strings" + "time" + + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/ethereum/go-ethereum/ethclient/gethclient" +) + +var ErrReserveNotFound = errors.New("reserve not found") + +type PoolTracker struct { + config *Config + ethrpcClient *ethrpc.Client +} + +func NewPoolTracker( + config *Config, + ethrpcClient *ethrpc.Client, +) (*PoolTracker, error) { + return &PoolTracker{ + config: config, + ethrpcClient: ethrpcClient, + }, nil +} + +func (t *PoolTracker) GetNewPoolState( + ctx context.Context, + p entity.Pool, + params poolpkg.GetNewPoolStateParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, params, nil) +} + +func (t *PoolTracker) GetNewPoolStateWithOverrides( + ctx context.Context, + p entity.Pool, + params poolpkg.GetNewPoolStateWithOverridesParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, poolpkg.GetNewPoolStateParams{Logs: params.Logs}, params.Overrides) +} + +func (t *PoolTracker) getNewPoolState( + ctx context.Context, + p entity.Pool, + _ poolpkg.GetNewPoolStateParams, + overrides map[common.Address]gethclient.OverrideAccount, +) (entity.Pool, error) { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Info("Start updating state ...") + + defer func() { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Info("Finish updating state.") + }() + + var staticExtra StaticExtra + if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + var oldExtra Extra + if err := json.Unmarshal([]byte(p.Extra), &oldExtra); err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + scalingFactors := oldExtra.DecimalScalingFactors + + // call RPC + rpcRes, err := t.queryRPC(ctx, p.Address, staticExtra.Vault, staticExtra.PoolType, overrides) + if err != nil { + return p, err + } + + var ( + amplificationParameter, _ = uint256.FromBig(rpcRes.Amp) + swapFeePercentage, _ = uint256.FromBig(rpcRes.SwapFeePercentage) + poolTokens = rpcRes.PoolTokens + pausedState = rpcRes.PausedState + blockNumber = rpcRes.BlockNumber + ) + + // if staticExtra.PoolType == PoolTypeMetaStable { + // factors := make([]*uint256.Int, len(rpcRes.ScalingFactors)) + // for idx, factor := range rpcRes.ScalingFactors { + // factors[idx], _ = uint256.FromBig(factor) + // } + + // scalingFactors = factors + // } + + // update pool + + extra := Extra{ + AmplificationParameter: amplificationParameter, + SwapFeePercentage: swapFeePercentage, + DecimalScalingFactors: scalingFactors, + IsPaused: !isNotPaused(pausedState), + } + extraBytes, err := json.Marshal(extra) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + reserves, err := t.initReserves(ctx, p, poolTokens) + if err != nil { + return p, err + } + + p.BlockNumber = blockNumber + p.Extra = string(extraBytes) + p.Timestamp = time.Now().Unix() + p.Reserves = reserves + + return p, nil +} + +func (t *PoolTracker) initReserves( + ctx context.Context, + p entity.Pool, + poolTokens PoolTokens, +) ([]string, error) { + reserveByToken := make(map[string]*big.Int) + for idx, token := range poolTokens.Tokens { + addr := strings.ToLower(token.Hex()) + reserveByToken[addr] = poolTokens.Balances[idx] + } + + reserves := make([]string, len(p.Tokens)) + for idx, token := range p.Tokens { + r, ok := reserveByToken[token.Address] + if !ok { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error("can not get reserve") + + return nil, ErrReserveNotFound + } + + reserves[idx] = r.String() + } + + return reserves, nil +} + +func (t *PoolTracker) queryRPC( + ctx context.Context, + poolAddress string, + vault string, + poolType string, + overrides map[common.Address]gethclient.OverrideAccount, +) (*rpcRes, error) { + var ( + poolTokens PoolTokens + swapFeePercentage *big.Int + pausedState PausedState + ampParams AmplificationParameter + ) + + req := t.ethrpcClient.R(). + SetContext(ctx). + SetRequireSuccess(true) + if overrides != nil { + req.SetOverrides(overrides) + } + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultABI, + Target: vault, + Method: shared.VaultMethodGetPoolTokens, + Params: []interface{}{common.HexToHash(poolAddress)}, + }, []interface{}{&poolTokens}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: shared.PoolMethodGetAmplificationParameter, + }, []interface{}{&Params}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: shared.PoolMethodGetSwapFeePercentage, + }, []interface{}{&swapFeePercentage}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: shared.PoolMethodGetPausedState, + }, []interface{}{&pausedState}) + + res, err := req.TryBlockAndAggregate() + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": poolAddress, + }).Error(err.Error()) + + return nil, err + } + + return &rpcRes{ + Amp: ampParams.Value, + PoolTokens: poolTokens, + SwapFeePercentage: swapFeePercentage, + PausedState: pausedState, + BlockNumber: res.BlockNumber.Uint64(), + }, nil +} + +func isNotPaused(pausedState PausedState) bool { + return time.Now().Unix() > pausedState.BufferPeriodEndTime.Int64() || !pausedState.Paused +} diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go new file mode 100644 index 000000000..d46fb741c --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -0,0 +1,185 @@ +package stable + +import ( + "context" + "strings" + "time" + + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" +) + +type PoolsListUpdater struct { + config *Config + ethrpcClient *ethrpc.Client + sharedUpdater *shared.PoolsListUpdater +} + +func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsListUpdater { + sharedUpdater := shared.NewPoolsListUpdater(&shared.Config{ + DexID: config.DexID, + SubgraphAPI: config.SubgraphAPI, + SubgraphHeaders: config.SubgraphHeaders, + NewPoolLimit: config.NewPoolLimit, + }) + + return &PoolsListUpdater{ + config: config, + ethrpcClient: ethrpcClient, + sharedUpdater: sharedUpdater, + } +} + +func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte) ([]entity.Pool, []byte, error) { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Infof("Start updating pools list ...") + defer func() { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Infof("Finish updating pools list.") + }() + + subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) + if err != nil { + return nil, nil, err + } + + vaults, poolVersions, err := u.getPoolInfos(ctx, subgraphPools) + if err != nil { + return nil, nil, err + } + + pools, err := u.initPools(subgraphPools, vaults, poolVersions) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Error(err.Error()) + + return nil, nil, err + } + + return pools, newMetadataBytes, nil +} + +func (u *PoolsListUpdater) getPoolInfos(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]string, []int, error) { + var ( + vaultAddresses = make([]common.Address, len(subgraphPools)) + vaults = make([]string, len(subgraphPools)) + poolInfos = make([]string, len(subgraphPools)) + poolVersions = make([]int, len(subgraphPools)) + ) + + req := u.ethrpcClient.R().SetContext(ctx) + for idx, subgraphPool := range subgraphPools { + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: subgraphPool.Address, + Method: shared.PoolMethodGetVault, + }, []interface{}{&vaultAddresses[idx]}) + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: subgraphPool.Address, + Method: shared.PoolMethodVersion, + }, []interface{}{&poolInfos[idx]}) + } + if _, err := req.Aggregate(); err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Errorf("failed to getPoolInfos: %v", err) + return nil, nil, err + } + + for idx, addr := range vaultAddresses { + var poolInfo shared.PoolInfo + err := json.Unmarshal([]byte(poolInfos[idx]), &poolInfo) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Warnf("invalid pool version data, fallback to %v", err) + + poolInfo.Version = shared.PoolVersion1 // temporary + } + + poolVersions[idx] = poolInfo.Version + vaults[idx] = strings.ToLower(addr.Hex()) + } + + return vaults, poolVersions, nil +} + +func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool, vaults []string, poolVersions []int) ([]entity.Pool, error) { + pools := make([]entity.Pool, 0, len(subgraphPools)) + for idx := range subgraphPools { + pool, err := u.initPool(subgraphPools[idx], vaults[idx], poolVersions[idx]) + if err != nil { + return nil, err + } + + pools = append(pools, pool) + } + + return pools, nil +} + +func (u *PoolsListUpdater) initPool(subgraphPool *shared.SubgraphPool, vault string, poolVersion int) (entity.Pool, error) { + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + scalingFactors = make([]*uint256.Int, len(subgraphPool.Tokens)) + err error + ) + + for j, token := range subgraphPool.Tokens { + scalingFactors[j], err = uint256.FromHex(token.ScalingFactor) + if err != nil { + return entity.Pool{}, err + } + + poolTokens[j] = &entity.PoolToken{ + Address: token.Address, + Weight: 1, + Swappable: true, + } + + reserves[j] = "0" + } + + staticExtra := StaticExtra{ + PoolType: PoolType, + PoolVersion: poolVersion, + Vault: vault, + } + staticExtraBytes, err := json.Marshal(staticExtra) + if err != nil { + return entity.Pool{}, err + } + + extra := Extra{DecimalScalingFactors: scalingFactors} + extraBytes, err := json.Marshal(extra) + if err != nil { + return entity.Pool{}, err + } + + return entity.Pool{ + Address: subgraphPool.Address, + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + Extra: string(extraBytes), + }, nil +} diff --git a/pkg/liquidity-source/balancer-v3/stable/type.go b/pkg/liquidity-source/balancer-v3/stable/type.go new file mode 100644 index 000000000..31ce3055a --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/stable/type.go @@ -0,0 +1,68 @@ +package stable + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" +) + +type Gas struct { + Swap int64 +} + +type Extra struct { + AmplificationParameter *uint256.Int `json:"amplificationParameter"` + SwapFeePercentage *uint256.Int `json:"swapFeePercentage"` + AggregateSwapFeePercentage *uint256.Int `json:"aggregateSwapFeePercentage"` + BalancesLiveScaled18 []*uint256.Int `json:"balancesLiveScaled18"` + DecimalScalingFactors []*uint256.Int `json:"decimalScalingFactors"` + TokenRates []*uint256.Int `json:"tokenRates"` + IsPaused bool `json:"isPaused"` + IsVaultLocked bool `json:"isVaultLocked"` +} + +type StaticExtra struct { + PoolType string `json:"poolType"` + PoolVersion int `json:"poolVersion"` + Vault string `json:"vault"` +} + +type PoolTokens struct { + Tokens []common.Address + Balances []*big.Int + LastChangeBlock *big.Int +} + +type PausedState struct { + Paused bool + PauseWindowEndTime *big.Int + BufferPeriodEndTime *big.Int +} + +type AmplificationParameter struct { + Value *big.Int + IsUpdating bool + Precision *big.Int +} + +type PoolMetaInfo struct { + Vault string `json:"vault"` + PoolType string `json:"poolType"` + PoolVersion int `json:"poolVersion"` + TokenOutIndex int `json:"tokenOutIndex"` + BlockNumber uint64 `json:"blockNumber"` +} + +type rpcRes struct { + Amp *big.Int + PoolTokens PoolTokens + SwapFeePercentage *big.Int + ScalingFactors []*big.Int + PausedState PausedState + BlockNumber uint64 +} + +type SwapInfo struct { + AggregateFee *big.Int `json:"aggregateFee"` +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/abis.go b/pkg/liquidity-source/balancer-v3/weighted/abis.go new file mode 100644 index 000000000..d1c7fc2fd --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/abis.go @@ -0,0 +1,28 @@ +package weighted + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var ( + poolABI abi.ABI +) + +func init() { + builder := []struct { + ABI *abi.ABI + data []byte + }{ + {&poolABI, poolJson}, + } + + for _, b := range builder { + var err error + *b.ABI, err = abi.JSON(bytes.NewReader(b.data)) + if err != nil { + panic(err) + } + } +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json b/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json new file mode 100644 index 000000000..f2f8141e8 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json @@ -0,0 +1,1155 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "normalizedWeights", + "type": "uint256[]" + }, + { + "internalType": "contract IRateProvider[]", + "name": "rateProviders", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "assetManagers", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "internalType": "struct WeightedPool.NewPoolParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "contract IVault", + "name": "vault", + "type": "address" + }, + { + "internalType": "contract IProtocolFeePercentagesProvider", + "name": "protocolFeeProvider", + "type": "address" + }, + { + "internalType": "uint256", + "name": "pauseWindowDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodDuration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "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": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "feeType", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "protocolFeePercentage", + "type": "uint256" + } + ], + "name": "ProtocolFeePercentageCacheUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "RecoveryModeStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "SwapFeePercentageChanged", + "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" + }, + { + "inputs": [], + "name": "DELEGATE_PROTOCOL_SWAP_FEES_SENTINEL", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "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": "amount", + "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": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "disableRecoveryMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableRecoveryMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getATHRateProduct", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getActionId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getActualSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "contract IAuthorizer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getInvariant", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLastPostJoinExitInvariant", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getNextNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNormalizedWeights", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "paused", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "pauseWindowEndTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodEndTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPoolId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "feeType", + "type": "uint256" + } + ], + "name": "getProtocolFeePercentageCache", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolFeesCollector", + "outputs": [ + { + "internalType": "contract IProtocolFeesCollector", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolSwapFeeDelegation", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRateProviders", + "outputs": [ + { + "internalType": "contract IRateProvider[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getScalingFactors", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [ + { + "internalType": "contract IVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inRecoveryMode", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "onExitPool", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "onJoinPool", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum IVault.SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct IPoolSwapStructs.SwapRequest", + "name": "request", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "balanceTokenIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceTokenOut", + "type": "uint256" + } + ], + "name": "onSwap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "queryExit", + "outputs": [ + { + "internalType": "uint256", + "name": "bptIn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "amountsOut", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "lastChangeBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "queryJoin", + "outputs": [ + { + "internalType": "uint256", + "name": "bptOut", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "amountsIn", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "bytes", + "name": "poolConfig", + "type": "bytes" + } + ], + "name": "setAssetManagerPoolConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "setSwapFeePercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "updateProtocolFeePercentageCache", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/weighted/config.go b/pkg/liquidity-source/balancer-v3/weighted/config.go new file mode 100644 index 000000000..5c81d3d36 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/config.go @@ -0,0 +1,10 @@ +package weighted + +import "net/http" + +type Config struct { + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/constant.go b/pkg/liquidity-source/balancer-v3/weighted/constant.go new file mode 100644 index 000000000..cabd0adcc --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/constant.go @@ -0,0 +1,7 @@ +package weighted + +const ( + DexType = "balancer-v3-weighted" + + PoolType = "WeightedPool" +) diff --git a/pkg/liquidity-source/balancer-v3/weighted/embed.go b/pkg/liquidity-source/balancer-v3/weighted/embed.go new file mode 100644 index 000000000..17605f392 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/embed.go @@ -0,0 +1,6 @@ +package weighted + +import _ "embed" + +//go:embed abis/WeightedPool.json +var poolJson []byte diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go new file mode 100644 index 000000000..47c042445 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -0,0 +1,493 @@ +package weighted + +import ( + "errors" + "math/big" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" +) + +var ( + ErrTokenNotRegistered = errors.New("TOKEN_NOT_REGISTERED") + ErrInvalidReserve = errors.New("invalid reserve") + ErrInvalidAmountIn = errors.New("invalid amount in") + ErrInvalidSwapFeePercentage = errors.New("invalid swap fee percentage") + ErrPoolPaused = errors.New("pool is paused") + ErrMaxTotalInRatio = errors.New("MAX_TOTAL_IN_RATIO") + ErrMaxTotalOutRatio = errors.New("MAX_TOTAL_OUT_RATIO") + ErrOverflow = errors.New("OVERFLOW") +) + +var ( + defaultGas = Gas{Swap: 80000} + + _MAX_IN_RATIO = uint256.NewInt(0.3e18) + _MAX_OUT_RATIO = uint256.NewInt(0.3e18) +) + +type ( + PoolSimulator struct { + poolpkg.Pool + + paused bool + + swapFeePercentage *uint256.Int + scalingFactors []*uint256.Int + normalizedWeights []*uint256.Int + + vault string + poolID string + poolVerion int + + totalAmountsIn []*uint256.Int + scaledMaxTotalAmountsIn []*uint256.Int + + totalAmountsOut []*uint256.Int + scaledMaxTotalAmountsOut []*uint256.Int + } + + Gas struct { + Swap int64 + } +) + +func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { + var ( + extra Extra + staticExtra StaticExtra + + tokens = make([]string, len(entityPool.Tokens)) + reserves = make([]*big.Int, len(entityPool.Tokens)) + + totalAmountsIn = make([]*uint256.Int, len(entityPool.Tokens)) + scaledMaxTotalAmountsIn = make([]*uint256.Int, len(entityPool.Tokens)) + + totalAmountsOut = make([]*uint256.Int, len(entityPool.Tokens)) + scaledMaxTotalAmountsOut = make([]*uint256.Int, len(entityPool.Tokens)) + ) + + if err := json.Unmarshal([]byte(entityPool.Extra), &extra); err != nil { + return nil, err + } + + if err := json.Unmarshal([]byte(entityPool.StaticExtra), &staticExtra); err != nil { + return nil, err + } + + for idx := 0; idx < len(entityPool.Tokens); idx++ { + tokens[idx] = entityPool.Tokens[idx].Address + reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) + } + + scaledInitialBalances, err := _upscaleArray(staticExtra.PoolVersion, reserves, staticExtra.ScalingFactors) + if err != nil { + return nil, err + } + for idx := 0; idx < len(entityPool.Tokens); idx++ { + totalAmountsIn[idx] = number.Zero + totalAmountsOut[idx] = number.Zero + + maxIn, err := math.FixedPoint.MulDown(scaledInitialBalances[idx], _MAX_IN_RATIO) + if err != nil { + return nil, err + } + scaledMaxTotalAmountsIn[idx] = maxIn + + maxOut, err := math.FixedPoint.MulDown(scaledInitialBalances[idx], _MAX_OUT_RATIO) + if err != nil { + return nil, err + } + scaledMaxTotalAmountsOut[idx] = maxOut + } + + poolInfo := poolpkg.PoolInfo{ + Address: entityPool.Address, + Exchange: entityPool.Exchange, + Type: entityPool.Type, + Tokens: tokens, + Reserves: reserves, + Checked: true, + BlockNumber: uint64(entityPool.BlockNumber), + } + + return &PoolSimulator{ + Pool: poolpkg.Pool{Info: poolInfo}, + paused: extra.Paused, + swapFeePercentage: extra.SwapFeePercentage, + scalingFactors: staticExtra.ScalingFactors, + vault: staticExtra.Vault, + poolID: staticExtra.PoolID, + poolVerion: staticExtra.PoolVersion, + totalAmountsIn: totalAmountsIn, + scaledMaxTotalAmountsIn: scaledMaxTotalAmountsIn, + totalAmountsOut: totalAmountsOut, + scaledMaxTotalAmountsOut: scaledMaxTotalAmountsOut, + }, nil +} + +// https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F7#L32 +func (s *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { + if s.paused { + return nil, ErrPoolPaused + } + + tokenAmountIn := params.TokenAmountIn + tokenOut := params.TokenOut + + indexIn, indexOut := s.GetTokenIndex(tokenAmountIn.Token), s.GetTokenIndex(tokenOut) + + if indexIn == -1 || indexOut == -1 { + return nil, ErrTokenNotRegistered + } + + reserveIn, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexIn]) + if overflow { + return nil, ErrInvalidReserve + } + + reserveOut, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexOut]) + if overflow { + return nil, ErrInvalidReserve + } + + amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) + if overflow { + return nil, ErrInvalidAmountIn + } + + scalingFactorTokenIn := s.scalingFactors[indexIn] + scalingFactorTokenOut := s.scalingFactors[indexOut] + normalizedWeightIn := s.normalizedWeights[indexIn] + normalizedWeightOut := s.normalizedWeights[indexOut] + + balanceTokenIn, err := _upscale(s.poolVerion, reserveIn, scalingFactorTokenIn) + if err != nil { + return nil, err + } + balanceTokenOut, err := _upscale(s.poolVerion, reserveOut, scalingFactorTokenOut) + if err != nil { + return nil, err + } + + feeAmount, err := math.FixedPoint.MulUp(amountIn, s.swapFeePercentage) + if err != nil { + return nil, err + } + + amountInAfterFee, err := math.FixedPoint.Sub(amountIn, feeAmount) + if err != nil { + return nil, err + } + + if err := s.validateMaxInRatio(indexIn, amountInAfterFee); err != nil { + return nil, err + } + + upScaledAmountIn, err := _upscale(s.poolVerion, amountInAfterFee, scalingFactorTokenIn) + if err != nil { + return nil, err + } + + upScaledAmountOut, err := s._onSwapGivenIn( + balanceTokenIn, + normalizedWeightIn, + balanceTokenOut, + normalizedWeightOut, + upScaledAmountIn, + ) + if err != nil { + return nil, err + } + + amountOut, err := _downscaleDown(s.poolVerion, upScaledAmountOut, scalingFactorTokenOut) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountOutResult{ + TokenAmountOut: &poolpkg.TokenAmount{ + Token: tokenOut, + Amount: amountOut.ToBig(), + }, + Fee: &poolpkg.TokenAmount{ + Token: tokenAmountIn.Token, + Amount: feeAmount.ToBig(), + }, + Gas: defaultGas.Swap, + }, nil +} + +func (s *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { + if s.paused { + return nil, ErrPoolPaused + } + + tokenAmountOut := params.TokenAmountOut + tokenIn := params.TokenIn + + indexIn, indexOut := s.GetTokenIndex(tokenIn), s.GetTokenIndex(tokenAmountOut.Token) + + if indexIn == -1 || indexOut == -1 { + return nil, ErrTokenNotRegistered + } + + reserveIn, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexIn]) + if overflow { + return nil, ErrInvalidReserve + } + + reserveOut, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexOut]) + if overflow { + return nil, ErrInvalidReserve + } + + amountOut, overflow := uint256.FromBig(tokenAmountOut.Amount) + if overflow { + return nil, ErrInvalidAmountIn + } + + if err := s.validateMaxOutRatio(indexOut, amountOut); err != nil { + return nil, err + } + + scalingFactorTokenIn := s.scalingFactors[indexIn] + scalingFactorTokenOut := s.scalingFactors[indexOut] + normalizedWeightIn := s.normalizedWeights[indexIn] + normalizedWeightOut := s.normalizedWeights[indexOut] + + balanceTokenIn, err := _upscale(s.poolVerion, reserveIn, scalingFactorTokenIn) + if err != nil { + return nil, err + } + balanceTokenOut, err := _upscale(s.poolVerion, reserveOut, scalingFactorTokenOut) + if err != nil { + return nil, err + } + + upScaledAmountOut, err := _upscale(s.poolVerion, amountOut, scalingFactorTokenOut) + if err != nil { + return nil, err + } + + upScaledAmountIn, err := s._onSwapGivenOut( + balanceTokenIn, + normalizedWeightIn, + balanceTokenOut, + normalizedWeightOut, + upScaledAmountOut, + ) + if err != nil { + return nil, err + } + + amountIn, err := _downscaleUp(s.poolVerion, upScaledAmountIn, scalingFactorTokenIn) + if err != nil { + return nil, err + } + + amountInAfterFee, err := s._addSwapFeeAmount(amountIn) + if err != nil { + return nil, err + } + + feeAmount, err := math.FixedPoint.Sub(amountInAfterFee, amountIn) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: tokenIn, + Amount: amountIn.ToBig(), + }, + Fee: &poolpkg.TokenAmount{ + Token: tokenIn, + Amount: feeAmount.ToBig(), + }, + Gas: defaultGas.Swap, + }, nil +} + +// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F1#L165 +// +// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F3#L117 +func (s *PoolSimulator) _onSwapGivenIn( + balanceTokenIn *uint256.Int, + normalizedWeightIn *uint256.Int, + balanceTokenOut *uint256.Int, + normalizedWeightOut *uint256.Int, + upScaledAmountIn *uint256.Int, +) (*uint256.Int, error) { + if s.poolVerion == shared.PoolVersion1 { + return math.WeightedMath.CalcOutGivenInV1( + balanceTokenIn, + normalizedWeightIn, + balanceTokenOut, + normalizedWeightOut, + upScaledAmountIn, + ) + } + + return math.WeightedMath.CalcOutGivenIn( + balanceTokenIn, + normalizedWeightIn, + balanceTokenOut, + normalizedWeightOut, + upScaledAmountIn, + ) +} + +// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F1#L182 +// +// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F3#L132 +func (s *PoolSimulator) _onSwapGivenOut( + balanceTokenIn *uint256.Int, + normalizedWeightIn *uint256.Int, + balanceTokenOut *uint256.Int, + normalizedWeightOut *uint256.Int, + upScaledAmountOut *uint256.Int, +) (*uint256.Int, error) { + if s.poolVerion == shared.PoolVersion1 { + return math.WeightedMath.CalcInGivenOutV1( + balanceTokenIn, + normalizedWeightIn, + balanceTokenOut, + normalizedWeightOut, + upScaledAmountOut, + ) + } + + return math.WeightedMath.CalcInGivenOut( + balanceTokenIn, + normalizedWeightIn, + balanceTokenOut, + normalizedWeightOut, + upScaledAmountOut, + ) +} + +// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F28#L454 +// +// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F14#L619 +func (s *PoolSimulator) _addSwapFeeAmount(amount *uint256.Int) (*uint256.Int, error) { + // This returns amount + fee amount, so we round up (favoring a higher fee amount). + return math.FixedPoint.DivUp(amount, math.FixedPoint.Complement(s.swapFeePercentage)) +} + +func (s *PoolSimulator) validateMaxInRatio(tokenIndex int, amountIn *uint256.Int) error { + sum := new(uint256.Int).Add(s.totalAmountsIn[tokenIndex], amountIn) + upscaledSum, err := _upscale(s.poolVerion, sum, s.scalingFactors[tokenIndex]) + if err != nil { + return err + } + + if upscaledSum.Gt(s.scaledMaxTotalAmountsIn[tokenIndex]) { + return ErrMaxTotalInRatio + } + + return nil +} + +func (s *PoolSimulator) validateMaxOutRatio(tokenIndex int, amountOut *uint256.Int) error { + sum := new(uint256.Int).Add(s.totalAmountsOut[tokenIndex], amountOut) + upscaledSum, err := _upscale(s.poolVerion, sum, s.scalingFactors[tokenIndex]) + if err != nil { + return err + } + + if upscaledSum.Gt(s.scaledMaxTotalAmountsOut[tokenIndex]) { + return ErrMaxTotalOutRatio + } + + return nil +} + +func (s *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { + for idx, token := range s.Info.Tokens { + if token == params.TokenAmountIn.Token { + s.Info.Reserves[idx] = new(big.Int).Add( + s.Info.Reserves[idx], + params.TokenAmountIn.Amount, + ) + + s.totalAmountsIn[idx] = new(uint256.Int).Add( + s.totalAmountsIn[idx], + uint256.MustFromBig(params.TokenAmountIn.Amount), + ) + } + + if token == params.TokenAmountOut.Token { + s.Info.Reserves[idx] = new(big.Int).Sub( + s.Info.Reserves[idx], + params.TokenAmountOut.Amount, + ) + } + } +} + +func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { + return PoolMetaInfo{ + Vault: s.vault, + PoolID: s.poolID, + TokenOutIndex: s.GetTokenIndex(tokenOut), + BlockNumber: s.Info.BlockNumber, + } +} + +// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F27#L529 +// +// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F13#L681 +func _upscale(poolVerion int, amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { + if poolVerion == shared.PoolVersion1 { + return math.Math.Mul(amount, scalingFactor) + } + + return math.FixedPoint.MulDown(amount, scalingFactor) +} + +// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F28#L547 +// +// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F14#L706 +func _downscaleDown(poolVerion int, amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { + if poolVerion == shared.PoolVersion1 { + return math.Math.DivDown(amount, scalingFactor) + } + + return math.FixedPoint.DivDown(amount, scalingFactor) +} + +// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F28#L565 +// +// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F14#L727 +func _downscaleUp(poolVerion int, amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { + if poolVerion == shared.PoolVersion1 { + return math.Math.DivUp(amount, scalingFactor) + } + + return math.FixedPoint.DivUp(amount, scalingFactor) +} + +func _upscaleArray(poolVerion int, balances []*big.Int, scalingFactors []*uint256.Int) ([]*uint256.Int, error) { + upscaled := make([]*uint256.Int, len(balances)) + for i, balance := range balances { + b, overflow := uint256.FromBig(balance) + if overflow { + return nil, ErrOverflow + } + + upscaledI, err := _upscale(poolVerion, b, scalingFactors[i]) + if err != nil { + return nil, err + } + upscaled[i] = upscaledI + } + return upscaled, nil +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go new file mode 100644 index 000000000..9f781f19d --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go @@ -0,0 +1,767 @@ +package weighted + +import ( + "math/big" + "testing" + + "github.com/goccy/go-json" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" +) + +func Test_CalcAmountOut(t *testing.T) { + t.Run("1. should return OK", func(t *testing.T) { + // input + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Tokens: []string{ + "0xac3E018457B222d93114458476f3E3416Abbe38F", + "0xae78736Cd615f374D3085123A210448E74Fc6393", + "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", + }, + Reserves: []*big.Int{ + big.NewInt(331125), + big.NewInt(320633), + big.NewInt(348846), + }, + }, + }, + + swapFeePercentage: uint256.NewInt(3000000000000000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + }, + normalizedWeights: []*uint256.Int{ + uint256.NewInt(333300000000000000), + uint256.NewInt(333300000000000000), + uint256.NewInt(333400000000000000), + }, + totalAmountsIn: []*uint256.Int{uint256.NewInt(0), uint256.NewInt(0), uint256.NewInt(0)}, + scaledMaxTotalAmountsIn: []*uint256.Int{ + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + }, + poolVerion: 3, + } + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xac3E018457B222d93114458476f3E3416Abbe38F", + Amount: big.NewInt(3311), + } + tokenOut := "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" + + // expected + amountOut := "3442" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, amountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("2. should return OK", func(t *testing.T) { + // input + reserve0, _ := new(big.Int).SetString("3360160080014532471350474", 10) + reserve1, _ := new(big.Int).SetString("1112301324508754708737", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Tokens: []string{ + "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a", + "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + }, + }, + }, + + swapFeePercentage: uint256.NewInt(1000000000000000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + }, + normalizedWeights: []*uint256.Int{ + uint256.NewInt(800000000000000000), + uint256.NewInt(200000000000000000), + }, + totalAmountsIn: []*uint256.Int{uint256.NewInt(0), uint256.NewInt(0)}, + scaledMaxTotalAmountsIn: []*uint256.Int{ + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + }, + poolVerion: 2, + } + + amountIn, _ := new(big.Int).SetString("60160080014532471350474", 10) + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a", + Amount: amountIn, + } + tokenOut := "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223" + + // expected + amountOut := "76143667376405160244" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, amountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("3. should return OK", func(t *testing.T) { + // input + reserve0, _ := new(big.Int).SetString("3360160080014532471350474", 10) + reserve1, _ := new(big.Int).SetString("1112301324508754708737", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Tokens: []string{ + "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a", + "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223", + }, + Reserves: []*big.Int{ + reserve0, + reserve1, + }, + }, + }, + + swapFeePercentage: uint256.NewInt(1000000000000000), + scalingFactors: []*uint256.Int{ + uint256.NewInt(1000000000000000000), + uint256.NewInt(1000000000000000000), + }, + normalizedWeights: []*uint256.Int{ + uint256.NewInt(800000000000000000), + uint256.NewInt(200000000000000000), + }, + totalAmountsIn: []*uint256.Int{uint256.NewInt(0), uint256.NewInt(0)}, + scaledMaxTotalAmountsIn: []*uint256.Int{ + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), + }, + poolVerion: 4, + } + + amountIn, _ := new(big.Int).SetString("6016008001453247", 10) + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223", + Amount: amountIn, + } + tokenOut := "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a" + + // expected + amountOut := "4538893010907736440" + + // calculation + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, amountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("4. should return OK", func(t *testing.T) { + // input + // block 18783187 + p := `{ + "address": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", + "reserveUsd": 153314467.24136648, + "amplifiedTvl": 153314467.24136648, + "exchange": "balancer-v2-weighted", + "type": "balancer-v2-weighted", + "timestamp": 1702542461, + "reserves": [ + "31686717298564222587034828", + "14236767788701850247952" + ], + "tokens": [ + { + "address": "0xba100000625a3754423978a60c9317c58a424e3d", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + } + ], + "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0xb1a2bc2ec500000\",\"0x2c68af0bb140000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" +}` + var pool entity.Pool + err := json.Unmarshal([]byte(p), &pool) + assert.Nil(t, err) + + // expected + expectedAmountOut := "1014934149732776116160723" + + // calculation + simulator, err := NewPoolSimulator(pool) + assert.Nil(t, err) + amountIn, _ := new(big.Int).SetString("2000000000000000000000", 10) + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return simulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: poolpkg.TokenAmount{ + Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Amount: amountIn, + }, + TokenOut: "0xba100000625a3754423978a60c9317c58a424e3d", + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) + + t.Run("5. should return OK", func(t *testing.T) { + // input + // polygon, block 51339771 + p := `{ + "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", + "reserveUsd": 3454.483888331181, + "amplifiedTvl": 3454.483888331181, + "exchange": "balancer-v2-weighted", + "type": "balancer-v2-weighted", + "timestamp": 1703033832, + "reserves": [ + "382259350067562080018", + "563895201975090444069", + "432276836", + "415858931425966091248020", + "198780894165507591", + "9187067339281421763", + "111172932376992452571", + "1835599921140802978251" + ], + "tokens": [ + { + "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + } + ], + "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }` + var pool entity.Pool + err := json.Unmarshal([]byte(p), &pool) + assert.Nil(t, err) + + // expected + expectedAmountOut := "49523009318781117474536" + + // calculation + simulator, err := NewPoolSimulator(pool) + assert.Nil(t, err) + amountIn, _ := new(big.Int).SetString("77000000000000000000", 10) + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return simulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: poolpkg.TokenAmount{ + Token: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + Amount: amountIn, + }, + TokenOut: "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + }) + }) + + // assert + assert.Nil(t, err) + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + }) +} + +func TestPoolSimulator_CalcAmountIn(t *testing.T) { + amountOutTest1, _ := new(big.Int).SetString("1014934149732776116160723", 10) + expectedAmountInTest1, _ := new(big.Int).SetString("1979999999999513367997", 10) + + amountOutTest2, _ := new(big.Int).SetString("49523009318781117474536", 10) + expectedAmountInTest2, _ := new(big.Int).SetString("76229999999999997187", 10) + + type fields struct { + p string + } + tests := []struct { + name string + fields fields + params poolpkg.CalcAmountInParams + want *poolpkg.CalcAmountInResult + wantErr error + }{ + { + name: "1. should return OK", + fields: fields{ + p: `{ + "address": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", + "reserveUsd": 153314467.24136648, + "amplifiedTvl": 153314467.24136648, + "exchange": "balancer-v2-weighted", + "type": "balancer-v2-weighted", + "timestamp": 1702542461, + "reserves": [ + "31686717298564222587034828", + "14236767788701850247952" + ], + "tokens": [ + { + "address": "0xba100000625a3754423978a60c9317c58a424e3d", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + } + ], + "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0xb1a2bc2ec500000\",\"0x2c68af0bb140000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0xba100000625a3754423978a60c9317c58a424e3d", + Amount: amountOutTest1, + }, + TokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + }, + want: &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Amount: expectedAmountInTest1, + }, + }, + wantErr: nil, + }, + { + name: "2. should return OK", + fields: fields{ + p: `{ + "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", + "reserveUsd": 3454.483888331181, + "amplifiedTvl": 3454.483888331181, + "exchange": "balancer-v2-weighted", + "type": "balancer-v2-weighted", + "timestamp": 1703033832, + "reserves": [ + "382259350067562080018", + "563895201975090444069", + "432276836", + "415858931425966091248020", + "198780894165507591", + "9187067339281421763", + "111172932376992452571", + "1835599921140802978251" + ], + "tokens": [ + { + "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + } + ], + "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + Amount: amountOutTest2, + }, + TokenIn: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + }, + want: &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + Amount: expectedAmountInTest2, + }, + }, + wantErr: nil, + }, + { + name: "3. should return error ErrPoolPaused", + fields: fields{ + p: `{ + "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", + "reserveUsd": 3454.483888331181, + "amplifiedTvl": 3454.483888331181, + "exchange": "balancer-v2-weighted", + "type": "balancer-v2-weighted", + "timestamp": 1703033832, + "reserves": [ + "382259350067562080018", + "563895201975090444069", + "432276836", + "415858931425966091248020", + "198780894165507591", + "9187067339281421763", + "111172932376992452571", + "1835599921140802978251" + ], + "tokens": [ + { + "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + } + ], + "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":true}", + "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0x580a84c73811e1839f75d86d75d88cca0c241ff5", // not registered token, last character should be 4 + Amount: amountOutTest2, + }, + TokenIn: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + }, + want: nil, + wantErr: ErrPoolPaused, + }, + { + name: "4. should return error ErrTokenNotRegistered", + fields: fields{ + p: `{ + "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", + "reserveUsd": 3454.483888331181, + "amplifiedTvl": 3454.483888331181, + "exchange": "balancer-v2-weighted", + "type": "balancer-v2-weighted", + "timestamp": 1703033832, + "reserves": [ + "382259350067562080018", + "563895201975090444069", + "432276836", + "415858931425966091248020", + "198780894165507591", + "9187067339281421763", + "111172932376992452571", + "1835599921140802978251" + ], + "tokens": [ + { + "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + }, + { + "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 0, + "swappable": true + } + ], + "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, + }, + params: poolpkg.CalcAmountInParams{ + TokenAmountOut: poolpkg.TokenAmount{ + Token: "0x580a84c73811e1839f75d86d75d88cca0c241ff5", // not registered token, last character should be 4 + Amount: amountOutTest2, + }, + TokenIn: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", + }, + want: nil, + wantErr: ErrTokenNotRegistered, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var pool entity.Pool + err := json.Unmarshal([]byte(tt.fields.p), &pool) + assert.Nil(t, err) + + simulator, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + got, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return simulator.CalcAmountIn(tt.params) + }) + if err != nil { + assert.ErrorIsf(t, err, tt.wantErr, "PoolSimulator.CalcAmountIn() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equalf(t, tt.want.TokenAmountIn.Token, got.TokenAmountIn.Token, "tokenIn = %v, want %v", got.TokenAmountIn.Token, tt.want.TokenAmountIn.Token) + assert.Equalf(t, tt.want.TokenAmountIn.Amount, got.TokenAmountIn.Amount, "amountIn = %v, want %v", got.TokenAmountIn.Amount.String(), tt.want.TokenAmountIn.Amount.String()) + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go new file mode 100644 index 000000000..33c3fb06c --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go @@ -0,0 +1,199 @@ +package weighted + +import ( + "context" + "errors" + "math/big" + "strings" + "time" + + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" +) + +var ErrReserveNotFound = errors.New("reserve not found") + +type PoolTracker struct { + config *Config + ethrpcClient *ethrpc.Client +} + +func NewPoolTracker( + config *Config, + ethrpcClient *ethrpc.Client, +) (*PoolTracker, error) { + return &PoolTracker{ + config: config, + ethrpcClient: ethrpcClient, + }, nil +} + +func (t *PoolTracker) GetNewPoolState( + ctx context.Context, + p entity.Pool, + _ poolpkg.GetNewPoolStateParams, +) (entity.Pool, error) { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Info("Start updating state ...") + + defer func() { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Info("Finish updating state.") + }() + + var staticExtra StaticExtra + if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + // call RPC + rpcRes, err := t.queryRPC(ctx, p.Address, staticExtra.PoolID, staticExtra.Vault) + if err != nil { + return p, err + } + + var ( + poolTokens = rpcRes.PoolTokens + swapFeePercentage = rpcRes.SwapFeePercentage + pausedState = rpcRes.PausedState + blockNumber = rpcRes.BlockNumber + ) + + // update pool + + extra := Extra{ + SwapFeePercentage: swapFeePercentage, + Paused: !isNotPaused(pausedState), + } + extraBytes, err := json.Marshal(extra) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error(err.Error()) + + return p, err + } + + reserves, err := t.initReserves(ctx, p, poolTokens) + if err != nil { + return p, err + } + + p.BlockNumber = blockNumber + p.Extra = string(extraBytes) + p.Timestamp = time.Now().Unix() + p.Reserves = reserves + + return p, nil +} + +func (t *PoolTracker) initReserves( + ctx context.Context, + p entity.Pool, + poolTokens PoolTokens, +) ([]string, error) { + reserveByToken := make(map[string]*big.Int) + for idx, token := range poolTokens.Tokens { + addr := strings.ToLower(token.Hex()) + reserveByToken[addr] = poolTokens.Balances[idx] + } + + reserves := make([]string, len(p.Tokens)) + for idx, token := range p.Tokens { + r, ok := reserveByToken[token.Address] + if !ok { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": p.Address, + }).Error("can not get reserve") + + return nil, ErrReserveNotFound + } + + reserves[idx] = r.String() + } + + return reserves, nil +} + +func (t *PoolTracker) queryRPC( + ctx context.Context, + poolAddress string, + poolID string, + vault string, +) (*rpcRes, error) { + var ( + poolTokens PoolTokens + swapFeePercentage *big.Int + pausedState PausedState + ) + + req := t.ethrpcClient.R(). + SetContext(ctx). + SetRequireSuccess(true) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultABI, + Target: vault, + Method: shared.VaultMethodGetPoolTokens, + Params: []interface{}{common.HexToHash(poolID)}, + }, []interface{}{&poolTokens}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: shared.PoolMethodGetSwapFeePercentage, + }, []interface{}{&swapFeePercentage}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: shared.PoolMethodGetPausedState, + }, []interface{}{&pausedState}) + + res, err := req.TryBlockAndAggregate() + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": poolAddress, + }).Error(err.Error()) + + return nil, err + } + + swapFeePercentageU256, _ := uint256.FromBig(swapFeePercentage) + + return &rpcRes{ + PoolTokens: poolTokens, + SwapFeePercentage: swapFeePercentageU256, + PausedState: pausedState, + BlockNumber: res.BlockNumber.Uint64(), + }, nil +} + +func isNotPaused(pausedState PausedState) bool { + return time.Now().Unix() > pausedState.BufferPeriodEndTime.Int64() || !pausedState.Paused +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go new file mode 100644 index 000000000..3bd697a05 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go @@ -0,0 +1,193 @@ +package weighted + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" +) + +var ErrInvalidWeight = errors.New("invalid weight") + +type PoolsListUpdater struct { + config Config + ethrpcClient *ethrpc.Client + sharedUpdater *shared.PoolsListUpdater +} + +func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsListUpdater { + sharedUpdater := shared.NewPoolsListUpdater(&shared.Config{ + DexID: config.DexID, + SubgraphAPI: config.SubgraphAPI, + SubgraphHeaders: config.SubgraphHeaders, + NewPoolLimit: config.NewPoolLimit, + }) + + return &PoolsListUpdater{ + config: *config, + ethrpcClient: ethrpcClient, + sharedUpdater: sharedUpdater, + } +} + +func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte) ([]entity.Pool, []byte, error) { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Infof("Start updating pools list ...") + defer func() { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Infof("Finish updating pools list.") + }() + + subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) + if err != nil { + return nil, nil, err + } + + vaults, poolVerions, err := u.getPoolInfos(ctx, subgraphPools) + if err != nil { + return nil, nil, err + } + + pools, err := u.initPools(subgraphPools, vaults, poolVerions) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Error(err.Error()) + + return nil, nil, err + } + + return pools, newMetadataBytes, nil +} + +func (u *PoolsListUpdater) getPoolInfos(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]string, []int, error) { + var ( + vaultAddresses = make([]common.Address, len(subgraphPools)) + vaults = make([]string, len(subgraphPools)) + poolInfos = make([]string, len(subgraphPools)) + poolVersions = make([]int, len(subgraphPools)) + ) + + req := u.ethrpcClient.R().SetContext(ctx) + for idx, subgraphPool := range subgraphPools { + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: subgraphPool.Address, + Method: shared.PoolMethodGetVault, + }, []interface{}{&vaultAddresses[idx]}) + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: subgraphPool.Address, + Method: shared.PoolMethodVersion, + }, []interface{}{&poolInfos[idx]}) + } + if _, err := req.Aggregate(); err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Errorf("failed to getPoolInfos: %v", err) + return nil, nil, err + } + + for idx, addr := range vaultAddresses { + var poolInfo shared.PoolInfo + err := json.Unmarshal([]byte(poolInfos[idx]), &poolInfo) + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Warnf("invalid pool version data, fallback to %v", err) + + poolInfo.Version = shared.PoolVersion1 // temporary + } + + poolVersions[idx] = poolInfo.Version + vaults[idx] = strings.ToLower(addr.Hex()) + } + + return vaults, poolVersions, nil +} + +func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool, vaults []string, poolVersions []int) ([]entity.Pool, error) { + pools := make([]entity.Pool, 0, len(subgraphPools)) + + for idx := range subgraphPools { + pool, err := u.initPool(subgraphPools[idx], vaults[idx], poolVersions[idx]) + if err != nil { + return nil, err + } + + pools = append(pools, pool) + } + + for idx := range subgraphPools { + pool, err := u.initPool(subgraphPools[idx], vaults[idx], poolVersions[idx]) + if err != nil { + return nil, err + } + + pools = append(pools, pool) + } + + return pools, nil +} + +func (u *PoolsListUpdater) initPool(subgraphPool *shared.SubgraphPool, vault string, poolVersion int) (entity.Pool, error) { + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + scalingFactors = make([]*uint256.Int, len(subgraphPool.Tokens)) + err error + ) + + for j, token := range subgraphPool.Tokens { + scalingFactors[j], err = uint256.FromHex(token.ScalingFactor) + if err != nil { + return entity.Pool{}, err + } + + poolTokens[j] = &entity.PoolToken{ + Address: token.Address, + Decimals: uint8(token.Decimals), + Weight: 1, + Swappable: true, + } + + reserves[j] = "0" + } + + staticExtra := StaticExtra{ + PoolID: subgraphPool.ID, + PoolType: PoolType, + PoolVersion: poolVersion, + Vault: vault, + } + staticExtraBytes, err := json.Marshal(staticExtra) + if err != nil { + return entity.Pool{}, err + } + + return entity.Pool{ + Address: strings.ToLower(subgraphPool.Address), + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + }, nil +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/type.go b/pkg/liquidity-source/balancer-v3/weighted/type.go new file mode 100644 index 000000000..571ab0c1c --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/type.go @@ -0,0 +1,47 @@ +package weighted + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" +) + +type Extra struct { + SwapFeePercentage *uint256.Int `json:"swapFeePercentage"` + Paused bool `json:"paused"` +} + +type StaticExtra struct { + PoolID string `json:"poolId"` + PoolType string `json:"poolType"` + PoolVersion int `json:"poolVersion"` + ScalingFactors []*uint256.Int `json:"scalingFactors"` + Vault string `json:"vault"` +} + +type PoolTokens struct { + Tokens []common.Address + Balances []*big.Int + LastChangeBlock *big.Int +} + +type PausedState struct { + Paused bool + PauseWindowEndTime *big.Int + BufferPeriodEndTime *big.Int +} + +type PoolMetaInfo struct { + Vault string `json:"vault"` + PoolID string `json:"poolId"` + TokenOutIndex int `json:"tokenOutIndex"` + BlockNumber uint64 `json:"blockNumber"` +} + +type rpcRes struct { + PoolTokens PoolTokens + SwapFeePercentage *uint256.Int + PausedState PausedState + BlockNumber uint64 +} From 3359ec7438ae47687f1ebf2df46272d669203101 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 30 Dec 2024 00:30:42 +0700 Subject: [PATCH 04/39] tmp --- .../balancer-v3/shared/vault.go | 24 ++++++++++++------- .../balancer-v3/stable/pool_simulator.go | 8 +++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/shared/vault.go b/pkg/liquidity-source/balancer-v3/shared/vault.go index da5c000fd..ea0e99e4b 100644 --- a/pkg/liquidity-source/balancer-v3/shared/vault.go +++ b/pkg/liquidity-source/balancer-v3/shared/vault.go @@ -5,11 +5,19 @@ import ( "github.com/holiman/uint256" ) -func Swap( +type vault struct{} + +var Vault *vault + +func init() { + Vault = &vault{} +} + +func (v *vault) Swap( param VaultSwapParams, onSwap func(_ bool, _, _ int, _ *uint256.Int) (*uint256.Int, error), ) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - amountGivenScaled18, err := ComputeAmountGivenScaled18(true, param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) + amountGivenScaled18, err := v.ComputeAmountGivenScaled18(true, param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) if err != nil { return nil, nil, nil, err } @@ -37,12 +45,12 @@ func Swap( return nil, nil, nil, ErrTradeAmountTooSmall } - amountCalculated, err := ComputeAmountCalculatedRaw(true, amountGivenScaled18, param.SwapFeePercentage, param.DecimalScalingFactor, param.TokenRate) + amountCalculated, err := v.ComputeAmountCalculatedRaw(true, amountGivenScaled18, param.SwapFeePercentage, param.DecimalScalingFactor, param.TokenRate) if err != nil { return nil, nil, nil, err } - totalSwapFee, aggregateFee, err := ComputeAggregateSwapFees(true, swapFeeScaled18, param.AggregateSwapFeePercentage, + totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(true, swapFeeScaled18, param.AggregateSwapFeePercentage, param.DecimalScalingFactor, param.TokenRate) if err != nil { return nil, nil, nil, err @@ -51,7 +59,7 @@ func Swap( return amountCalculated, totalSwapFee, aggregateFee, nil } -func ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { +func (v *vault) ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { if isExactIn { return toScaled18ApplyRateRoundDown(amountGiven, decimalScalingFactor, tokenRate) } @@ -59,7 +67,7 @@ func ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFacto return toScaled18ApplyRateRoundUp(amountGiven, decimalScalingFactor, computeRateRoundUp(tokenRate)) } -func ComputeAmountCalculatedRaw( +func (v *vault) ComputeAmountCalculatedRaw( isExactIn bool, amountCalculatedScaled18, swapFeePercentage, decimalScalingFactor, tokenRate *uint256.Int, @@ -81,7 +89,7 @@ func ComputeAmountCalculatedRaw( return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, tokenRate) } -func ComputeAggregateSwapFees( +func (v *vault) ComputeAggregateSwapFees( isExactIn bool, totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, decimalScalingFactor, tokenRate *uint256.Int, @@ -108,7 +116,7 @@ func ComputeAggregateSwapFees( return totalSwapFeeAmountRaw, aggregateFeeAmountRaw, nil } -func UpdateLiveBalance( +func (v *vault) UpdateLiveBalance( param VaultSwapParams, rounding Rounding, ) (*uint256.Int, error) { diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index f4de77088..392dc227e 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -117,7 +117,7 @@ func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk return nil, ErrInvalidAmountOut } - amountIn, totalSwapFee, aggregateSwapFee, err := shared.Swap(shared.VaultSwapParams{ + amountIn, totalSwapFee, aggregateSwapFee, err := shared.Vault.Swap(shared.VaultSwapParams{ IsExactIn: false, IndexIn: indexIn, IndexOut: indexOut, @@ -179,7 +179,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { TokenRate: p.tokenRates[tokenIndexIn], } - updatedLiveBalanceIn, err := shared.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + updatedLiveBalanceIn, err := shared.Vault.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return @@ -194,7 +194,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { vaultParams.DecimalScalingFactor = p.decimalScalingFactors[tokenIndexOut] vaultParams.TokenRate = p.tokenRates[tokenIndexOut] - updatedLiveBalanceOut, err := shared.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + updatedLiveBalanceOut, err := shared.Vault.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return @@ -283,7 +283,7 @@ func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool return nil, ErrInvalidAmountIn } - amountOut, totalSwapFee, aggregateFee, err := shared.Swap(shared.VaultSwapParams{ + amountOut, totalSwapFee, aggregateFee, err := shared.Vault.Swap(shared.VaultSwapParams{ IsExactIn: true, IndexIn: indexIn, IndexOut: indexOut, From 4bd8d989f1ffbadcc015d40d225339cdedc279ef Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 30 Dec 2024 00:32:50 +0700 Subject: [PATCH 05/39] tmp --- .../balancer-v3/shared/abis/Vault.json | 3042 +++++++++++++---- 1 file changed, 2355 insertions(+), 687 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json index ccaba5181..215a232a0 100644 --- a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json +++ b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json @@ -2,355 +2,2028 @@ { "inputs": [ { - "internalType": "contract IAuthorizer", - "name": "authorizer", + "internalType": "contract IVault", + "name": "mainVault", "type": "address" }, { - "internalType": "contract IWETH", - "name": "weth", + "internalType": "contract IVaultAdmin", + "name": "vaultAdmin", "type": "address" - }, - { - "internalType": "uint256", - "name": "pauseWindowDuration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "bufferPeriodDuration", - "type": "uint256" } ], "stateMutability": "nonpayable", "type": "constructor" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "contract IAuthorizer", - "name": "newAuthorizer", + "internalType": "address", + "name": "target", "type": "address" } ], - "name": "AuthorizerChanged", - "type": "event" + "name": "AddressEmptyCode", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "contract IERC20", - "name": "token", + "internalType": "address", + "name": "account", "type": "address" - }, + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "AfterAddLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AfterInitializeHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AfterRemoveLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AfterSwapHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AmountGivenZero", + "type": "error" + }, + { + "inputs": [ { - "indexed": true, - "internalType": "address", - "name": "sender", + "internalType": "contract IERC20", + "name": "tokenIn", "type": "address" }, { - "indexed": false, - "internalType": "address", - "name": "recipient", - "type": "address" + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" }, { - "indexed": false, "internalType": "uint256", - "name": "amount", + "name": "maxAmountIn", "type": "uint256" } ], - "name": "ExternalBalanceTransfer", - "type": "event" + "name": "AmountInAboveMax", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "contract IFlashLoanRecipient", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, "internalType": "contract IERC20", - "name": "token", + "name": "tokenOut", "type": "address" }, { - "indexed": false, "internalType": "uint256", - "name": "amount", + "name": "amountOut", "type": "uint256" }, { - "indexed": false, "internalType": "uint256", - "name": "feeAmount", + "name": "minAmountOut", "type": "uint256" } ], - "name": "FlashLoan", - "type": "event" + "name": "AmountOutBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceNotSettled", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeAddLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeInitializeHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeRemoveLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeSwapHookFailed", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" }, { - "indexed": true, - "internalType": "contract IERC20", - "name": "token", - "type": "address" + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + } + ], + "name": "BptAmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" }, { - "indexed": false, - "internalType": "int256", - "name": "delta", - "type": "int256" + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" } ], - "name": "InternalBalanceChanged", - "type": "event" + "name": "BptAmountOutBelowMin", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" } ], - "name": "PausedStateChanged", - "type": "event" + "name": "BufferAlreadyInitialized", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "BufferNotInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "BufferSharesInvalidOwner", + "type": "error" + }, + { + "inputs": [], + "name": "BufferSharesInvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + } + ], + "name": "BufferTotalSupplyTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "CannotReceiveEth", + "type": "error" + }, + { + "inputs": [], + "name": "CannotSwapSameToken", + "type": "error" + }, + { + "inputs": [], + "name": "CodecOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportAddLiquidityCustom", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportDonation", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportRemoveLiquidityCustom", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportUnbalancedLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "DynamicSwapFeeHookFailed", + "type": "error" + }, + { + "inputs": [ { - "indexed": true, "internalType": "address", - "name": "liquidityProvider", + "name": "spender", "type": "address" }, { - "indexed": false, - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "indexed": false, - "internalType": "int256[]", - "name": "deltas", - "type": "int256[]" + "internalType": "uint256", + "name": "allowance", + "type": "uint256" }, { - "indexed": false, - "internalType": "uint256[]", - "name": "protocolFeeAmounts", - "type": "uint256[]" + "internalType": "uint256", + "name": "needed", + "type": "uint256" } ], - "name": "PoolBalanceChanged", - "type": "event" + "name": "ERC20InsufficientAllowance", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "indexed": true, "internalType": "address", - "name": "assetManager", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "token", + "name": "sender", "type": "address" }, { - "indexed": false, - "internalType": "int256", - "name": "cashDelta", - "type": "int256" + "internalType": "uint256", + "name": "balance", + "type": "uint256" }, { - "indexed": false, - "internalType": "int256", - "name": "managedDelta", - "type": "int256" + "internalType": "uint256", + "name": "needed", + "type": "uint256" } ], - "name": "PoolBalanceManaged", - "type": "event" + "name": "ERC20InsufficientBalance", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "indexed": true, "internalType": "address", - "name": "poolAddress", + "name": "approver", "type": "address" - }, - { - "indexed": false, - "internalType": "enum IVault.PoolSpecialization", - "name": "specialization", - "type": "uint8" } ], - "name": "PoolRegistered", - "type": "event" + "name": "ERC20InvalidApprover", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, "internalType": "address", - "name": "relayer", + "name": "receiver", "type": "address" - }, + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ { - "indexed": true, "internalType": "address", "name": "sender", "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "approved", - "type": "bool" } ], - "name": "RelayerApprovalChanged", - "type": "event" + "name": "ERC20InvalidSender", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenIn", + "internalType": "address", + "name": "spender", "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenOut", + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "ErrorSelectorNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "FeePrecisionTooHigh", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + } + ], + "name": "HookAdjustedAmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + } + ], + "name": "HookAdjustedAmountOutBelowMin", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "HookAdjustedSwapLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolHooksContract", + "type": "address" + }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "poolFactory", + "type": "address" + } + ], + "name": "HookRegistrationFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRemoveLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidToken", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenConfiguration", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenDecimals", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "InvalidUnderlyingToken", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "issuedShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minIssuedShares", + "type": "uint256" + } + ], + "name": "IssuedSharesBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "MaxTokens", + "type": "error" + }, + { + "inputs": [], + "name": "MinTokens", + "type": "error" + }, + { + "inputs": [], + "name": "NotEnoughBufferShares", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "expectedUnderlyingAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualUnderlyingAmount", + "type": "uint256" + } + ], + "name": "NotEnoughUnderlying", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "expectedWrappedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualWrappedAmount", + "type": "uint256" + } + ], + "name": "NotEnoughWrapped", + "type": "error" + }, + { + "inputs": [], + "name": "NotStaticCall", + "type": "error" + }, + { + "inputs": [], + "name": "NotVaultDelegateCall", + "type": "error" + }, + { + "inputs": [], + "name": "OutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "PauseBufferPeriodDurationTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "PercentageAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPauseWindowExpired", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + } + ], + "name": "PoolTotalSupplyTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolFeesExceedTotalCollected", + "type": "error" + }, + { + "inputs": [], + "name": "QueriesDisabled", + "type": "error" + }, + { + "inputs": [], + "name": "QueriesDisabledPermanently", + "type": "error" + }, + { + "inputs": [], + "name": "QuoteResultSpoofed", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "name": "Result", + "type": "error" + }, + { + "inputs": [], + "name": "RouterNotTrusted", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintToInt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderIsNotVault", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFeePercentageTooLow", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "SwapLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "TokenAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "TokenNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "expectedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "actualToken", + "type": "address" + } + ], + "name": "TokensMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "TokensNotSorted", + "type": "error" + }, + { + "inputs": [], + "name": "TradeAmountTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "VaultBuffersArePaused", + "type": "error" + }, + { + "inputs": [], + "name": "VaultIsNotUnlocked", + "type": "error" + }, + { + "inputs": [], + "name": "VaultNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowDurationTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowExpired", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "WrapAmountTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "WrongProtocolFeeControllerDeployment", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "underlyingToken", + "type": "address" + } + ], + "name": "WrongUnderlyingToken", + "type": "error" + }, + { + "inputs": [], + "name": "WrongVaultAdminDeployment", + "type": "error" + }, + { + "inputs": [], + "name": "WrongVaultExtensionDeployment", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + } + ], + "name": "AggregateSwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + } + ], + "name": "AggregateYieldFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": true, + "internalType": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "AuthorizerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "burnedShares", + "type": "uint256" + } + ], + "name": "BufferSharesBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "issuedShares", + "type": "uint256" + } + ], + "name": "BufferSharesMinted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": true, + "internalType": "enum AddLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amountsAddedRaw", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "swapFeeAmountsRaw", + "type": "uint256[]" + } + ], + "name": "LiquidityAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWrapped", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "LiquidityAddedToBuffer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": true, + "internalType": "enum RemoveLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amountsRemovedRaw", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "swapFeeAmountsRaw", + "type": "uint256[]" + } + ], + "name": "LiquidityRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWrapped", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "LiquidityRemovedFromBuffer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInitialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PoolPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "recoveryMode", + "type": "bool" + } + ], + "name": "PoolRecoveryModeStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "internalType": "address", + "name": "swapFeeManager", + "type": "address" + }, + { + "internalType": "address", + "name": "poolCreator", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct PoolRoleAccounts", + "name": "roleAccounts", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "enableHookAdjustedAmounts", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallComputeDynamicSwapFee", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "address", + "name": "hooksContract", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct HooksConfig", + "name": "hooksConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableDonation", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IProtocolFeeController", + "name": "newProtocolFeeController", + "type": "address" + } + ], + "name": "ProtocolFeeControllerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeeAmount", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "SwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "burnedShares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "withdrawnUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "Unwrap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "eventKey", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "eventData", + "type": "bytes" + } + ], + "name": "VaultAuxiliary", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "VaultBuffersPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "VaultPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "VaultQueriesDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "VaultQueriesEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "depositedUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintedShares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "Wrap", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "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": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "components": [ + { + "internalType": "enum SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amountGivenScaled18", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "balancesScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "indexIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "indexOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct PoolSwapParams", + "name": "swapParams", + "type": "tuple" + } + ], + "name": "computeDynamicSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "dynamicSwapFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "eventKey", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "eventData", + "type": "bytes" + } + ], + "name": "emitAuxiliaryEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getAddLiquidityCalledFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getAggregateSwapFeeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", "type": "address" }, { - "indexed": false, - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getAggregateYieldFeeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "contract IAuthorizer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getBptRate", + "outputs": [ { - "indexed": false, "internalType": "uint256", - "name": "amountOut", + "name": "rate", "type": "uint256" } ], - "name": "Swap", - "type": "event" + "stateMutability": "view", + "type": "function" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getCurrentLiveBalances", + "outputs": [ { - "indexed": false, - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" } ], - "name": "TokensDeregistered", - "type": "event" + "stateMutability": "view", + "type": "function" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "getERC4626BufferAsset", + "outputs": [ { - "indexed": false, - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { - "indexed": false, - "internalType": "address[]", - "name": "assetManagers", - "type": "address[]" + "internalType": "address", + "name": "pool", + "type": "address" } ], - "name": "TokensRegistered", - "type": "event" + "name": "getHooksConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "enableHookAdjustedAmounts", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallComputeDynamicSwapFee", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "address", + "name": "hooksContract", + "type": "address" + } + ], + "internalType": "struct HooksConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" }, { "inputs": [], - "name": "WETH", + "name": "getNonzeroDeltaCount", "outputs": [ { - "internalType": "contract IWETH", + "internalType": "uint256", "name": "", - "type": "address" + "type": "uint256" } ], "stateMutability": "view", @@ -359,232 +2032,341 @@ { "inputs": [ { - "internalType": "enum IVault.SwapKind", - "name": "kind", - "type": "uint8" - }, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolConfig", + "outputs": [ { "components": [ { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" + "components": [ + { + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableDonation", + "type": "bool" + } + ], + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" }, { "internalType": "uint256", - "name": "assetInIndex", + "name": "staticSwapFeePercentage", "type": "uint256" }, { "internalType": "uint256", - "name": "assetOutIndex", + "name": "aggregateSwapFeePercentage", "type": "uint256" }, { "internalType": "uint256", - "name": "amount", + "name": "aggregateYieldFeePercentage", "type": "uint256" }, { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct IVault.BatchSwapStep[]", - "name": "swaps", - "type": "tuple[]" - }, - { - "internalType": "contract IAsset[]", - "name": "assets", - "type": "address[]" - }, - { - "components": [ + "internalType": "uint40", + "name": "tokenDecimalDiffs", + "type": "uint40" + }, { - "internalType": "address", - "name": "sender", - "type": "address" + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" }, { "internalType": "bool", - "name": "fromInternalBalance", + "name": "isPoolRegistered", "type": "bool" }, { - "internalType": "address payable", - "name": "recipient", - "type": "address" + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolPaused", + "type": "bool" }, { "internalType": "bool", - "name": "toInternalBalance", + "name": "isPoolInRecoveryMode", "type": "bool" } ], - "internalType": "struct IVault.FundManagement", - "name": "funds", + "internalType": "struct PoolConfig", + "name": "", "type": "tuple" - }, - { - "internalType": "int256[]", - "name": "limits", - "type": "int256[]" - }, + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" + "internalType": "address", + "name": "pool", + "type": "address" } ], - "name": "batchSwap", + "name": "getPoolData", "outputs": [ { - "internalType": "int256[]", - "name": "assetDeltas", - "type": "int256[]" + "components": [ + { + "internalType": "PoolConfigBits", + "name": "poolConfigBits", + "type": "bytes32" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + } + ], + "internalType": "struct PoolData", + "name": "", + "type": "tuple" } ], - "stateMutability": "payable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" }, { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "address", + "name": "", + "type": "address" } ], - "name": "deregisterTokens", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, { "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address payable", - "name": "recipient", + "name": "pool", "type": "address" - }, + } + ], + "name": "getPoolRoleAccounts", + "outputs": [ { "components": [ { - "internalType": "contract IAsset[]", - "name": "assets", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "minAmountsOut", - "type": "uint256[]" + "internalType": "address", + "name": "pauseManager", + "type": "address" }, { - "internalType": "bytes", - "name": "userData", - "type": "bytes" + "internalType": "address", + "name": "swapFeeManager", + "type": "address" }, { - "internalType": "bool", - "name": "toInternalBalance", - "type": "bool" + "internalType": "address", + "name": "poolCreator", + "type": "address" } ], - "internalType": "struct IVault.ExitPoolRequest", - "name": "request", + "internalType": "struct PoolRoleAccounts", + "name": "", "type": "tuple" } ], - "name": "exitPool", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "contract IFlashLoanRecipient", - "name": "recipient", + "internalType": "address", + "name": "pool", "type": "address" - }, + } + ], + "name": "getPoolTokenInfo", + "outputs": [ { "internalType": "contract IERC20[]", "name": "tokens", "type": "address[]" }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, { "internalType": "uint256[]", - "name": "amounts", + "name": "balancesRaw", "type": "uint256[]" }, { - "internalType": "bytes", - "name": "userData", - "type": "bytes" + "internalType": "uint256[]", + "name": "lastBalancesLiveScaled18", + "type": "uint256[]" } ], - "name": "flashLoan", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" + "internalType": "address", + "name": "pool", + "type": "address" } ], - "name": "getActionId", + "name": "getPoolTokenRates", "outputs": [ { - "internalType": "bytes32", - "name": "", - "type": "bytes32" + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" } ], "stateMutability": "view", "type": "function" }, { - "inputs": [], - "name": "getAuthorizer", - "outputs": [ + "inputs": [ { - "internalType": "contract IAuthorizer", - "name": "", + "internalType": "address", + "name": "pool", "type": "address" } ], + "name": "getPoolTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "getDomainSeparator", + "name": "getProtocolFeeController", "outputs": [ { - "internalType": "bytes32", + "internalType": "contract IProtocolFeeController", "name": "", - "type": "bytes32" + "type": "address" } ], "stateMutability": "view", @@ -593,22 +2375,17 @@ { "inputs": [ { - "internalType": "address", - "name": "user", + "internalType": "contract IERC20", + "name": "token", "type": "address" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" } ], - "name": "getInternalBalance", + "name": "getReservesOf", "outputs": [ { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" + "internalType": "uint256", + "name": "", + "type": "uint256" } ], "stateMutability": "view", @@ -618,11 +2395,11 @@ "inputs": [ { "internalType": "address", - "name": "user", + "name": "pool", "type": "address" } ], - "name": "getNextNonce", + "name": "getStaticSwapFeePercentage", "outputs": [ { "internalType": "uint256", @@ -634,47 +2411,32 @@ "type": "function" }, { - "inputs": [], - "name": "getPausedState", - "outputs": [ - { - "internalType": "bool", - "name": "paused", - "type": "bool" - }, + "inputs": [ { - "internalType": "uint256", - "name": "pauseWindowEndTime", - "type": "uint256" - }, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenDelta", + "outputs": [ { - "internalType": "uint256", - "name": "bufferPeriodEndTime", - "type": "uint256" + "internalType": "int256", + "name": "", + "type": "int256" } ], "stateMutability": "view", "type": "function" }, { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - } - ], - "name": "getPool", + "inputs": [], + "name": "getVaultAdmin", "outputs": [ { "internalType": "address", "name": "", "type": "address" - }, - { - "internalType": "enum IVault.PoolSpecialization", - "name": "", - "type": "uint8" } ], "stateMutability": "view", @@ -683,52 +2445,15 @@ { "inputs": [ { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "contract IERC20", - "name": "token", + "internalType": "address", + "name": "pool", "type": "address" - } - ], - "name": "getPoolTokenInfo", - "outputs": [ - { - "internalType": "uint256", - "name": "cash", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "managed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" }, { "internalType": "address", - "name": "assetManager", + "name": "to", "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - } - ], - "name": "getPoolTokens", - "outputs": [ + }, { "internalType": "contract IERC20[]", "name": "tokens", @@ -736,45 +2461,40 @@ }, { "internalType": "uint256[]", - "name": "balances", + "name": "exactAmountsIn", "type": "uint256[]" }, { "internalType": "uint256", - "name": "lastChangeBlock", + "name": "minBptAmountOut", "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" } ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getProtocolFeesCollector", + "name": "initialize", "outputs": [ { - "internalType": "contract ProtocolFeesCollector", - "name": "", - "type": "address" + "internalType": "uint256", + "name": "bptAmountOut", + "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "relayer", + "internalType": "contract IERC4626", + "name": "wrappedToken", "type": "address" } ], - "name": "hasApprovedRelayer", + "name": "isERC4626BufferInitialized", "outputs": [ { "internalType": "bool", @@ -787,389 +2507,337 @@ }, { "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, { "internalType": "address", - "name": "recipient", + "name": "pool", "type": "address" - }, - { - "components": [ - { - "internalType": "contract IAsset[]", - "name": "assets", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "maxAmountsIn", - "type": "uint256[]" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "fromInternalBalance", - "type": "bool" - } - ], - "internalType": "struct IVault.JoinPoolRequest", - "name": "request", - "type": "tuple" - } - ], - "name": "joinPool", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum IVault.PoolBalanceOpKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "internalType": "struct IVault.PoolBalanceOp[]", - "name": "ops", - "type": "tuple[]" } ], - "name": "managePoolBalance", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ + "name": "isPoolInRecoveryMode", + "outputs": [ { - "components": [ - { - "internalType": "enum IVault.UserBalanceOpKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "contract IAsset", - "name": "asset", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address payable", - "name": "recipient", - "type": "address" - } - ], - "internalType": "struct IVault.UserBalanceOp[]", - "name": "ops", - "type": "tuple[]" + "internalType": "bool", + "name": "", + "type": "bool" } ], - "name": "manageUserBalance", - "outputs": [], - "stateMutability": "payable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "enum IVault.SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "assetInIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "assetOutIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct IVault.BatchSwapStep[]", - "name": "swaps", - "type": "tuple[]" - }, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInitialized", + "outputs": [ { - "internalType": "contract IAsset[]", - "name": "assets", - "type": "address[]" - }, + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { - "components": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "bool", - "name": "fromInternalBalance", - "type": "bool" - }, - { - "internalType": "address payable", - "name": "recipient", - "type": "address" - }, - { - "internalType": "bool", - "name": "toInternalBalance", - "type": "bool" - } - ], - "internalType": "struct IVault.FundManagement", - "name": "funds", - "type": "tuple" + "internalType": "address", + "name": "pool", + "type": "address" } ], - "name": "queryBatchSwap", + "name": "isPoolPaused", "outputs": [ { - "internalType": "int256[]", + "internalType": "bool", "name": "", - "type": "int256[]" + "type": "bool" } ], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "enum IVault.PoolSpecialization", - "name": "specialization", - "type": "uint8" + "internalType": "address", + "name": "pool", + "type": "address" } ], - "name": "registerPool", + "name": "isPoolRegistered", "outputs": [ { - "internalType": "bytes32", + "internalType": "bool", "name": "", - "type": "bytes32" + "type": "bool" } ], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "inputs": [ + "inputs": [], + "name": "isQueryDisabled", + "outputs": [ { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isQueryDisabledPermanently", + "outputs": [ { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isUnlocked", + "outputs": [ { - "internalType": "address[]", - "name": "assetManagers", - "type": "address[]" + "internalType": "bool", + "name": "", + "type": "bool" } ], - "name": "registerTokens", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "contract IAuthorizer", - "name": "newAuthorizer", - "type": "address" + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" } ], - "name": "setAuthorizer", - "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "bool", - "name": "paused", - "type": "bool" + "internalType": "bytes", + "name": "data", + "type": "bytes" } ], - "name": "setPaused", + "name": "quoteAndRevert", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "relayer", - "type": "address" - }, + "inputs": [], + "name": "reentrancyGuardEntered", + "outputs": [ { "internalType": "bool", - "name": "approved", + "name": "", "type": "bool" } ], - "name": "setRelayerApproval", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, { "components": [ { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" + "internalType": "contract IERC20", + "name": "token", + "type": "address" }, { - "internalType": "enum IVault.SwapKind", - "name": "kind", + "internalType": "enum TokenType", + "name": "tokenType", "type": "uint8" }, { - "internalType": "contract IAsset", - "name": "assetIn", + "internalType": "contract IRateProvider", + "name": "rateProvider", "type": "address" }, { - "internalType": "contract IAsset", - "name": "assetOut", + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "protocolFeeExempt", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "pauseManager", "type": "address" }, { - "internalType": "uint256", - "name": "amount", - "type": "uint256" + "internalType": "address", + "name": "swapFeeManager", + "type": "address" }, { - "internalType": "bytes", - "name": "userData", - "type": "bytes" + "internalType": "address", + "name": "poolCreator", + "type": "address" } ], - "internalType": "struct IVault.SingleSwap", - "name": "singleSwap", + "internalType": "struct PoolRoleAccounts", + "name": "roleAccounts", "type": "tuple" }, + { + "internalType": "address", + "name": "poolHooksContract", + "type": "address" + }, { "components": [ { - "internalType": "address", - "name": "sender", - "type": "address" + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" }, { "internalType": "bool", - "name": "fromInternalBalance", + "name": "enableAddLiquidityCustom", "type": "bool" }, { - "internalType": "address payable", - "name": "recipient", - "type": "address" + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" }, { "internalType": "bool", - "name": "toInternalBalance", + "name": "enableDonation", "type": "bool" } ], - "internalType": "struct IVault.FundManagement", - "name": "funds", + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", "type": "tuple" + } + ], + "name": "registerPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" }, { - "internalType": "uint256", - "name": "limit", - "type": "uint256" + "internalType": "address", + "name": "from", + "type": "address" }, { "internalType": "uint256", - "name": "deadline", + "name": "exactBptAmountIn", "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "minAmountsOut", + "type": "uint256[]" + } + ], + "name": "removeLiquidityRecovery", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amountsOutRaw", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" } ], - "name": "swap", + "name": "totalSupply", "outputs": [ { "internalType": "uint256", - "name": "amountCalculated", + "name": "", "type": "uint256" } ], - "stateMutability": "payable", + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vault", + "outputs": [ + { + "internalType": "contract IVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", "type": "function" }, { From 30b872c01457ad509626a1eb53d03fdcafb7364c Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 30 Dec 2024 09:04:18 +0700 Subject: [PATCH 06/39] tmp --- .../balancer-v3/math/error.go | 11 + .../balancer-v3/math/fixed_point.go | 183 -------- .../balancer-v3/math/fixed_point_test.go | 25 -- .../balancer-v3/math/log_exp_math.go | 389 ------------------ .../balancer-v3/math/log_exp_math_test.go | 48 --- .../balancer-v3/math/weighted_math.go | 350 ++++++++-------- .../balancer-v3/stable/pool_simulator.go | 4 +- 7 files changed, 188 insertions(+), 822 deletions(-) create mode 100644 pkg/liquidity-source/balancer-v3/math/error.go delete mode 100644 pkg/liquidity-source/balancer-v3/math/fixed_point.go delete mode 100644 pkg/liquidity-source/balancer-v3/math/fixed_point_test.go delete mode 100644 pkg/liquidity-source/balancer-v3/math/log_exp_math.go delete mode 100644 pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go diff --git a/pkg/liquidity-source/balancer-v3/math/error.go b/pkg/liquidity-source/balancer-v3/math/error.go new file mode 100644 index 000000000..c58c3d8ad --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/error.go @@ -0,0 +1,11 @@ +package math + +import "errors" + +var ( + ErrAddOverflow = errors.New("ADD_OVERFLOW") + ErrSubOverflow = errors.New("SUB_OVERFLOW") + ErrZeroDivision = errors.New("ZERO_DIVISION") + ErrDivInternal = errors.New("DIV_INTERNAL") + ErrMulOverflow = errors.New("MUL_OVERFLOW") +) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point.go b/pkg/liquidity-source/balancer-v3/math/fixed_point.go deleted file mode 100644 index da1b9700a..000000000 --- a/pkg/liquidity-source/balancer-v3/math/fixed_point.go +++ /dev/null @@ -1,183 +0,0 @@ -package math - -import ( - "errors" - - "github.com/KyberNetwork/blockchain-toolkit/number" - "github.com/holiman/uint256" -) - -var ( - ErrAddOverflow = errors.New("ADD_OVERFLOW") - ErrSubOverflow = errors.New("SUB_OVERFLOW") - ErrZeroDivision = errors.New("ZERO_DIVISION") - ErrDivInternal = errors.New("DIV_INTERNAL") - ErrMulOverflow = errors.New("MUL_OVERFLOW") -) - -var FixedPoint *fixedPoint - -type fixedPoint struct { - ZERO *uint256.Int - ONE *uint256.Int - TWO *uint256.Int - FOUR *uint256.Int - MAX_POW_RELATIVE_ERROR *uint256.Int -} - -func init() { - zero := uint256.NewInt(0) - one := number.Number_1e18 - two := new(uint256.Int).Mul(number.Number_2, one) - four := new(uint256.Int).Mul(number.Number_4, one) - - FixedPoint = &fixedPoint{ - ZERO: zero, - ONE: one, - TWO: two, - FOUR: four, - MAX_POW_RELATIVE_ERROR: uint256.NewInt(10000), - } -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L34 -func (l *fixedPoint) Add(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - c := new(uint256.Int).Add(a, b) - - if c.Lt(a) { - return nil, ErrAddOverflow - } - - return c, nil -} - -func (l *fixedPoint) Sub(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - if a.Lt(b) { - return nil, ErrSubOverflow - } - - return new(uint256.Int).Sub(a, b), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L83 -func (l *fixedPoint) DivUp(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - if b.IsZero() { - return nil, ErrZeroDivision - } - - aInflated := new(uint256.Int).Mul(a, l.ONE) - - if !(a.IsZero() || new(uint256.Int).Div(aInflated, a).Eq(l.ONE)) { - return nil, ErrDivInternal - } - - if aInflated.IsZero() { - return number.Zero, nil - } - - return new(uint256.Int).Add(new(uint256.Int).Div(new(uint256.Int).Sub(aInflated, number.Number_1), b), number.Number_1), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L74 -func (l *fixedPoint) DivDown(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - if b.IsZero() { - return nil, ErrZeroDivision - } - - aInflated := new(uint256.Int).Mul(a, l.ONE) - - if !(a.IsZero() || new(uint256.Int).Div(aInflated, a).Eq(l.ONE)) { - return nil, ErrDivInternal - } - - return new(uint256.Int).Div(aInflated, b), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L132 -func (l *fixedPoint) PowUp(x *uint256.Int, y *uint256.Int) (*uint256.Int, error) { - if y.Eq(l.ONE) { - return x, nil - } - if y.Eq(l.TWO) { - return l.MulUp(x, x) - } - if y.Eq(l.FOUR) { - square, err := l.MulUp(x, x) - if err != nil { - return nil, err - } - - return l.MulUp(square, square) - } - - raw, err := LogExpMath.Pow(x, y) - if err != nil { - return nil, err - } - - mulUpRawAndMaxPow, err := l.MulUp(raw, l.MAX_POW_RELATIVE_ERROR) - if err != nil { - return nil, err - } - - maxError, err := l.Add(mulUpRawAndMaxPow, number.Number_1) - if err != nil { - return nil, err - } - - return l.Add(raw, maxError) -} - -func (l *fixedPoint) PowUpV1(x *uint256.Int, y *uint256.Int) (*uint256.Int, error) { - raw, err := LogExpMath.Pow(x, y) - if err != nil { - return nil, err - } - - mulUpRawAndMaxPow, err := l.MulUp(raw, l.MAX_POW_RELATIVE_ERROR) - if err != nil { - return nil, err - } - - maxError, err := l.Add(mulUpRawAndMaxPow, number.Number_1) - if err != nil { - return nil, err - } - - return l.Add(raw, maxError) -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L50 -func (l *fixedPoint) MulDown(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - product := new(uint256.Int).Mul(a, b) - - if !(a.IsZero() || new(uint256.Int).Div(product, a).Eq(b)) { - return nil, ErrMulOverflow - } - - return new(uint256.Int).Div(product, l.ONE), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L57 -func (l *fixedPoint) MulUp(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - product := new(uint256.Int).Mul(a, b) - - if !(a.IsZero() || new(uint256.Int).Div(product, a).Eq(b)) { - return nil, ErrMulOverflow - } - - if product.IsZero() { - return number.Zero, nil - } - - return new(uint256.Int).Add(new(uint256.Int).Div(new(uint256.Int).Sub(product, number.Number_1), l.ONE), number.Number_1), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/FixedPoint.sol#L156 -func (l *fixedPoint) Complement(x *uint256.Int) *uint256.Int { - if x.Lt(l.ONE) { - return new(uint256.Int).Sub(l.ONE, x) - } - - return number.Zero -} diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_test.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_test.go deleted file mode 100644 index d43285ff1..000000000 --- a/pkg/liquidity-source/balancer-v3/math/fixed_point_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package math - -import ( - "testing" - - "github.com/KyberNetwork/blockchain-toolkit/number" - "github.com/stretchr/testify/assert" -) - -func TestFixedPoint_MulDown(t *testing.T) { - t.Run("it should return correct result", func(t *testing.T) { - result, err := FixedPoint.MulDown(number.NewUint256("25925243203807071591361"), number.NewUint256("176891139771667")) - - assert.Nil(t, err) - assert.Zero(t, result.Cmp(number.NewUint256("4585945819179096677"))) - }) -} - -func TestFixedPoint_Complement(t *testing.T) { - t.Run("it should return correct result", func(t *testing.T) { - result := FixedPoint.Complement(number.NewUint256("999823108860228333")) - - assert.Zero(t, result.Cmp(number.NewUint256("176891139771667"))) - }) -} diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go deleted file mode 100644 index 88a1a111f..000000000 --- a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go +++ /dev/null @@ -1,389 +0,0 @@ -package math - -import ( - "errors" - "math/big" - - "github.com/KyberNetwork/blockchain-toolkit/integer" - "github.com/KyberNetwork/blockchain-toolkit/number" - "github.com/holiman/uint256" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" -) - -var ( - ErrXOutOfBounds = errors.New("X_OUT_OF_BOUNDS") - ErrYOutOfBounds = errors.New("Y_OUT_OF_BOUNDS") - ErrProductOutOfBounds = errors.New("PRODUCT_OUT_OF_BOUNDS") - ErrInvalidExponent = errors.New("INVALID_EXPONENT") -) - -var LogExpMath *logExpMath - -func init() { - one_18 := integer.TenPow(18) - one_20 := integer.TenPow(20) - one_36 := integer.TenPow(36) - - LogExpMath = &logExpMath{ - ONE_18: one_18, - ONE_20: one_20, - ONE_36: one_36, - MILD_EXPONENT_BOUND: new(uint256.Int).Div(new(uint256.Int).Exp(number.Number_2, uint256.NewInt(254)), number.TenPow(20)), - LN_36_LOWER_BOUND: new(big.Int).Sub(one_18, integer.TenPow(17)), - LN_36_UPPER_BOUND: new(big.Int).Add(one_18, integer.TenPow(17)), - MIN_NATURAL_EXPONENT: new(big.Int).Mul(big.NewInt(-41), one_18), - MAX_NATURAL_EXPONENT: new(big.Int).Mul(big.NewInt(130), one_18), - x0: bignumber.NewBig10("128000000000000000000"), - a0: bignumber.NewBig10("38877084059945950922200000000000000000000000000000000000"), - x1: bignumber.NewBig10("64000000000000000000"), - a1: bignumber.NewBig10("6235149080811616882910000000"), - x2: bignumber.NewBig10("3200000000000000000000"), - a2: bignumber.NewBig10("7896296018268069516100000000000000"), - x3: bignumber.NewBig10("1600000000000000000000"), - a3: bignumber.NewBig10("888611052050787263676000000"), - x4: bignumber.NewBig10("800000000000000000000"), - a4: bignumber.NewBig10("298095798704172827474000"), - x5: bignumber.NewBig10("400000000000000000000"), - a5: bignumber.NewBig10("5459815003314423907810"), - x6: bignumber.NewBig10("200000000000000000000"), - a6: bignumber.NewBig10("738905609893065022723"), - x7: bignumber.NewBig10("100000000000000000000"), - a7: bignumber.NewBig10("271828182845904523536"), - x8: bignumber.NewBig10("50000000000000000000"), - a8: bignumber.NewBig10("164872127070012814685"), - x9: bignumber.NewBig10("25000000000000000000"), - a9: bignumber.NewBig10("128402541668774148407"), - x10: bignumber.NewBig10("12500000000000000000"), - a10: bignumber.NewBig10("113314845306682631683"), - x11: bignumber.NewBig10("6250000000000000000"), - a11: bignumber.NewBig10("106449445891785942956"), - } -} - -type logExpMath struct { - ONE_18 *big.Int - ONE_20 *big.Int - ONE_36 *big.Int - MILD_EXPONENT_BOUND *uint256.Int - LN_36_LOWER_BOUND *big.Int - LN_36_UPPER_BOUND *big.Int - MIN_NATURAL_EXPONENT *big.Int - MAX_NATURAL_EXPONENT *big.Int - x0 *big.Int - a0 *big.Int - x1 *big.Int - a1 *big.Int - x2 *big.Int - a2 *big.Int - x3 *big.Int - a3 *big.Int - x4 *big.Int - a4 *big.Int - x5 *big.Int - a5 *big.Int - x6 *big.Int - a6 *big.Int - x7 *big.Int - a7 *big.Int - x8 *big.Int - a8 *big.Int - x9 *big.Int - a9 *big.Int - x10 *big.Int - a10 *big.Int - x11 *big.Int - a11 *big.Int -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L93 -func (l *logExpMath) Pow(x *uint256.Int, y *uint256.Int) (*uint256.Int, error) { - if y.IsZero() { - return number.Number_1e18, nil - } - - if x.IsZero() { - return number.Zero, nil - } - - if !new(uint256.Int).Rsh(x, 255).IsZero() { - return nil, ErrXOutOfBounds - } - x_int256 := x.ToBig() - - if y.Cmp(l.MILD_EXPONENT_BOUND) >= 0 { - return nil, ErrYOutOfBounds - } - y_int256 := y.ToBig() - - var logXTimesY *big.Int - - if l.LN_36_LOWER_BOUND.Cmp(x_int256) < 0 && x_int256.Cmp(l.LN_36_UPPER_BOUND) < 0 { - ln36X := l._ln_36(x_int256) - logXTimesY = new(big.Int).Add( - new(big.Int).Mul(new(big.Int).Quo(ln36X, l.ONE_18), y_int256), - new(big.Int).Quo(new(big.Int).Mul(new(big.Int).Rem(ln36X, l.ONE_18), y_int256), l.ONE_18), - ) - } else { - logXTimesY = new(big.Int).Mul(l._ln(x_int256), y_int256) - } - - logXTimesY = new(big.Int).Quo(logXTimesY, l.ONE_18) - - if l.MIN_NATURAL_EXPONENT.Cmp(logXTimesY) > 0 || logXTimesY.Cmp(l.MAX_NATURAL_EXPONENT) > 0 { - return nil, ErrProductOutOfBounds - } - - result, err := l.Exp(logXTimesY) - if err != nil { - return nil, err - } - - return uint256.MustFromBig(result), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L146 -func (l *logExpMath) Exp(x *big.Int) (*big.Int, error) { - if x.Cmp(l.MIN_NATURAL_EXPONENT) < 0 || x.Cmp(l.MAX_NATURAL_EXPONENT) > 0 { - return nil, ErrInvalidExponent - } - - if x.Cmp(integer.Zero()) < 0 { - negativeXExp, err := l.Exp(new(big.Int).Neg(x)) - if err != nil { - return nil, err - } - - return new(big.Int).Quo(new(big.Int).Mul(l.ONE_18, l.ONE_18), negativeXExp), nil - } - - var firstAN *big.Int - if x.Cmp(l.x0) >= 0 { - x = new(big.Int).Sub(x, l.x0) - firstAN = l.a0 - } else if x.Cmp(l.x1) >= 0 { - x = new(big.Int).Sub(x, l.x1) - firstAN = l.a1 - } else { - firstAN = integer.One() - } - - x = new(big.Int).Mul(x, big.NewInt(100)) - product := new(big.Int).Set(l.ONE_20) - - if x.Cmp(l.x2) >= 0 { - x = new(big.Int).Sub(x, l.x2) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a2), l.ONE_20) - } - - if x.Cmp(l.x3) >= 0 { - x = new(big.Int).Sub(x, l.x3) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a3), l.ONE_20) - } - - if x.Cmp(l.x4) >= 0 { - x = new(big.Int).Sub(x, l.x4) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a4), l.ONE_20) - } - - if x.Cmp(l.x5) >= 0 { - x = new(big.Int).Sub(x, l.x5) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a5), l.ONE_20) - } - - if x.Cmp(l.x6) >= 0 { - x = new(big.Int).Sub(x, l.x6) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a6), l.ONE_20) - } - - if x.Cmp(l.x7) >= 0 { - x = new(big.Int).Sub(x, l.x7) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a7), l.ONE_20) - } - - if x.Cmp(l.x8) >= 0 { - x = new(big.Int).Sub(x, l.x8) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a8), l.ONE_20) - } - - if x.Cmp(l.x9) >= 0 { - x = new(big.Int).Sub(x, l.x9) - product = new(big.Int).Quo(new(big.Int).Mul(product, l.a9), l.ONE_20) - } - - seriesSum := new(big.Int).Set(l.ONE_20) - - term := new(big.Int).Set(x) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(2)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(3)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(4)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(5)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(6)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(7)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(8)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(9)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(10)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(11)) - seriesSum = new(big.Int).Add(seriesSum, term) - - term = new(big.Int).Quo(new(big.Int).Quo(new(big.Int).Mul(term, x), l.ONE_20), big.NewInt(12)) - seriesSum = new(big.Int).Add(seriesSum, term) - - return new(big.Int).Quo(new(big.Int).Mul(new(big.Int).Quo(new(big.Int).Mul(product, seriesSum), l.ONE_20), firstAN), big.NewInt(100)), nil -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L466 -func (l *logExpMath) _ln_36(x *big.Int) *big.Int { - x = new(big.Int).Mul(x, l.ONE_18) - - z := new(big.Int).Quo( - new(big.Int).Mul( - new(big.Int).Sub(x, l.ONE_36), - l.ONE_36, - ), - new(big.Int).Add(x, l.ONE_36), - ) - zSquared := new(big.Int).Quo(new(big.Int).Mul(z, z), l.ONE_36) - - num := new(big.Int).Set(z) - seriesSum := new(big.Int).Set(num) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(3))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(5))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(7))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(9))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(11))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(13))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_36) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(15))) - - return new(big.Int).Mul(seriesSum, integer.Two()) -} - -// https://github.com/balancer/balancer-v2-monorepo/blob/c7d4abbea39834e7778f9ff7999aaceb4e8aa048/pkg/solidity-utils/contracts/math/LogExpMath.sol#L326 -func (l *logExpMath) _ln(a *big.Int) *big.Int { - if a.Cmp(l.ONE_18) < 0 { - return new(big.Int).Neg(l._ln(new(big.Int).Quo(new(big.Int).Mul(l.ONE_18, l.ONE_18), a))) - } - - sum := integer.Zero() - if a.Cmp(new(big.Int).Mul(l.a0, l.ONE_18)) >= 0 { - a = new(big.Int).Quo(a, l.a0) - sum = new(big.Int).Add(sum, l.x0) - } - - if a.Cmp(new(big.Int).Mul(l.a1, l.ONE_18)) >= 0 { - a = new(big.Int).Quo(a, l.a1) - sum = new(big.Int).Add(sum, l.x1) - } - - sum = new(big.Int).Mul(sum, big.NewInt(100)) - a = new(big.Int).Mul(a, big.NewInt(100)) - - if a.Cmp(l.a2) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a2) - sum = new(big.Int).Add(sum, l.x2) - } - - if a.Cmp(l.a3) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a3) - sum = new(big.Int).Add(sum, l.x3) - } - - if a.Cmp(l.a4) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a4) - sum = new(big.Int).Add(sum, l.x4) - } - - if a.Cmp(l.a5) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a5) - sum = new(big.Int).Add(sum, l.x5) - } - - if a.Cmp(l.a6) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a6) - sum = new(big.Int).Add(sum, l.x6) - } - - if a.Cmp(l.a7) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a7) - sum = new(big.Int).Add(sum, l.x7) - } - - if a.Cmp(l.a8) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a8) - sum = new(big.Int).Add(sum, l.x8) - } - - if a.Cmp(l.a9) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a9) - sum = new(big.Int).Add(sum, l.x9) - } - - if a.Cmp(l.a10) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a10) - sum = new(big.Int).Add(sum, l.x10) - } - - if a.Cmp(l.a11) >= 0 { - a = new(big.Int).Quo(new(big.Int).Mul(a, l.ONE_20), l.a11) - sum = new(big.Int).Add(sum, l.x11) - } - - z := new(big.Int).Quo(new(big.Int).Mul(new(big.Int).Sub(a, l.ONE_20), l.ONE_20), new(big.Int).Add(a, l.ONE_20)) - zSquared := new(big.Int).Quo(new(big.Int).Mul(z, z), l.ONE_20) - - num := z - seriesSum := num - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(3))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(5))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(7))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(9))) - - num = new(big.Int).Quo(new(big.Int).Mul(num, zSquared), l.ONE_20) - seriesSum = new(big.Int).Add(seriesSum, new(big.Int).Quo(num, big.NewInt(11))) - - seriesSum = new(big.Int).Mul(seriesSum, integer.Two()) - - return new(big.Int).Quo(new(big.Int).Add(sum, seriesSum), big.NewInt(100)) -} diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go deleted file mode 100644 index 5b3fa10e0..000000000 --- a/pkg/liquidity-source/balancer-v3/math/log_exp_math_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package math - -import ( - "fmt" - "testing" - - "github.com/KyberNetwork/blockchain-toolkit/number" - "github.com/stretchr/testify/assert" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" -) - -func TestLogExpMath_Pow(t *testing.T) { - t.Run("it should return correct result", func(t *testing.T) { - result, err := LogExpMath.Pow(number.NewUint256("999955774281269788"), number.NewUint256("4000000000000000000")) - - fmt.Printf("result: %s\n", result.ToBig().String()) - - assert.Nil(t, err) - assert.Zero(t, result.Cmp(number.NewUint256("999823108860218333"))) - }) - - t.Run("it should return correct result", func(t *testing.T) { - result, err := LogExpMath.Pow(number.NewUint256("877904152923774542"), number.NewUint256("250000000000000000")) - - fmt.Printf("result: %s\n", result.ToBig().String()) - - assert.Nil(t, err) - assert.Zero(t, result.Cmp(number.NewUint256("967969728761424232"))) - }) -} - -func TestLogExpMath_Exp(t *testing.T) { - t.Run("should be return correct result", func(t *testing.T) { - result, err := LogExpMath.Exp(bignumber.NewBig10("-176906786864581")) - - assert.Nil(t, err) - assert.Zero(t, result.Cmp(bignumber.NewBig10("999823108860218333"))) - }) -} - -func TestLogExpMath_ln_36(t *testing.T) { - t.Run("it should return correct result", func(t *testing.T) { - result := LogExpMath._ln_36(bignumber.NewBig10("999955774281269788")) - - assert.Zero(t, result.Cmp(bignumber.NewBig10("-44226696716145462061506341909418"))) - }) -} diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math.go b/pkg/liquidity-source/balancer-v3/math/weighted_math.go index 473eec182..5eaca1375 100644 --- a/pkg/liquidity-source/balancer-v3/math/weighted_math.go +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math.go @@ -23,178 +23,178 @@ func init() { WeightedMath = &weightedMath{} } -// https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L78 -func (l *weightedMath) CalcOutGivenIn( - balanceIn *uint256.Int, - weightIn *uint256.Int, - balanceOut *uint256.Int, - weightOut *uint256.Int, - amountIn *uint256.Int, -) (*uint256.Int, error) { - maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) - if err != nil { - return nil, err - } - - if amountIn.Gt(maxIn) { - return nil, ErrMaxInRatio - } - - denominator, err := FixedPoint.Add(balanceIn, amountIn) - if err != nil { - return nil, err - } - - base, err := FixedPoint.DivUp(balanceIn, denominator) - if err != nil { - return nil, err - } - - exponent, err := FixedPoint.DivDown(weightIn, weightOut) - if err != nil { - return nil, err - } - - power, err := FixedPoint.PowUp(base, exponent) - if err != nil { - return nil, err - } - - return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) -} - -// https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L69 -func (l *weightedMath) CalcOutGivenInV1( - balanceIn *uint256.Int, - weightIn *uint256.Int, - balanceOut *uint256.Int, - weightOut *uint256.Int, - amountIn *uint256.Int, -) (*uint256.Int, error) { - maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) - if err != nil { - return nil, err - } - - if amountIn.Gt(maxIn) { - return nil, ErrMaxInRatio - } - - denominator, err := FixedPoint.Add(balanceIn, amountIn) - if err != nil { - return nil, err - } - - base, err := FixedPoint.DivUp(balanceIn, denominator) - if err != nil { - return nil, err - } - - exponent, err := FixedPoint.DivDown(weightIn, weightOut) - if err != nil { - return nil, err - } - - power, err := FixedPoint.PowUpV1(base, exponent) - if err != nil { - return nil, err - } - - return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) -} - -// https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L113 -func (l *weightedMath) CalcInGivenOut( - balanceIn *uint256.Int, - weightIn *uint256.Int, - balanceOut *uint256.Int, - weightOut *uint256.Int, - amountOut *uint256.Int, -) (*uint256.Int, error) { - maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) - if err != nil { - return nil, err - } - - // Cannot exceed maximum out ratio - if amountOut.Gt(maxOut) { - return nil, ErrMaxOutRatio - } - - remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) - if err != nil { - return nil, err - } - - base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) - if err != nil { - return nil, err - } - - exponent, err := FixedPoint.DivUp(weightOut, weightIn) - if err != nil { - return nil, err - } - - power, err := FixedPoint.PowUp(base, exponent) - if err != nil { - return nil, err - } - - // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so - // the following subtraction should never revert. - ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) - if err != nil { - return nil, err - } - - return FixedPoint.MulUp(balanceIn, ratio) -} - -// https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L104 -func (l *weightedMath) CalcInGivenOutV1( - balanceIn *uint256.Int, - weightIn *uint256.Int, - balanceOut *uint256.Int, - weightOut *uint256.Int, - amountOut *uint256.Int, -) (*uint256.Int, error) { - maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) - if err != nil { - return nil, err - } - - // Cannot exceed maximum out ratio - if amountOut.Gt(maxOut) { - return nil, ErrMaxOutRatio - } - - remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) - if err != nil { - return nil, err - } - - base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) - if err != nil { - return nil, err - } - - exponent, err := FixedPoint.DivUp(weightOut, weightIn) - if err != nil { - return nil, err - } - - power, err := FixedPoint.PowUpV1(base, exponent) - if err != nil { - return nil, err - } - - // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so - // the following subtraction should never revert. - ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) - if err != nil { - return nil, err - } - - return FixedPoint.MulUp(balanceIn, ratio) -} +// // https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L78 +// func (l *weightedMath) CalcOutGivenIn( +// balanceIn *uint256.Int, +// weightIn *uint256.Int, +// balanceOut *uint256.Int, +// weightOut *uint256.Int, +// amountIn *uint256.Int, +// ) (*uint256.Int, error) { +// maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) +// if err != nil { +// return nil, err +// } + +// if amountIn.Gt(maxIn) { +// return nil, ErrMaxInRatio +// } + +// denominator, err := FixedPoint.Add(balanceIn, amountIn) +// if err != nil { +// return nil, err +// } + +// base, err := FixedPoint.DivUp(balanceIn, denominator) +// if err != nil { +// return nil, err +// } + +// exponent, err := FixedPoint.DivDown(weightIn, weightOut) +// if err != nil { +// return nil, err +// } + +// power, err := FixedPoint.PowUp(base, exponent) +// if err != nil { +// return nil, err +// } + +// return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) +// } + +// // https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L69 +// func (l *weightedMath) CalcOutGivenInV1( +// balanceIn *uint256.Int, +// weightIn *uint256.Int, +// balanceOut *uint256.Int, +// weightOut *uint256.Int, +// amountIn *uint256.Int, +// ) (*uint256.Int, error) { +// maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) +// if err != nil { +// return nil, err +// } + +// if amountIn.Gt(maxIn) { +// return nil, ErrMaxInRatio +// } + +// denominator, err := FixedPoint.Add(balanceIn, amountIn) +// if err != nil { +// return nil, err +// } + +// base, err := FixedPoint.DivUp(balanceIn, denominator) +// if err != nil { +// return nil, err +// } + +// exponent, err := FixedPoint.DivDown(weightIn, weightOut) +// if err != nil { +// return nil, err +// } + +// power, err := FixedPoint.PowUpV1(base, exponent) +// if err != nil { +// return nil, err +// } + +// return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) +// } + +// // https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L113 +// func (l *weightedMath) CalcInGivenOut( +// balanceIn *uint256.Int, +// weightIn *uint256.Int, +// balanceOut *uint256.Int, +// weightOut *uint256.Int, +// amountOut *uint256.Int, +// ) (*uint256.Int, error) { +// maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) +// if err != nil { +// return nil, err +// } + +// // Cannot exceed maximum out ratio +// if amountOut.Gt(maxOut) { +// return nil, ErrMaxOutRatio +// } + +// remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) +// if err != nil { +// return nil, err +// } + +// base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) +// if err != nil { +// return nil, err +// } + +// exponent, err := FixedPoint.DivUp(weightOut, weightIn) +// if err != nil { +// return nil, err +// } + +// power, err := FixedPoint.PowUp(base, exponent) +// if err != nil { +// return nil, err +// } + +// // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so +// // the following subtraction should never revert. +// ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) +// if err != nil { +// return nil, err +// } + +// return FixedPoint.MulUp(balanceIn, ratio) +// } + +// // https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L104 +// func (l *weightedMath) CalcInGivenOutV1( +// balanceIn *uint256.Int, +// weightIn *uint256.Int, +// balanceOut *uint256.Int, +// weightOut *uint256.Int, +// amountOut *uint256.Int, +// ) (*uint256.Int, error) { +// maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) +// if err != nil { +// return nil, err +// } + +// // Cannot exceed maximum out ratio +// if amountOut.Gt(maxOut) { +// return nil, ErrMaxOutRatio +// } + +// remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) +// if err != nil { +// return nil, err +// } + +// base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) +// if err != nil { +// return nil, err +// } + +// exponent, err := FixedPoint.DivUp(weightOut, weightIn) +// if err != nil { +// return nil, err +// } + +// power, err := FixedPoint.PowUpV1(base, exponent) +// if err != nil { +// return nil, err +// } + +// // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so +// // the following subtraction should never revert. +// ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) +// if err != nil { +// return nil, err +// } + +// return FixedPoint.MulUp(balanceIn, ratio) +// } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index 392dc227e..03273f198 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -11,7 +11,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" "github.com/KyberNetwork/logger" @@ -315,7 +315,7 @@ func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool }, nil } -func (p *PoolSimulator) CloneState() pool.IPoolSimulator { +func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { cloned := *p cloned.swapFeePercentage = p.swapFeePercentage.Clone() cloned.aggregateSwapFeePercentage = p.aggregateSwapFeePercentage.Clone() From 26eb4c620941c96163792e508cc04c1d8fb12477 Mon Sep 17 00:00:00 2001 From: SunSpirit <48086732+sunspirit99@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:14:54 +0700 Subject: [PATCH 07/39] Integrate solidly-v2 (#671) --- pkg/liquidity-source/curve/stable-ng/math.go | 8 +- .../deltaswap-v1/pool_simulator.go | 7 +- .../dodo/classical/sell_helper.go | 8 +- pkg/liquidity-source/dodo/classical/trader.go | 14 +- pkg/liquidity-source/dodo/dpp/trader.go | 14 +- pkg/liquidity-source/dodo/dsp/trader.go | 14 +- pkg/liquidity-source/dodo/dvm/trader.go | 14 +- .../ringswap/pool_simulator.go | 7 +- pkg/liquidity-source/solidly-v2/abis.go | 34 + .../solidly-v2/abis/Factory.json | 486 ++++++++ .../solidly-v2/abis/Pool.json | 1079 +++++++++++++++++ pkg/liquidity-source/solidly-v2/config.go | 8 + pkg/liquidity-source/solidly-v2/constant.go | 25 + pkg/liquidity-source/solidly-v2/embed.go | 9 + .../solidly-v2/pool_list_updater.go | 342 ++++++ .../solidly-v2/pool_simulator.go | 440 +++++++ .../solidly-v2/pool_simulator_test.go | 357 ++++++ .../solidly-v2/pool_tracker.go | 154 +++ pkg/liquidity-source/solidly-v2/types.go | 18 + .../uniswap-v2/pool_simulator.go | 7 +- .../velodrome-v1/pool_simulator.go | 7 +- .../velodrome-v2/pool_simulator.go | 15 +- .../velodrome-v2/pool_tracker.go | 130 +- pkg/liquidity-source/velodrome-v2/types.go | 1 - .../virtual-fun/pool_simulator.go | 13 +- pkg/msgpack/register_pool_types.go | 2 + pkg/pooltypes/pooltypes.go | 3 + pkg/valueobject/exchange.go | 10 + 28 files changed, 3126 insertions(+), 100 deletions(-) create mode 100644 pkg/liquidity-source/solidly-v2/abis.go create mode 100644 pkg/liquidity-source/solidly-v2/abis/Factory.json create mode 100644 pkg/liquidity-source/solidly-v2/abis/Pool.json create mode 100644 pkg/liquidity-source/solidly-v2/config.go create mode 100644 pkg/liquidity-source/solidly-v2/constant.go create mode 100644 pkg/liquidity-source/solidly-v2/embed.go create mode 100644 pkg/liquidity-source/solidly-v2/pool_list_updater.go create mode 100644 pkg/liquidity-source/solidly-v2/pool_simulator.go create mode 100644 pkg/liquidity-source/solidly-v2/pool_simulator_test.go create mode 100644 pkg/liquidity-source/solidly-v2/pool_tracker.go create mode 100644 pkg/liquidity-source/solidly-v2/types.go diff --git a/pkg/liquidity-source/curve/stable-ng/math.go b/pkg/liquidity-source/curve/stable-ng/math.go index ad561a29c..9a72f322a 100644 --- a/pkg/liquidity-source/curve/stable-ng/math.go +++ b/pkg/liquidity-source/curve/stable-ng/math.go @@ -1,6 +1,7 @@ package stableng import ( + "fmt" "math" "time" @@ -315,8 +316,11 @@ func (t *PoolSimulator) GetDx( ) (err error) { defer func() { if r := recover(); r != nil { - err = errors.Wrapf(ErrExecutionReverted, "recovered error: %v", r.(error)) - return + if recoveredError, ok := r.(error); ok { + err = errors.Wrapf(ErrExecutionReverted, "recovered error: %v", recoveredError) + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/deltaswap-v1/pool_simulator.go b/pkg/liquidity-source/deltaswap-v1/pool_simulator.go index 2f5147750..db629f903 100644 --- a/pkg/liquidity-source/deltaswap-v1/pool_simulator.go +++ b/pkg/liquidity-source/deltaswap-v1/pool_simulator.go @@ -2,6 +2,7 @@ package deltaswapv1 import ( "errors" + "fmt" "math/big" "github.com/KyberNetwork/blockchain-toolkit/integer" @@ -260,7 +261,11 @@ func (s *PoolSimulator) getAmountOut(amountIn, reserveIn, reserveOut, fee *uint2 func (s *PoolSimulator) getAmountIn(amountOut, reserveIn, reserveOut, fee *uint256.Int) (amountIn *uint256.Int, err error) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/dodo/classical/sell_helper.go b/pkg/liquidity-source/dodo/classical/sell_helper.go index 46d12f470..6d16e1ee9 100644 --- a/pkg/liquidity-source/dodo/classical/sell_helper.go +++ b/pkg/liquidity-source/dodo/classical/sell_helper.go @@ -1,6 +1,8 @@ package classical import ( + "fmt" + "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/dodo/libv1" @@ -23,7 +25,11 @@ func (p *PoolSimulator) querySellQuoteToken(amount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/dodo/classical/trader.go b/pkg/liquidity-source/dodo/classical/trader.go index 037790ab2..c1631c5a3 100644 --- a/pkg/liquidity-source/dodo/classical/trader.go +++ b/pkg/liquidity-source/dodo/classical/trader.go @@ -1,6 +1,8 @@ package classical import ( + "fmt" + "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/dodo/libv1" @@ -18,7 +20,11 @@ func (p *PoolSimulator) _querySellBaseToken(amount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() @@ -75,7 +81,11 @@ func (p *PoolSimulator) _queryBuyBaseToken(amount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/dodo/dpp/trader.go b/pkg/liquidity-source/dodo/dpp/trader.go index d582e20ea..b2e2dab2b 100644 --- a/pkg/liquidity-source/dodo/dpp/trader.go +++ b/pkg/liquidity-source/dodo/dpp/trader.go @@ -1,6 +1,8 @@ package dpp import ( + "fmt" + "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/dodo/libv2" @@ -15,7 +17,11 @@ func (p *PoolSimulator) querySellBase(payBaseAmount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() @@ -42,7 +48,11 @@ func (p *PoolSimulator) querySellQuote(payQuoteAmount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/dodo/dsp/trader.go b/pkg/liquidity-source/dodo/dsp/trader.go index edb21cbdf..6bd992025 100644 --- a/pkg/liquidity-source/dodo/dsp/trader.go +++ b/pkg/liquidity-source/dodo/dsp/trader.go @@ -1,6 +1,8 @@ package dsp import ( + "fmt" + "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/dodo/libv2" @@ -15,7 +17,11 @@ func (p *PoolSimulator) querySellBase(payBaseAmount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() @@ -42,7 +48,11 @@ func (p *PoolSimulator) querySellQuote(payQuoteAmount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/dodo/dvm/trader.go b/pkg/liquidity-source/dodo/dvm/trader.go index c467882a8..a4be1c4c6 100644 --- a/pkg/liquidity-source/dodo/dvm/trader.go +++ b/pkg/liquidity-source/dodo/dvm/trader.go @@ -1,6 +1,8 @@ package dvm import ( + "fmt" + "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/dodo/libv2" @@ -15,7 +17,11 @@ func (p *PoolSimulator) querySellBase(payBaseAmount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() @@ -42,7 +48,11 @@ func (p *PoolSimulator) querySellQuote(payQuoteAmount *uint256.Int) ( ) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/ringswap/pool_simulator.go b/pkg/liquidity-source/ringswap/pool_simulator.go index 73b738329..915a0d024 100644 --- a/pkg/liquidity-source/ringswap/pool_simulator.go +++ b/pkg/liquidity-source/ringswap/pool_simulator.go @@ -2,6 +2,7 @@ package ringswap import ( "errors" + "fmt" "math/big" "github.com/KyberNetwork/blockchain-toolkit/integer" @@ -295,7 +296,11 @@ func (s *PoolSimulator) getAmountOut(amountIn, reserveIn, reserveOut *uint256.In func (s *PoolSimulator) getAmountIn(amountOut, reserveIn, reserveOut *uint256.Int) (amountIn *uint256.Int, err error) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/solidly-v2/abis.go b/pkg/liquidity-source/solidly-v2/abis.go new file mode 100644 index 000000000..cd107efa5 --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/abis.go @@ -0,0 +1,34 @@ +package solidlyv2 + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var ( + poolABI abi.ABI + factoryABI abi.ABI +) + +func init() { + builder := []struct { + ABI *abi.ABI + data []byte + }{ + { + &poolABI, poolABIJson, + }, + { + &factoryABI, factoryABIJson, + }, + } + + for _, b := range builder { + var err error + *b.ABI, err = abi.JSON(bytes.NewReader(b.data)) + if err != nil { + panic(err) + } + } +} diff --git a/pkg/liquidity-source/solidly-v2/abis/Factory.json b/pkg/liquidity-source/solidly-v2/abis/Factory.json new file mode 100644 index 000000000..8a1df7014 --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/abis/Factory.json @@ -0,0 +1,486 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "OperatorStatus", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "stable", + "type": "bool" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPairs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allPairsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "childInterfaceAddress", + "outputs": [ + { + "internalType": "address", + "name": "_childInterface", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "childSubImplementationAddress", + "outputs": [ + { + "internalType": "address", + "name": "_childSubImplementation", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "createFees", + "outputs": [ + { + "internalType": "address", + "name": "fees", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "bool", + "name": "stable", + "type": "bool" + } + ], + "name": "createPair", + "outputs": [ + { + "internalType": "address", + "name": "pair", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "feesFactory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "name": "getPair", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governanceAddress", + "outputs": [ + { + "internalType": "address", + "name": "_governanceAddress", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_feesFactory", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interfaceSourceAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isOperator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isPair", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pairCodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "poolSpecificFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "poolSpecificFeesEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "setOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_state", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_pool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_fees", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_enabled", + "type": "bool" + } + ], + "name": "setPoolSpecificFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_stableFees", + "type": "uint256" + } + ], + "name": "setStableFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_volatileFees", + "type": "uint256" + } + ], + "name": "setVolatileFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stableFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_childInterfaceAddress", + "type": "address" + } + ], + "name": "updateChildInterfaceAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_childSubImplementationAddress", + "type": "address" + } + ], + "name": "updateChildSubImplementationAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "volatileFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "voter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/solidly-v2/abis/Pool.json b/pkg/liquidity-source/solidly-v2/abis/Pool.json new file mode 100644 index 000000000..19edc34e1 --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/abis/Pool.json @@ -0,0 +1,1079 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Claim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Fees", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "reserve0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reserve1", + "type": "uint256" + } + ], + "name": "Sync", + "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" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "blockTimestampLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "claimFees", + "outputs": [ + { + "internalType": "uint256", + "name": "claimed0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimed1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "name": "current", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "currentCumulativePrices", + "outputs": [ + { + "internalType": "uint256", + "name": "reserve0Cumulative", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve1Cumulative", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockTimestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factoryAddress", + "outputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fees", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + } + ], + "name": "getAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getReserves", + "outputs": [ + { + "internalType": "uint256", + "name": "_reserve0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_reserve1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_blockTimestampLast", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governanceAddress", + "outputs": [ + { + "internalType": "address", + "name": "_governanceAddress", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token0", + "type": "address" + }, + { + "internalType": "address", + "name": "_token1", + "type": "address" + }, + { + "internalType": "bool", + "name": "_stable", + "type": "bool" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "lastObservation", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve0Cumulative", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve1Cumulative", + "type": "uint256" + } + ], + "internalType": "struct BaseV2PairInterface.Observation", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "metadata", + "outputs": [ + { + "internalType": "uint256", + "name": "dec0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dec1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "r0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "r1", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "st", + "type": "bool" + }, + { + "internalType": "address", + "name": "t0", + "type": "address" + }, + { + "internalType": "address", + "name": "t1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_feeRatio", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "observationLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "observations", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve0Cumulative", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve1Cumulative", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "points", + "type": "uint256" + } + ], + "name": "prices", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "granularity", + "type": "uint256" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reserve0", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reserve0CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reserve1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reserve1CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "points", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "window", + "type": "uint256" + } + ], + "name": "sample", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "skim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "sync", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "syncFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/solidly-v2/config.go b/pkg/liquidity-source/solidly-v2/config.go new file mode 100644 index 000000000..002c2753b --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/config.go @@ -0,0 +1,8 @@ +package solidlyv2 + +type Config struct { + DexID string `json:"dexID"` + FeePrecision uint64 `json:"feePrecision"` + FactoryAddress string `json:"factoryAddress"` + NewPoolLimit int `json:"newPoolLimit"` +} diff --git a/pkg/liquidity-source/solidly-v2/constant.go b/pkg/liquidity-source/solidly-v2/constant.go new file mode 100644 index 000000000..99c40168c --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/constant.go @@ -0,0 +1,25 @@ +package solidlyv2 + +import "math/big" + +const ( + DexType = "solidly-v2" +) + +var ( + defaultGas = Gas{Swap: 227000} + + ZERO = big.NewInt(0) +) + +const ( + factoryMethodIsPaused = "isPaused" + factoryMethodAllPairs = "allPairs" + factoryMethodAllPairsLength = "allPairsLength" + factoryMethodStableFee = "stableFees" + factoryMethodVolatileFees = "volatileFees" + + poolMethodMetadata = "metadata" + poolMethodGetReserves = "getReserves" + poolMethodFeeRatio = "feeRatio" +) diff --git a/pkg/liquidity-source/solidly-v2/embed.go b/pkg/liquidity-source/solidly-v2/embed.go new file mode 100644 index 000000000..cf5c794e0 --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/embed.go @@ -0,0 +1,9 @@ +package solidlyv2 + +import _ "embed" + +//go:embed abis/Pool.json +var poolABIJson []byte + +//go:embed abis/Factory.json +var factoryABIJson []byte diff --git a/pkg/liquidity-source/solidly-v2/pool_list_updater.go b/pkg/liquidity-source/solidly-v2/pool_list_updater.go new file mode 100644 index 000000000..7891b69a0 --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/pool_list_updater.go @@ -0,0 +1,342 @@ +package solidlyv2 + +import ( + "context" + "errors" + "math/big" + "strings" + "time" + + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + velodromev2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/velodrome-v2" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util" +) + +type PoolsListUpdater struct { + config *Config + ethrpcClient *ethrpc.Client +} + +func NewPoolsListUpdater( + cfg *Config, + ethrpcClient *ethrpc.Client, +) *PoolsListUpdater { + return &PoolsListUpdater{ + config: cfg, + ethrpcClient: ethrpcClient, + } +} + +func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte) ([]entity.Pool, []byte, error) { + var ( + dexID = u.config.DexID + startTime = time.Now() + ) + + logger.WithFields(logger.Fields{"dex_id": dexID}).Info("Started getting new pools") + + ctx = util.NewContextWithTimestamp(ctx) + + poolFactoryData, err := u.getPoolFactoryData(ctx) + if err != nil { + logger. + WithFields(logger.Fields{"dex_id": dexID}). + Error("getPoolFactoryData failed") + + return nil, metadataBytes, err + } + + if poolFactoryData.IsPaused { + logger. + WithFields(logger.Fields{"dex_id": dexID}). + Info("factory is paused") + return nil, metadataBytes, nil + } + + offset, err := u.getOffset(metadataBytes) + if err != nil { + logger. + WithFields(logger.Fields{"dex_id": dexID, "err": err}). + Warn("getOffset failed") + } + + batchSize := getBatchSize(int(poolFactoryData.AllPairsLength.Int64()), u.config.NewPoolLimit, offset) + + poolAddresses, err := u.listPoolAddresses(ctx, offset, batchSize) + if err != nil { + logger. + WithFields(logger.Fields{"dex_id": dexID, "err": err}). + Error("listPoolAddresses failed") + + return nil, metadataBytes, err + } + + pools, err := u.initPools(ctx, poolAddresses, poolFactoryData) + if err != nil { + logger. + WithFields(logger.Fields{"dex_id": dexID, "err": err}). + Error("initPools failed") + + return nil, metadataBytes, err + } + + newMetadataBytes, err := u.newMetadata(offset + batchSize) + if err != nil { + logger. + WithFields(logger.Fields{"dex_id": dexID, "err": err}). + Error("newMetadata failed") + + return nil, metadataBytes, err + } + + logger. + WithFields( + logger.Fields{ + "dex_id": dexID, + "pools_len": len(pools), + "offset": offset, + "duration_ms": time.Since(startTime).Milliseconds(), + }, + ). + Info("Finished getting new pools") + + return pools, newMetadataBytes, nil +} + +// getPoolFactoryData gets number of pairs from the factory contracts +func (u *PoolsListUpdater) getPoolFactoryData(ctx context.Context) (velodromev2.PoolFactoryData, error) { + pairFactoryData := velodromev2.PoolFactoryData{} + + getAllPairsLengthRequest := u.ethrpcClient.NewRequest().SetContext(ctx) + + getAllPairsLengthRequest.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: u.config.FactoryAddress, + Method: factoryMethodAllPairsLength, + Params: nil, + }, []interface{}{&pairFactoryData.AllPairsLength}) + + if _, err := getAllPairsLengthRequest.TryBlockAndAggregate(); err != nil { + return velodromev2.PoolFactoryData{}, err + } + + return pairFactoryData, nil +} + +// getOffset gets index of the last pair that is fetched +func (u *PoolsListUpdater) getOffset(metadataBytes []byte) (int, error) { + if len(metadataBytes) == 0 { + return 0, nil + } + + var metadata velodromev2.PoolsListUpdaterMetadata + if err := json.Unmarshal(metadataBytes, &metadata); err != nil { + return 0, err + } + + return metadata.Offset, nil +} + +// listPoolAddresses lists address of pairs from offset +func (u *PoolsListUpdater) listPoolAddresses(ctx context.Context, offset int, batchSize int) ([]common.Address, error) { + listPoolAddressesResult := make([]common.Address, batchSize) + + listPoolAddressesRequest := u.ethrpcClient.NewRequest().SetContext(ctx) + for i := 0; i < batchSize; i++ { + index := big.NewInt(int64(offset + i)) + + listPoolAddressesRequest.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: u.config.FactoryAddress, + Method: factoryMethodAllPairs, + Params: []interface{}{index}, + }, []interface{}{&listPoolAddressesResult[i]}) + } + + resp, err := listPoolAddressesRequest.TryAggregate() + if err != nil { + return nil, err + } + + var poolAddresses []common.Address + for i, isSuccess := range resp.Result { + if !isSuccess { + continue + } + + poolAddresses = append(poolAddresses, listPoolAddressesResult[i]) + } + + return poolAddresses, nil +} + +// initPools fetches token data and initializes pools +func (u *PoolsListUpdater) initPools( + ctx context.Context, + poolAddresses []common.Address, + poolFactoryData velodromev2.PoolFactoryData, +) ([]entity.Pool, error) { + metadataList, stableFee, volatileFee, blockNumber, err := u.listPoolData(ctx, poolAddresses) + if err != nil { + return nil, err + } + + pools := make([]entity.Pool, 0, len(poolAddresses)) + for i, poolAddress := range poolAddresses { + fee := volatileFee + if metadataList[i].St { + fee = stableFee + } + + extra, err := u.newExtra(poolFactoryData.IsPaused, fee) + if err != nil { + logger. + WithFields(logger.Fields{"pool_address": poolAddress, "dex_id": u.config.DexID, "err": err}). + Error("newExtra failed") + continue + } + + staticExtra, err := u.newStaticExtra(metadataList[i]) + if err != nil { + logger. + WithFields(logger.Fields{"pool_address": poolAddress, "dex_id": u.config.DexID, "err": err}). + Error("newStaticExtra failed") + continue + } + + newPool := entity.Pool{ + Address: strings.ToLower(poolAddress.Hex()), + Exchange: u.config.DexID, + Type: DexType, + BlockNumber: blockNumber.Uint64(), + Timestamp: time.Now().Unix(), + Reserves: []string{metadataList[i].R0.String(), metadataList[i].R1.String()}, + Tokens: []*entity.PoolToken{ + { + Address: strings.ToLower(metadataList[i].T0.String()), + Swappable: true, + }, + { + Address: strings.ToLower(metadataList[i].T1.String()), + Swappable: true, + }, + }, + Extra: string(extra), + StaticExtra: string(staticExtra), + } + + pools = append(pools, newPool) + } + + return pools, nil +} + +// listPairTokens receives list of pair addresses and returns their token0 and token1 +func (u *PoolsListUpdater) listPoolData( + ctx context.Context, + poolAddresses []common.Address, +) ([]PoolMetadata, *big.Int, *big.Int, *big.Int, error) { + var ( + stableFee = ZERO + volatileFees = ZERO + + poolMetadataList = make([]PoolMetadata, len(poolAddresses)) + ) + + listPoolMetadataRequest := u.ethrpcClient.NewRequest().SetContext(ctx) + + listPoolMetadataRequest.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: u.config.FactoryAddress, + Method: factoryMethodStableFee, + Params: []interface{}{}, + }, []interface{}{&stableFee}) + listPoolMetadataRequest.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: u.config.FactoryAddress, + Method: factoryMethodVolatileFees, + Params: []interface{}{}, + }, []interface{}{&volatileFees}) + + for i, poolAddress := range poolAddresses { + listPoolMetadataRequest.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress.Hex(), + Method: poolMethodMetadata, + Params: nil, + }, []interface{}{&poolMetadataList[i]}) + } + + resp, err := listPoolMetadataRequest.Aggregate() + if err != nil { + return nil, nil, nil, nil, err + } + + return poolMetadataList, stableFee, volatileFees, resp.BlockNumber, nil +} + +func (u *PoolsListUpdater) newMetadata(newOffset int) ([]byte, error) { + metadata := velodromev2.PoolsListUpdaterMetadata{ + Offset: newOffset, + } + + metadataBytes, err := json.Marshal(metadata) + if err != nil { + return nil, err + } + + return metadataBytes, nil +} + +func (u *PoolsListUpdater) newExtra(isPaused bool, fee *big.Int) ([]byte, error) { + extra := velodromev2.PoolExtra{ + IsPaused: isPaused, + Fee: fee.Uint64(), + } + + return json.Marshal(extra) +} + +func (u *PoolsListUpdater) newStaticExtra(poolMetadata PoolMetadata) ([]byte, error) { + decimal0, overflow := uint256.FromBig(poolMetadata.Dec0) + if overflow { + return nil, errors.New("dec0 overflow") + } + + decimal1, overflow := uint256.FromBig(poolMetadata.Dec1) + if overflow { + return nil, errors.New("dec1 overflow") + } + + staticExtra := velodromev2.PoolStaticExtra{ + FeePrecision: u.config.FeePrecision, + Decimal0: decimal0, + Decimal1: decimal1, + Stable: poolMetadata.St, + } + + return json.Marshal(staticExtra) +} + +// getBatchSize +// @params length number of pairs (factory tracked) +// @params limit number of pairs to be fetched in one run +// @params offset index of the last pair has been fetched +// @returns batchSize +func getBatchSize(length int, limit int, offset int) int { + if offset == length { + return 0 + } + + if offset+limit >= length { + return length - offset + } + + return limit +} diff --git a/pkg/liquidity-source/solidly-v2/pool_simulator.go b/pkg/liquidity-source/solidly-v2/pool_simulator.go new file mode 100644 index 000000000..671e0ffac --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/pool_simulator.go @@ -0,0 +1,440 @@ +package solidlyv2 + +import ( + "errors" + "fmt" + "math/big" + + "github.com/KyberNetwork/blockchain-toolkit/integer" + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/goccy/go-json" + "github.com/holiman/uint256" + "github.com/samber/lo" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + velodromev2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/velodrome-v2" + + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + utils "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" +) + +var ( + ErrPoolIsPaused = errors.New("pool is paused") + ErrInvalidAmountIn = errors.New("invalid amountIn") + ErrInvalidAmountOut = errors.New("invalid amountOut") + ErrInvalidReserve = errors.New("invalid reserve") + ErrInsufficientOutputAmount = errors.New("INSUFFICIENT_OUTPUT_AMOUNT") + ErrInsufficientInputAmount = errors.New("INSUFFICIENT_INPUT_AMOUNT") + ErrInsufficientLiquidity = errors.New("INSUFFICIENT_LIQUIDITY") + ErrK = errors.New("K") + ErrY = errors.New("!Y") + ErrUnimplemented = errors.New("unimplemented") +) + +type ( + PoolSimulator struct { + poolpkg.Pool + + stable bool + decimals0 *uint256.Int + decimals1 *uint256.Int + feePrecision *uint256.Int + + isPaused bool + fee *uint256.Int + + gas Gas + } + + Gas struct { + Swap int64 + } +) + +func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { + var staticExtra velodromev2.PoolStaticExtra + if err := json.Unmarshal([]byte(entityPool.StaticExtra), &staticExtra); err != nil { + return nil, err + } + + var extra velodromev2.PoolExtra + if err := json.Unmarshal([]byte(entityPool.Extra), &extra); err != nil { + return nil, err + } + + return &PoolSimulator{ + Pool: poolpkg.Pool{Info: poolpkg.PoolInfo{ + Address: entityPool.Address, + ReserveUsd: entityPool.ReserveUsd, + Exchange: entityPool.Exchange, + Type: entityPool.Type, + Tokens: lo.Map(entityPool.Tokens, func(item *entity.PoolToken, index int) string { return item.Address }), + Reserves: lo.Map(entityPool.Reserves, func(item string, index int) *big.Int { return utils.NewBig(item) }), + BlockNumber: entityPool.BlockNumber, + }}, + + stable: staticExtra.Stable, + decimals0: staticExtra.Decimal0, + decimals1: staticExtra.Decimal1, + feePrecision: uint256.NewInt(staticExtra.FeePrecision), + + isPaused: extra.IsPaused, + fee: uint256.NewInt(extra.Fee), + + gas: defaultGas, + }, nil +} + +func (s *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { + if s.isPaused { + return nil, ErrPoolIsPaused + } + + amountIn, overflow := uint256.FromBig(params.TokenAmountIn.Amount) + if overflow { + return nil, ErrInvalidAmountIn + } + + feeAmount := new(uint256.Int).Div(new(uint256.Int).Mul(amountIn, s.fee), s.feePrecision) + amountInAfterFee := new(uint256.Int).Sub(amountIn, feeAmount) + + amountOut, err := s.getAmountOut( + amountInAfterFee, + params.TokenAmountIn.Token, + ) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountOutResult{ + TokenAmountOut: &poolpkg.TokenAmount{Token: params.TokenOut, Amount: amountOut.ToBig()}, + Fee: &poolpkg.TokenAmount{Token: params.TokenAmountIn.Token, Amount: feeAmount.ToBig()}, + Gas: s.gas.Swap, + }, nil +} + +func (s *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { + if s.isPaused { + return nil, ErrPoolIsPaused + } + + amountOut, overflow := uint256.FromBig(params.TokenAmountOut.Amount) + if overflow { + return nil, ErrInvalidAmountOut + } + + amountIn, err := s.getAmountIn( + amountOut, + params.TokenAmountOut.Token, + ) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{Token: params.TokenIn, Amount: amountIn.ToBig()}, + // NOTE: we don't use fee to update balance so that we don't need to calculate it. I put it number.Zero to avoid null pointer exception + Fee: &poolpkg.TokenAmount{Token: params.TokenAmountOut.Token, Amount: integer.Zero()}, + Gas: s.gas.Swap, + }, nil +} + +func (s *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { + indexIn := s.GetTokenIndex(params.TokenAmountIn.Token) + indexOut := s.GetTokenIndex(params.TokenAmountOut.Token) + if indexIn < 0 || indexOut < 0 { + return + } + s.Pool.Info.Reserves[indexIn] = new(big.Int).Sub(new(big.Int).Add(s.Pool.Info.Reserves[indexIn], params.TokenAmountIn.Amount), params.Fee.Amount) + s.Pool.Info.Reserves[indexOut] = new(big.Int).Sub(s.Pool.Info.Reserves[indexOut], params.TokenAmountOut.Amount) +} + +func (s *PoolSimulator) GetMetaInfo(_ string, _ string) interface{} { + return velodromev2.PoolMeta{ + Fee: s.fee.Uint64(), + FeePrecision: s.feePrecision.Uint64(), + BlockNumber: s.Pool.Info.BlockNumber, + } +} + +func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { + cloned := *p + cloned.Info.Reserves = lo.Map(p.Info.Reserves, func(v *big.Int, i int) *big.Int { + return new(big.Int).Set(v) + }) + return &cloned +} + +func (s *PoolSimulator) getAmountOut( + amountIn *uint256.Int, + tokenIn string, +) (*uint256.Int, error) { + reserve0, overflow := uint256.FromBig(s.Info.Reserves[0]) + if overflow { + return nil, ErrInvalidReserve + } + + reserve1, overflow := uint256.FromBig(s.Info.Reserves[1]) + if overflow { + return nil, ErrInvalidReserve + } + + amountOut, err := s._getAmountOut(amountIn, tokenIn, reserve0, reserve1) + if err != nil { + return nil, err + } + + if amountOut.Cmp(number.Zero) <= 0 { + return nil, ErrInsufficientOutputAmount + } + + if tokenIn == s.Info.Tokens[0] && amountOut.Cmp(reserve1) > 0 { + return nil, ErrInsufficientLiquidity + } + + if tokenIn == s.Info.Tokens[1] && amountOut.Cmp(reserve0) > 0 { + return nil, ErrInsufficientLiquidity + } + + var balance0, balance1 *uint256.Int + if tokenIn == s.Info.Tokens[0] { + balance0 = new(uint256.Int).Add(reserve0, amountIn) + balance1 = new(uint256.Int).Sub(reserve1, amountOut) + } else { + balance0 = new(uint256.Int).Sub(reserve0, amountOut) + balance1 = new(uint256.Int).Add(reserve1, amountIn) + } + + if s._k(balance0, balance1).Cmp(s._k(reserve0, reserve1)) < 0 { + return nil, ErrK + } + + return amountOut, nil +} + +func (s *PoolSimulator) _getAmountOut( + amountIn *uint256.Int, + tokenIn string, + _reserve0 *uint256.Int, + _reserve1 *uint256.Int, +) (*uint256.Int, error) { + if s.stable { + xy := s._k(_reserve0, _reserve1) + _reserve0 = new(uint256.Int).Div(new(uint256.Int).Mul(_reserve0, number.Number_1e18), s.decimals0) + _reserve1 = new(uint256.Int).Div(new(uint256.Int).Mul(_reserve1, number.Number_1e18), s.decimals1) + + if tokenIn == s.Info.Tokens[0] { + amountIn = new(uint256.Int).Div(new(uint256.Int).Mul(amountIn, number.Number_1e18), s.decimals0) + y, err := s._get_y(new(uint256.Int).Add(amountIn, _reserve0), xy, _reserve1) + if err != nil { + return nil, err + } + + y = new(uint256.Int).Sub(_reserve1, y) + + return new(uint256.Int).Div(new(uint256.Int).Mul(y, s.decimals1), number.Number_1e18), nil + } + + amountIn = new(uint256.Int).Div(new(uint256.Int).Mul(amountIn, number.Number_1e18), s.decimals1) + y, err := s._get_y(new(uint256.Int).Add(amountIn, _reserve1), xy, _reserve0) + if err != nil { + return nil, err + } + + y = new(uint256.Int).Sub(_reserve0, y) + return new(uint256.Int).Div(new(uint256.Int).Mul(y, s.decimals0), number.Number_1e18), nil + } + + if tokenIn == s.Info.Tokens[0] { + return new(uint256.Int).Div(new(uint256.Int).Mul(amountIn, _reserve1), new(uint256.Int).Add(_reserve0, amountIn)), nil + } + + return new(uint256.Int).Div(new(uint256.Int).Mul(amountIn, _reserve0), new(uint256.Int).Add(_reserve1, amountIn)), nil +} + +func (s *PoolSimulator) getAmountIn( + amountOut *uint256.Int, + tokenOut string, +) (*uint256.Int, error) { + reserve0, overflow := uint256.FromBig(s.Info.Reserves[0]) + if overflow { + return nil, ErrInvalidReserve + } + + reserve1, overflow := uint256.FromBig(s.Info.Reserves[1]) + if overflow { + return nil, ErrInvalidReserve + } + + if tokenOut == s.Info.Tokens[0] && amountOut.Cmp(reserve0) > 0 { + return nil, ErrInsufficientLiquidity + } + + if tokenOut == s.Info.Tokens[1] && amountOut.Cmp(reserve1) > 0 { + return nil, ErrInsufficientLiquidity + } + + amountIn, err := s._getAmountIn(amountOut, tokenOut, reserve0, reserve1) + if err != nil { + return nil, err + } + + if amountIn.Cmp(number.Zero) <= 0 { + return nil, ErrInsufficientInputAmount + } + + var balance0, balance1 *uint256.Int + if tokenOut == s.Info.Tokens[0] { + balance0 = new(uint256.Int).Sub(reserve0, amountOut) + balance1 = new(uint256.Int).Add(reserve1, amountIn) + } else { + balance0 = new(uint256.Int).Add(reserve0, amountIn) + balance1 = new(uint256.Int).Sub(reserve1, amountOut) + } + + if s._k(balance0, balance1).Cmp(s._k(reserve0, reserve1)) < 0 { + return nil, ErrK + } + + return amountIn, nil +} + +func (s *PoolSimulator) _getAmountIn( + amountOut *uint256.Int, + tokenOut string, + _reserve0 *uint256.Int, + _reserve1 *uint256.Int, +) (amountIn *uint256.Int, err error) { + if s.stable { + return nil, ErrUnimplemented + } + + defer func() { + if r := recover(); r != nil { + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } + } + }() + + var reserveIn, reserveOut *uint256.Int + if tokenOut == s.Info.Tokens[0] { + reserveIn = _reserve1 + reserveOut = _reserve0 + } else { + reserveIn = _reserve0 + reserveOut = _reserve1 + } + + numerator := velodromev2.SafeMul( + velodromev2.SafeMul(reserveIn, amountOut), + s.feePrecision, + ) + denominator := velodromev2.SafeMul( + velodromev2.SafeSub(reserveOut, amountOut), + velodromev2.SafeSub(s.feePrecision, s.fee), + ) + + return velodromev2.SafeAdd(new(uint256.Int).Div(numerator, denominator), number.Number_1), nil +} + +func (s *PoolSimulator) _k(x *uint256.Int, y *uint256.Int) *uint256.Int { + if s.stable { + _x := new(uint256.Int).Div(new(uint256.Int).Mul(x, number.Number_1e18), s.decimals0) + _y := new(uint256.Int).Div(new(uint256.Int).Mul(y, number.Number_1e18), s.decimals1) + _a := new(uint256.Int).Div(new(uint256.Int).Mul(_x, _y), number.Number_1e18) + _b := new(uint256.Int).Add( + new(uint256.Int).Div( + new(uint256.Int).Mul(_x, _x), + number.Number_1e18, + ), + new(uint256.Int).Div( + new(uint256.Int).Mul(_y, _y), + number.Number_1e18, + ), + ) + return new(uint256.Int).Div(new(uint256.Int).Mul(_a, _b), number.Number_1e18) + } + + return new(uint256.Int).Mul(x, y) +} + +func (s *PoolSimulator) _get_y(x0 *uint256.Int, xy *uint256.Int, y *uint256.Int) (*uint256.Int, error) { + for i := 0; i < 255; i++ { + k := _f(x0, y) + + if k.Cmp(xy) < 0 { + dy := new(uint256.Int).Div( + new(uint256.Int).Mul(new(uint256.Int).Sub(xy, k), number.Number_1e18), + _d(x0, y), + ) + + if dy.Cmp(number.Zero) == 0 { + if k.Cmp(xy) == 0 { + return y, nil + } + + if s._k(x0, new(uint256.Int).Add(y, number.Number_1)).Cmp(xy) > 0 { + return new(uint256.Int).Add(y, number.Number_1), nil + } + + dy = number.Number_1 + } + + y = new(uint256.Int).Add(y, dy) + } else { + dy := new(uint256.Int).Div( + new(uint256.Int).Mul(new(uint256.Int).Sub(k, xy), number.Number_1e18), + _d(x0, y), + ) + + if dy.Cmp(number.Zero) == 0 { + if k.Cmp(xy) == 0 || _f(x0, new(uint256.Int).Sub(y, number.Number_1)).Cmp(xy) < 0 { + return y, nil + } + + dy = number.Number_1 + } + + y = new(uint256.Int).Sub(y, dy) + } + } + + return nil, ErrY +} + +func _f(x0 *uint256.Int, y *uint256.Int) *uint256.Int { + _a := new(uint256.Int).Div(new(uint256.Int).Mul(x0, y), number.Number_1e18) + _b := new(uint256.Int).Add( + new(uint256.Int).Div( + new(uint256.Int).Mul(x0, x0), + number.Number_1e18, + ), + new(uint256.Int).Div( + new(uint256.Int).Mul(y, y), + number.Number_1e18, + ), + ) + return new(uint256.Int).Div(new(uint256.Int).Mul(_a, _b), number.Number_1e18) +} + +func _d(x0 *uint256.Int, y *uint256.Int) *uint256.Int { + return new(uint256.Int).Add( + new(uint256.Int).Div( + new(uint256.Int).Mul( + number.Number_3, + new(uint256.Int).Mul( + x0, + new(uint256.Int).Div(new(uint256.Int).Mul(y, y), number.Number_1e18), + ), + ), + number.Number_1e18, + ), + new(uint256.Int).Mul( + new(uint256.Int).Div(new(uint256.Int).Mul(x0, x0), number.Number_1e18), + new(uint256.Int).Div(x0, number.Number_1e18), + ), + ) +} diff --git a/pkg/liquidity-source/solidly-v2/pool_simulator_test.go b/pkg/liquidity-source/solidly-v2/pool_simulator_test.go new file mode 100644 index 000000000..2062f13e0 --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/pool_simulator_test.go @@ -0,0 +1,357 @@ +package solidlyv2 + +import ( + "fmt" + "math/big" + "testing" + + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + utils "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" +) + +func TestNewPoolSimulator(t *testing.T) { + t.Run("it should init pool simulator correctly", func(t *testing.T) { + entityPool := entity.Pool{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Exchange: "solidly-v2", + Type: "solidly-v2", + Timestamp: 1700031705, + Reserves: []string{"2455334631692", "48474602535901272544258453"}, + Tokens: []*entity.PoolToken{ + {Address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", Swappable: true}, + {Address: "0x9560e827af36c94d2ac33a39bce1fe78631088db", Swappable: true}, + }, + Extra: "{\"isPaused\":true,\"fee\":5}", + StaticExtra: "{\"feePrecision\":10000,\"decimal0\":\"0xf4240\",\"decimal1\":\"0xde0b6b3a7640000\",\"stable\":false}", + } + + poolSimulator, err := NewPoolSimulator(entityPool) + + assert.Nil(t, err) + assert.True(t, poolSimulator.isPaused) + assert.False(t, poolSimulator.stable) + assert.EqualValues(t, uint64(5), poolSimulator.fee.Uint64()) + assert.Zero(t, number.NewUint256("1000000").Cmp(poolSimulator.decimals0)) + assert.Zero(t, number.NewUint256("1000000000000000000").Cmp(poolSimulator.decimals1)) + + }) +} + +// TestPoolSimulator_getAmountOut +// [volatile][1to0]: https://optimistic.etherscan.io/address/0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5 +// [volatile][0to1]: https://optimistic.etherscan.io/address/0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5 +// [stable][1to0]: https://optimistic.etherscan.io/address/0x1ad06ca54de04dbe9e2817f4c13ecb406dcbeaf0 +// [stable][0to1]: https://optimistic.etherscan.io/address/0x1ad06ca54de04dbe9e2817f4c13ecb406dcbeaf0 +func TestPoolSimulator_getAmountOut(t *testing.T) { + testCases := []struct { + name string + poolSimulator PoolSimulator + tokenAmountIn poolpkg.TokenAmount + tokenOut string + expectedAmountOut *big.Int + expectedFee *big.Int + calcInThreshold int64 + }{ + { + name: "[volatile][0to1] it should return correct amount", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Tokens: []string{"0x7F5c764cBc14f9669B88837ca1490cCa17c31607", "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db"}, + Reserves: []*big.Int{utils.NewBig10("2458244583526"), utils.NewBig10("48437610421475879640774762")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(100), + feePrecision: uint256.NewInt(10000), + }, + tokenAmountIn: poolpkg.TokenAmount{Token: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", Amount: utils.NewBig10("33762029")}, + tokenOut: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db", + expectedAmountOut: utils.NewBig10("658590483453928603087"), + expectedFee: utils.NewBig10("337620"), + calcInThreshold: 10, + }, + { + name: "[volatile][1to0] it should return correct amount", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Tokens: []string{"0x7F5c764cBc14f9669B88837ca1490cCa17c31607", "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db"}, + Reserves: []*big.Int{utils.NewBig10("2458244583526"), utils.NewBig10("48437697487082485250805965")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(100), + feePrecision: uint256.NewInt(10000), + }, + tokenAmountIn: poolpkg.TokenAmount{Token: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db", Amount: utils.NewBig10("4843761042147587964077")}, + tokenOut: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", + expectedAmountOut: utils.NewBig10("243341685"), + expectedFee: utils.NewBig10("48437610421475879640"), + calcInThreshold: 1, + }, + { + name: "[stable][1to0] it should return correct amount", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x1ad06ca54de04dbe9e2817f4c13ecb406dcbeaf0", + Tokens: []string{"0x3e29d3a9316dab217754d13b28646b76607c5f04", "0x6806411765af15bddd26f8f544a34cc40cb9838b"}, + Reserves: []*big.Int{utils.NewBig10("165363502891169888414"), utils.NewBig10("70707320014274856246")}, + }, + }, + isPaused: false, + stable: true, + decimals0: number.NewUint256("1000000000000000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(5), + feePrecision: uint256.NewInt(10000), + }, + tokenAmountIn: poolpkg.TokenAmount{Token: "0x6806411765af15bddd26f8f544a34cc40cb9838b", Amount: utils.NewBig10("7070085324939016")}, + tokenOut: "0x3e29d3a9316dab217754d13b28646b76607c5f04", + expectedAmountOut: utils.NewBig10("8040168956751976"), + expectedFee: utils.NewBig10("3535042662469"), + calcInThreshold: 10, + }, + { + name: "[stable][0to1] it should return correct amount", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x1ad06ca54de04dbe9e2817f4c13ecb406dcbeaf0", + Tokens: []string{"0x3e29d3a9316dab217754d13b28646b76607c5f04", "0x6806411765af15bddd26f8f544a34cc40cb9838b"}, + Reserves: []*big.Int{utils.NewBig10("165363502891169888414"), utils.NewBig10("70707320014274856246")}, + }, + }, + isPaused: false, + stable: true, + decimals0: number.NewUint256("1000000000000000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(5), + feePrecision: uint256.NewInt(10000), + }, + tokenAmountIn: poolpkg.TokenAmount{Token: "0x3e29d3a9316dab217754d13b28646b76607c5f04", Amount: utils.NewBig10("7070085324939016")}, + tokenOut: "0x6806411765af15bddd26f8f544a34cc40cb9838b", + expectedAmountOut: utils.NewBig10("6210478971090850"), + expectedFee: utils.NewBig10("3535042662469"), + calcInThreshold: 10, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return tc.poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{TokenAmountIn: tc.tokenAmountIn, TokenOut: tc.tokenOut}) + }) + + if tc.expectedAmountOut != nil { + assert.Nil(t, err) + assert.Zero(t, tc.expectedAmountOut.Cmp(result.TokenAmountOut.Amount)) + assert.Zero(t, tc.expectedFee.Cmp(result.Fee.Amount)) + } + }) + } +} + +func TestPoolSimulator_getAmountIn(t *testing.T) { + testCases := []struct { + name string + poolSimulator PoolSimulator + tokenAmountOut poolpkg.TokenAmount + tokenIn string + expectedAmountIn *big.Int + expectedFee *big.Int + }{ + { + name: "[volatile][1to0] it should return correct amount", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Tokens: []string{"0x7F5c764cBc14f9669B88837ca1490cCa17c31607", "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db"}, + Reserves: []*big.Int{utils.NewBig10("2458244583526"), utils.NewBig10("48437610421475879640774762")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(100), + feePrecision: uint256.NewInt(10000), + }, + tokenAmountOut: poolpkg.TokenAmount{Token: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", Amount: utils.NewBig10("33762029")}, + tokenIn: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db", + expectedAmountIn: utils.NewBig10("671980897831826369735"), + expectedFee: utils.NewBig10("0"), + }, + { + name: "[volatile][0to1] it should return correct amount", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Tokens: []string{"0x7F5c764cBc14f9669B88837ca1490cCa17c31607", "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db"}, + Reserves: []*big.Int{utils.NewBig10("2458244583526"), utils.NewBig10("48437697487082485250805965")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(100), + feePrecision: uint256.NewInt(10000), + }, + tokenAmountOut: poolpkg.TokenAmount{Token: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db", Amount: utils.NewBig10("4843761042147587964077")}, + tokenIn: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", + expectedAmountIn: utils.NewBig10("248331921"), + expectedFee: utils.NewBig10("0"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return tc.poolSimulator.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tc.tokenAmountOut, + TokenIn: tc.tokenIn, + }) + }) + + if tc.expectedAmountIn != nil { + assert.Nil(t, err) + assert.Equalf(t, tc.expectedAmountIn, result.TokenAmountIn.Amount, "expected amount in: %s, got: %s", tc.expectedAmountIn.String(), result.TokenAmountIn.Amount.String()) + assert.Equalf(t, tc.expectedFee, result.Fee.Amount, "expected fee: %s, got: %s", tc.expectedFee.String(), result.Fee.Amount.String()) + } + }) + } +} + +func TestPoolSimulator_UpdateBalance(t *testing.T) { + testCases := []struct { + name string + poolSimulator PoolSimulator + params poolpkg.UpdateBalanceParams + expectedReserves []*big.Int + }{ + { + name: "[volatile][1to0] it should update reserve correctly", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Tokens: []string{"0x7F5c764cBc14f9669B88837ca1490cCa17c31607", "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db"}, + Reserves: []*big.Int{utils.NewBig10("2458244583526"), utils.NewBig10("48437610421475879640774762")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(100), + feePrecision: uint256.NewInt(10000), + }, + params: poolpkg.UpdateBalanceParams{ + TokenAmountOut: poolpkg.TokenAmount{Token: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", Amount: utils.NewBig10("243341685")}, + TokenAmountIn: poolpkg.TokenAmount{Token: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db", Amount: utils.NewBig10("4843761042147587964077")}, + Fee: poolpkg.TokenAmount{Token: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", Amount: utils.NewBig10("337620")}, + }, + expectedReserves: []*big.Int{utils.NewBig10("2458001241841"), utils.NewBig10("48442454182518027228401219")}, + }, + { + name: "[volatile][0to1] it should update reserve correctly", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x8134a2fdc127549480865fb8e5a9e8a8a95a54c5", + Tokens: []string{"0x7F5c764cBc14f9669B88837ca1490cCa17c31607", "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db"}, + Reserves: []*big.Int{utils.NewBig10("2458244583526"), utils.NewBig10("48437610421475879640774762")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(100), + feePrecision: uint256.NewInt(10000), + }, + params: poolpkg.UpdateBalanceParams{ + TokenAmountOut: poolpkg.TokenAmount{Token: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db", Amount: utils.NewBig10("658590483453928603087")}, + TokenAmountIn: poolpkg.TokenAmount{Token: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", Amount: utils.NewBig10("33762029")}, + Fee: poolpkg.TokenAmount{Token: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", Amount: utils.NewBig10("337620")}, + }, + expectedReserves: []*big.Int{utils.NewBig10("2458278007935"), utils.NewBig10("48436951830992425712171675")}, + }, + { + name: "[stable][1to0] it should update reserve correctly", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x1ad06ca54de04dbe9e2817f4c13ecb406dcbeaf0", + Tokens: []string{"0x3e29d3a9316dab217754d13b28646b76607c5f04", "0x6806411765af15bddd26f8f544a34cc40cb9838b"}, + Reserves: []*big.Int{utils.NewBig10("165363502891169888414"), utils.NewBig10("70707320014274856246")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000000000000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(5), + feePrecision: uint256.NewInt(10000), + }, + params: poolpkg.UpdateBalanceParams{ + TokenAmountOut: poolpkg.TokenAmount{Token: "0x3e29d3a9316dab217754d13b28646b76607c5f04", Amount: utils.NewBig10("8040168956751976")}, + TokenAmountIn: poolpkg.TokenAmount{Token: "0x6806411765af15bddd26f8f544a34cc40cb9838b", Amount: utils.NewBig10("7070085324939016")}, + Fee: poolpkg.TokenAmount{Token: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", Amount: utils.NewBig10("3535042662469")}, + }, + expectedReserves: []*big.Int{utils.NewBig10("165355462722213136438"), utils.NewBig10("70714386564557132793")}, + }, + { + name: "[stable][0to1] it should update reserve correctly", + poolSimulator: PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Address: "0x1ad06ca54de04dbe9e2817f4c13ecb406dcbeaf0", + Tokens: []string{"0x3e29d3a9316dab217754d13b28646b76607c5f04", "0x6806411765af15bddd26f8f544a34cc40cb9838b"}, + Reserves: []*big.Int{utils.NewBig10("165363502891169888414"), utils.NewBig10("70707320014274856246")}, + }, + }, + isPaused: false, + stable: false, + decimals0: number.NewUint256("1000000000000000000"), + decimals1: number.NewUint256("1000000000000000000"), + fee: uint256.NewInt(5), + feePrecision: uint256.NewInt(10000), + }, + params: poolpkg.UpdateBalanceParams{ + TokenAmountOut: poolpkg.TokenAmount{Token: "0x6806411765af15bddd26f8f544a34cc40cb9838b", Amount: utils.NewBig10("6210478971090850")}, + TokenAmountIn: poolpkg.TokenAmount{Token: "0x3e29d3a9316dab217754d13b28646b76607c5f04", Amount: utils.NewBig10("7070085324939016")}, + Fee: poolpkg.TokenAmount{Token: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", Amount: utils.NewBig10("3535042662469")}, + }, + expectedReserves: []*big.Int{utils.NewBig10("165370569441452164961"), utils.NewBig10("70701109535303765396")}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tc.poolSimulator.UpdateBalance(tc.params) + fmt.Println("reserves, reserves1", tc.poolSimulator.Info.Reserves[0].String(), tc.poolSimulator.Info.Reserves[1]) + + assert.Zero(t, tc.expectedReserves[0].Cmp(tc.poolSimulator.Info.Reserves[0])) + assert.Zero(t, tc.expectedReserves[1].Cmp(tc.poolSimulator.Info.Reserves[1])) + }) + } +} diff --git a/pkg/liquidity-source/solidly-v2/pool_tracker.go b/pkg/liquidity-source/solidly-v2/pool_tracker.go new file mode 100644 index 000000000..fe7ffaaaa --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/pool_tracker.go @@ -0,0 +1,154 @@ +package solidlyv2 + +import ( + "context" + "math/big" + "time" + + "github.com/KyberNetwork/ethrpc" + "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient/gethclient" + "github.com/goccy/go-json" + + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + velodromev2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/velodrome-v2" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" +) + +type PoolTracker struct { + config *Config + ethrpcClient *ethrpc.Client +} + +func NewPoolTracker( + config *Config, + ethrpcClient *ethrpc.Client, +) (*PoolTracker, error) { + return &PoolTracker{ + config: config, + ethrpcClient: ethrpcClient, + }, nil +} + +func (t *PoolTracker) GetNewPoolState( + ctx context.Context, + p entity.Pool, + params pool.GetNewPoolStateParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, params, nil) +} + +func (t *PoolTracker) GetNewPoolStateWithOverrides( + ctx context.Context, + p entity.Pool, + params pool.GetNewPoolStateWithOverridesParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, pool.GetNewPoolStateParams{Logs: params.Logs}, params.Overrides) +} + +func (d *PoolTracker) getNewPoolState( + ctx context.Context, + p entity.Pool, + _ pool.GetNewPoolStateParams, + overrides map[common.Address]gethclient.OverrideAccount, +) (entity.Pool, error) { + startTime := time.Now() + + logger.WithFields(logger.Fields{"pool_id": p.Address}).Info("Started getting new pool state") + defer func() { + logger. + WithFields( + logger.Fields{ + "pool_id": p.Address, + "duration_ms": time.Since(startTime).Milliseconds(), + }, + ). + Info("Finished getting new pool state") + }() + + reserveData, isPaused, fee, blockNumber, err := d.getPoolData(ctx, p.Address, overrides) + if err != nil { + return p, err + } + + return d.updatePool(p, reserveData, isPaused, fee, blockNumber) +} + +func (d *PoolTracker) getPoolData( + ctx context.Context, + poolAddress string, + overrides map[common.Address]gethclient.OverrideAccount, +) (velodromev2.ReserveData, bool, uint64, uint64, error) { + var ( + isPaused bool + fee *big.Int + getReservesResult velodromev2.GetReservesResult + ) + + req := d.ethrpcClient.NewRequest().SetContext(ctx) + if overrides != nil { + req.SetOverrides(overrides) + } + + req.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: d.config.FactoryAddress, + Method: factoryMethodIsPaused, + Params: nil, + }, []interface{}{&isPaused}) + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodFeeRatio, + Params: []interface{}{}, + }, []interface{}{&fee}) + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetReserves, + Params: nil, + }, []interface{}{&getReservesResult}) + + resp, err := req.TryBlockAndAggregate() + if err != nil { + return velodromev2.ReserveData{}, false, 0, 0, err + } + + return velodromev2.ReserveData{ + Reserve0: getReservesResult.Reserve0, + Reserve1: getReservesResult.Reserve1, + }, isPaused, fee.Uint64(), resp.BlockNumber.Uint64(), nil + +} + +func (d *PoolTracker) updatePool( + pool entity.Pool, + reserveData velodromev2.ReserveData, + isPaused bool, + fee uint64, + blockNumber uint64) (entity.Pool, error) { + if pool.BlockNumber > blockNumber { + return pool, nil + } + + poolExtra := velodromev2.PoolExtra{ + IsPaused: isPaused, + Fee: fee, + } + + poolExtraBytes, err := json.Marshal(poolExtra) + if err != nil { + return pool, err + } + + pool.Reserves = entity.PoolReserves{ + reserveData.Reserve0.String(), + reserveData.Reserve1.String(), + } + pool.Extra = string(poolExtraBytes) + pool.BlockNumber = blockNumber + pool.Timestamp = time.Now().Unix() + + return pool, nil +} diff --git a/pkg/liquidity-source/solidly-v2/types.go b/pkg/liquidity-source/solidly-v2/types.go new file mode 100644 index 000000000..e5ce3e0cd --- /dev/null +++ b/pkg/liquidity-source/solidly-v2/types.go @@ -0,0 +1,18 @@ +package solidlyv2 + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +type PoolMetadata struct { + Dec0 *big.Int + Dec1 *big.Int + R0 *big.Int + R1 *big.Int + St bool + T0 common.Address + T1 common.Address + FeeRatio *big.Int +} diff --git a/pkg/liquidity-source/uniswap-v2/pool_simulator.go b/pkg/liquidity-source/uniswap-v2/pool_simulator.go index 169feec63..a424550e3 100644 --- a/pkg/liquidity-source/uniswap-v2/pool_simulator.go +++ b/pkg/liquidity-source/uniswap-v2/pool_simulator.go @@ -2,6 +2,7 @@ package uniswapv2 import ( "errors" + "fmt" "math/big" "github.com/KyberNetwork/blockchain-toolkit/integer" @@ -220,7 +221,11 @@ func (s *PoolSimulator) getAmountOut(amountIn, reserveIn, reserveOut *uint256.In func (s *PoolSimulator) getAmountIn(amountOut, reserveIn, reserveOut *uint256.Int) (amountIn *uint256.Int, err error) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/velodrome-v1/pool_simulator.go b/pkg/liquidity-source/velodrome-v1/pool_simulator.go index 9bd559234..f5dd9ae0e 100644 --- a/pkg/liquidity-source/velodrome-v1/pool_simulator.go +++ b/pkg/liquidity-source/velodrome-v1/pool_simulator.go @@ -2,6 +2,7 @@ package velodromev1 import ( "errors" + "fmt" "math/big" "github.com/KyberNetwork/blockchain-toolkit/integer" @@ -285,7 +286,11 @@ func (s *PoolSimulator) _getAmountIn( defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/velodrome-v2/pool_simulator.go b/pkg/liquidity-source/velodrome-v2/pool_simulator.go index 45d79e3a3..0be8b195e 100644 --- a/pkg/liquidity-source/velodrome-v2/pool_simulator.go +++ b/pkg/liquidity-source/velodrome-v2/pool_simulator.go @@ -2,6 +2,7 @@ package velodromev2 import ( "errors" + "fmt" "math/big" "github.com/KyberNetwork/blockchain-toolkit/integer" @@ -154,6 +155,14 @@ func (s *PoolSimulator) GetMetaInfo(_ string, _ string) interface{} { } } +func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { + cloned := *p + cloned.Info.Reserves = lo.Map(p.Info.Reserves, func(v *big.Int, i int) *big.Int { + return new(big.Int).Set(v) + }) + return &cloned +} + func (s *PoolSimulator) getAmountOut( amountIn *uint256.Int, tokenIn string, @@ -300,7 +309,11 @@ func (s *PoolSimulator) _getAmountIn( defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/liquidity-source/velodrome-v2/pool_tracker.go b/pkg/liquidity-source/velodrome-v2/pool_tracker.go index 5fa3ac2d5..8bc2ac81e 100644 --- a/pkg/liquidity-source/velodrome-v2/pool_tracker.go +++ b/pkg/liquidity-source/velodrome-v2/pool_tracker.go @@ -8,24 +8,17 @@ import ( "github.com/KyberNetwork/ethrpc" "github.com/KyberNetwork/logger" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/goccy/go-json" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" ) -type ( - ILogDecoder interface { - Decode(logs []types.Log) (ReserveData, uint64, error) - } - - PoolTracker struct { - config *Config - ethrpcClient *ethrpc.Client - logDecoder ILogDecoder - } -) +type PoolTracker struct { + config *Config + ethrpcClient *ethrpc.Client +} func NewPoolTracker( config *Config, @@ -34,14 +27,30 @@ func NewPoolTracker( return &PoolTracker{ config: config, ethrpcClient: ethrpcClient, - //logDecoder: NewLogDecoder(), }, nil } -func (d *PoolTracker) GetNewPoolState( +func (t *PoolTracker) GetNewPoolState( ctx context.Context, p entity.Pool, params pool.GetNewPoolStateParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, params, nil) +} + +func (t *PoolTracker) GetNewPoolStateWithOverrides( + ctx context.Context, + p entity.Pool, + params pool.GetNewPoolStateWithOverridesParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, pool.GetNewPoolStateParams{Logs: params.Logs}, params.Overrides) +} + +func (d *PoolTracker) getNewPoolState( + ctx context.Context, + p entity.Pool, + _ pool.GetNewPoolStateParams, + overrides map[common.Address]gethclient.OverrideAccount, ) (entity.Pool, error) { startTime := time.Now() @@ -62,12 +71,7 @@ func (d *PoolTracker) GetNewPoolState( return p, err } - reserveData, blockNumber, err := d.getReserves(ctx, p.Address, params.Logs) - if err != nil { - return p, err - } - - isPaused, fee, err := d.getFactoryData(ctx, p.Address, staticExtra.Stable, blockNumber) + reserveData, isPaused, fee, blockNumber, err := d.getPoolData(ctx, p.Address, staticExtra.Stable, overrides) if err != nil { return p, err } @@ -75,50 +79,51 @@ func (d *PoolTracker) GetNewPoolState( return d.updatePool(p, reserveData, isPaused, fee, blockNumber) } -func (d *PoolTracker) getReserves(ctx context.Context, poolAddress string, logs []types.Log) (ReserveData, uint64, error) { - reserveData, blockNumber, err := d.getReservesFromLogs(logs) - if err != nil { - return d.getReservesFromRPCNode(ctx, poolAddress) - } - - if reserveData.IsZero() { - return d.getReservesFromRPCNode(ctx, poolAddress) - } - - return reserveData, blockNumber, nil -} - -func (d *PoolTracker) getFactoryData( +func (d *PoolTracker) getPoolData( ctx context.Context, poolAddress string, stable bool, - blockNumber uint64, -) (bool, uint64, error) { + overrides map[common.Address]gethclient.OverrideAccount, +) (ReserveData, bool, uint64, uint64, error) { var ( - isPaused bool - fee *big.Int + isPaused bool + fee *big.Int + getReservesResult GetReservesResult ) - getFactoryDataRequest := d.ethrpcClient.NewRequest().SetContext(ctx).SetBlockNumber(big.NewInt(int64(blockNumber))) + req := d.ethrpcClient.NewRequest().SetContext(ctx) + if overrides != nil { + req.SetOverrides(overrides) + } - getFactoryDataRequest.AddCall(ðrpc.Call{ + req.AddCall(ðrpc.Call{ ABI: poolFactoryABI, Target: d.config.FactoryAddress, Method: poolFactoryMethodIsPaused, Params: nil, }, []interface{}{&isPaused}) - getFactoryDataRequest.AddCall(ðrpc.Call{ + req.AddCall(ðrpc.Call{ ABI: poolFactoryABI, Target: d.config.FactoryAddress, Method: poolFactoryMethodGetFee, Params: []interface{}{common.HexToAddress(poolAddress), stable}, }, []interface{}{&fee}) + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetReserves, + Params: nil, + }, []interface{}{&getReservesResult}) - if _, err := getFactoryDataRequest.TryBlockAndAggregate(); err != nil { - return false, 0, err + resp, err := req.TryBlockAndAggregate() + if err != nil { + return ReserveData{}, false, 0, 0, err } - return isPaused, fee.Uint64(), nil + return ReserveData{ + Reserve0: getReservesResult.Reserve0, + Reserve1: getReservesResult.Reserve1, + }, isPaused, fee.Uint64(), resp.BlockNumber.Uint64(), nil } func (d *PoolTracker) updatePool( @@ -150,40 +155,3 @@ func (d *PoolTracker) updatePool( return pool, nil } - -func (d *PoolTracker) getReservesFromRPCNode(ctx context.Context, poolAddress string) (ReserveData, uint64, error) { - var ( - getReservesResult GetReservesResult - ) - - getReservesRequest := d.ethrpcClient.NewRequest().SetContext(ctx) - - getReservesRequest.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetReserves, - Params: nil, - }, []interface{}{&getReservesResult}) - - resp, err := getReservesRequest.TryBlockAndAggregate() - if err != nil { - return ReserveData{}, 0, err - } - - return ReserveData{ - Reserve0: getReservesResult.Reserve0, - Reserve1: getReservesResult.Reserve1, - }, resp.BlockNumber.Uint64(), nil -} - -func (d *PoolTracker) getReservesFromLogs(logs []types.Log) (ReserveData, uint64, error) { - if len(logs) == 0 { - return ReserveData{}, 0, nil - } - - if d.logDecoder == nil { - return ReserveData{}, 0, nil - } - - return d.logDecoder.Decode(logs) -} diff --git a/pkg/liquidity-source/velodrome-v2/types.go b/pkg/liquidity-source/velodrome-v2/types.go index 56d2125e9..caa969e0f 100644 --- a/pkg/liquidity-source/velodrome-v2/types.go +++ b/pkg/liquidity-source/velodrome-v2/types.go @@ -12,7 +12,6 @@ type PoolStaticExtra struct { Decimal0 *uint256.Int `json:"decimal0"` Decimal1 *uint256.Int `json:"decimal1"` Stable bool `json:"stable"` - DecBig *big.Int `json:"decBig"` } type PoolExtra struct { diff --git a/pkg/liquidity-source/virtual-fun/pool_simulator.go b/pkg/liquidity-source/virtual-fun/pool_simulator.go index fe591bab4..afc3178e2 100644 --- a/pkg/liquidity-source/virtual-fun/pool_simulator.go +++ b/pkg/liquidity-source/virtual-fun/pool_simulator.go @@ -3,6 +3,7 @@ package virtualfun import ( "encoding/json" "errors" + "fmt" "math/big" "github.com/holiman/uint256" @@ -329,7 +330,11 @@ func (s *PoolSimulator) getAmountsOut(amountIn *uint256.Int, isBuy bool) *uint25 func (s *PoolSimulator) sellExactOut(amountOut *uint256.Int) (amountIn, amountOutBeforeFee *uint256.Int, err error) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() @@ -348,7 +353,11 @@ func (s *PoolSimulator) sellExactOut(amountOut *uint256.Int) (amountIn, amountOu func (s *PoolSimulator) buyExactOut(amountOut *uint256.Int) (amountInBeforeFee *uint256.Int, err error) { defer func() { if r := recover(); r != nil { - err = r.(error) + if recoveredError, ok := r.(error); ok { + err = recoveredError + } else { + err = fmt.Errorf("unexpected panic: %v", r) + } } }() diff --git a/pkg/msgpack/register_pool_types.go b/pkg/msgpack/register_pool_types.go index df2fc3063..6aecae749 100644 --- a/pkg/msgpack/register_pool_types.go +++ b/pkg/msgpack/register_pool_types.go @@ -59,6 +59,7 @@ import ( pkg_liquiditysource_renzo_ezeth "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/renzo/ezeth" pkg_liquiditysource_ringswap "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/ringswap" pkg_liquiditysource_rocketpool_reth "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/rocketpool/reth" + pkg_liquiditysource_solidlyv2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/solidly-v2" pkg_liquiditysource_staderethx "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/staderethx" pkg_liquiditysource_swaapv2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swaap-v2" pkg_liquiditysource_swell_rsweth "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swell/rsweth" @@ -191,6 +192,7 @@ func init() { msgpack.RegisterConcreteType(&pkg_liquiditysource_renzo_ezeth.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_ringswap.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_rocketpool_reth.PoolSimulator{}) + msgpack.RegisterConcreteType(&pkg_liquiditysource_solidlyv2.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_staderethx.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_swaapv2.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_swell_rsweth.PoolSimulator{}) diff --git a/pkg/pooltypes/pooltypes.go b/pkg/pooltypes/pooltypes.go index c180531ad..7bf637985 100644 --- a/pkg/pooltypes/pooltypes.go +++ b/pkg/pooltypes/pooltypes.go @@ -55,6 +55,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/renzo/ezeth" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/ringswap" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/rocketpool/reth" + solidlyv2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/solidly-v2" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/staderethx" swaapv2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swaap-v2" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swell/rsweth" @@ -214,6 +215,7 @@ type Types struct { VelocoreV2CPMM string VelocoreV2WombatStable string Fulcrom string + SolidlyV2 string SolidlyV3 string LegacyBalancerWeighted string LegacyBalancerStable string @@ -358,6 +360,7 @@ var ( VelocoreV2CPMM: velocorev2cpmm.DexType, VelocoreV2WombatStable: velocorev2wombatstable.DexType, Fulcrom: fulcrom.DexTypeFulcrom, + SolidlyV2: solidlyv2.DexType, SolidlyV3: solidlyv3.DexTypeSolidlyV3, LegacyBalancerWeighted: string(balancer.DexTypeBalancerWeighted), LegacyBalancerStable: string(balancer.DexTypeBalancerStable), diff --git a/pkg/valueobject/exchange.go b/pkg/valueobject/exchange.go index 12bba74ea..5593bb0f4 100644 --- a/pkg/valueobject/exchange.go +++ b/pkg/valueobject/exchange.go @@ -165,6 +165,7 @@ var ( ExchangeSuperSwapV3 Exchange = "superswap-v3" ExchangeWagmi Exchange = "wagmi" ExchangeMetavaultV3 Exchange = "metavault-v3" + ExchangeSolidlyV2 Exchange = "solidly-v2" ExchangeSolidlyV3 Exchange = "solidly-v3" ExchangeZero Exchange = "zero" ExchangeZebraV2 Exchange = "zebra-v2" @@ -388,6 +389,10 @@ var ( ExchangeSonicMarket Exchange = "sonic-market" ExchangeSboom Exchange = "sboom" ExchangeBeetsSS Exchange = "beets-ss" + ExchangeShadowDex Exchange = "shadow-dex" + ExchangeSwapXCL Exchange = "swap-x-cl" + ExchangeSwapXV2 Exchange = "swap-x-v2" + ExchangeMemeBox Exchange = "memebox" ) var AMMSourceSet = map[Exchange]struct{}{ @@ -527,6 +532,7 @@ var AMMSourceSet = map[Exchange]struct{}{ ExchangeSuperSwapV3: {}, ExchangeWagmi: {}, ExchangeMetavaultV3: {}, + ExchangeSolidlyV2: {}, ExchangeSolidlyV3: {}, ExchangeZero: {}, ExchangeBalancerV2Weighted: {}, @@ -708,6 +714,10 @@ var AMMSourceSet = map[Exchange]struct{}{ ExchangeSonicMarket: {}, ExchangeSboom: {}, ExchangeBeetsSS: {}, + ExchangeShadowDex: {}, + ExchangeSwapXCL: {}, + ExchangeSwapXV2: {}, + ExchangeMemeBox: {}, } func IsAMMSource(exchange Exchange) bool { From cdd2e743448221457a10263c4ad31305ff6b6f84 Mon Sep 17 00:00:00 2001 From: Nguyen Minh Chien Date: Mon, 30 Dec 2024 17:56:13 +0700 Subject: [PATCH 08/39] [Bebop] Support aggregate order (#672) --- pkg/liquidity-source/bebop/rfq.go | 100 ++++++++++++++++++++++++++++- pkg/liquidity-source/bebop/type.go | 88 +++++++++++++++++++++++-- 2 files changed, 182 insertions(+), 6 deletions(-) diff --git a/pkg/liquidity-source/bebop/rfq.go b/pkg/liquidity-source/bebop/rfq.go index 00f3839ad..9961d499f 100644 --- a/pkg/liquidity-source/bebop/rfq.go +++ b/pkg/liquidity-source/bebop/rfq.go @@ -7,6 +7,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" "github.com/KyberNetwork/logger" + "github.com/goccy/go-json" "github.com/mitchellh/mapstructure" ) @@ -51,7 +52,10 @@ func (h *RFQHandler) RFQ(ctx context.Context, params pool.RFQParams) (*pool.RFQR return nil, fmt.Errorf("quote single order result: %w", err) } - newAmountOut, _ := new(big.Int).SetString(result.ToSign.MakerAmount, 10) + newAmountOut, err := getAmountOutFromToSign(result.OnchainOrderType, result.ToSign) + if err != nil { + return nil, fmt.Errorf("get amount out from to sign: %w", err) + } return &pool.RFQResult{ NewAmountOut: newAmountOut, @@ -59,6 +63,100 @@ func (h *RFQHandler) RFQ(ctx context.Context, params pool.RFQParams) (*pool.RFQR }, nil } +func getAmountOutFromToSign(onchainOrderType string, rawTxSign json.RawMessage) (*big.Int, error) { + switch onchainOrderType { + case OnchainOrderTypeSingleOrder: + return getAmountOutOfSingleOrderToSign(rawTxSign) + case OnchainOrderTypeAggregateOrder: + return getAmountOutOfAggregateOrderToSign(rawTxSign) + case OnchainOrderTypeOrderWithPermit2: + return getAmountOutOfOrderWithPermit2ToSign(rawTxSign) + case OnchainOrderTypeOrderWithBatchPermit2: + return getAmountOutOfOrderWithBatchPermit2ToSign(rawTxSign) + default: + return nil, fmt.Errorf("unsupported onchain order type: %s", onchainOrderType) + } +} + +func getAmountOutOfSingleOrderToSign(rawTxSign json.RawMessage) (*big.Int, error) { + var toSign SingleOrderToSign + if err := json.Unmarshal(rawTxSign, &toSign); err != nil { + return nil, fmt.Errorf("unmarshal single order result: %w", err) + } + amountOut, ok := new(big.Int).SetString(toSign.MakerAmount, 10) + if !ok { + return nil, fmt.Errorf("invalid maker amount: %s", toSign.MakerAmount) + } + return amountOut, nil +} + +func getAmountOutOfAggregateOrderToSign(rawTxSign json.RawMessage) (*big.Int, error) { + var toSign AggregateOrderToSign + if err := json.Unmarshal(rawTxSign, &toSign); err != nil { + return nil, fmt.Errorf("unmarshal aggregate order result: %w", err) + } + + // With the aggregate order, it has some fields with format: + // - TakerAddress: common.Address + // - MakerAddress: [m]common.Address + // - TakerTokens: [m][n]common.Address + // - MakerTokens: [m][n]common.Address + // - TakerAmounts: [m][n]*big.Int + // - MakerAmounts: [m][n]*big.Int + // With m is number of makers and n is number of swap token pairs. + // Because we currently only support swap 1-1 token pair so n is always 1. + // So we can simplify the check by only checking the first element of each field in the logic below. + + totalAmountOut := big.NewInt(0) + + for _, amounts := range toSign.MakerAmounts { + if len(amounts) != 1 { + return nil, fmt.Errorf("invalid maker amounts: %v", amounts) + } + amountOut, ok := new(big.Int).SetString(amounts[0], 10) + if !ok { + return nil, fmt.Errorf("invalid maker amount: %s", amounts[0]) + } + totalAmountOut.Add(totalAmountOut, amountOut) + } + + return totalAmountOut, nil +} + +func getAmountOutOfOrderWithPermit2ToSign(rawTxSign json.RawMessage) (*big.Int, error) { + var toSign OrderWithPermit2ToSign + if err := json.Unmarshal(rawTxSign, &toSign); err != nil { + return nil, fmt.Errorf("unmarshal order with permit2 result: %w", err) + } + amountOut, ok := new(big.Int).SetString(toSign.Witness.MakerAmount, 10) + if !ok { + return nil, fmt.Errorf("invalid maker amount: %s", toSign.Witness.MakerAmount) + } + return amountOut, nil +} + +func getAmountOutOfOrderWithBatchPermit2ToSign(rawTxSign json.RawMessage) (*big.Int, error) { + var toSign OrderWithBatchPermit2ToSign + if err := json.Unmarshal(rawTxSign, &toSign); err != nil { + return nil, fmt.Errorf("unmarshal order with batch permit2 result: %w", err) + } + + // logic here same as getAmountOutOfAggregateOrderToSign + totalAmountOut := big.NewInt(0) + for _, amounts := range toSign.Witness.MakerAmounts { + if len(amounts) != 1 { + return nil, fmt.Errorf("invalid maker amounts: %v", amounts) + } + amountOut, ok := new(big.Int).SetString(amounts[0], 10) + if !ok { + return nil, fmt.Errorf("invalid maker amount: %s", amounts[0]) + } + totalAmountOut.Add(totalAmountOut, amountOut) + } + + return totalAmountOut, nil +} + func (h *RFQHandler) BatchRFQ(context.Context, []pool.RFQParams) ([]*pool.RFQResult, error) { return nil, nil } diff --git a/pkg/liquidity-source/bebop/type.go b/pkg/liquidity-source/bebop/type.go index ace37cdf3..3e3f08884 100644 --- a/pkg/liquidity-source/bebop/type.go +++ b/pkg/liquidity-source/bebop/type.go @@ -1,5 +1,7 @@ package bebop +import "github.com/goccy/go-json" + type QueryParams = string const ( @@ -18,6 +20,13 @@ const ( ParamsSourceAuth QueryParams = "source-auth" ) +const ( + OnchainOrderTypeSingleOrder = "SingleOrder" + OnchainOrderTypeAggregateOrder = "AggregateOrder" + OnchainOrderTypeOrderWithPermit2 = "OrderWithPermit2" + OnchainOrderTypeOrderWithBatchPermit2 = "OrderWithBatchPermit2" +) + type QuoteParams struct { // The tokens that will be supplied by the taker SellTokens string @@ -93,7 +102,16 @@ type QuoteSingleOrderResult struct { Gas int `json:"gas"` GasPrice int64 `json:"gasPrice"` } `json:"tx"` - ToSign struct { // the toSign part uses snake_case + ToSign json.RawMessage `json:"toSign"` + OnchainOrderType string `json:"onchainOrderType"` + PartialFillOffset int `json:"partialFillOffset"` +} + +// ToSign struct will depend on the OnchainOrderType field of the QuoteSingleOrderResult. +// Although, bebop support get quote for one to many and many to one, but we will only use one to one for now. +// So, we will only define the ToSign struct for one to one. +type ( + SingleOrderToSign struct { PartnerID int `json:"partner_id"` Expiry int `json:"expiry"` TakerAddress string `json:"taker_address"` @@ -105,7 +123,67 @@ type QuoteSingleOrderResult struct { MakerAmount string `json:"maker_amount"` Receiver string `json:"receiver"` PackedCommands string `json:"packed_commands"` - } `json:"toSign"` - OnchainOrderType string `json:"onchainOrderType"` - PartialFillOffset int `json:"partialFillOffset"` -} + } + + AggregateOrderToSign struct { + PartnerID int `json:"partner_id"` + Expiry int `json:"expiry"` + TakerAddress string `json:"taker_address"` + MakerAddresses []string `json:"maker_addresses"` + MakerNonces []string `json:"maker_nonces"` + TakerTokens [][]string `json:"taker_tokens"` + MakerTokens [][]string `json:"maker_tokens"` + TakerAmounts [][]string `json:"taker_amounts"` + MakerAmounts [][]string `json:"maker_amounts"` + Receiver string `json:"receiver"` + Commands string `json:"commands"` + } + + OrderWithPermit2ToSign struct { + Permitted struct { + Token string `json:"token"` + Amount string `json:"amount"` + } `json:"permitted"` + Spender string `json:"spender"` + Nonce string `json:"nonce"` + Deadline int64 `json:"deadline"` + Witness struct { + PartnerID int `json:"partner_id"` + Expiry int `json:"expiry"` + TakerAddress string `json:"taker_address"` + MakerAddress string `json:"maker_address"` + MakerNonce string `json:"maker_nonce"` + TakerToken string `json:"taker_token"` + MakerToken string `json:"maker_token"` + TakerAmount string `json:"taker_amount"` + MakerAmount string `json:"maker_amount"` + Receiver string `json:"receiver"` + PackedCommands string `json:"packed_commands"` + HooksHash string `json:"hooksHash"` + } `json:"witness"` + } + + OrderWithBatchPermit2ToSign struct { + Permitted []struct { + Token string `json:"token"` + Amount string `json:"amount"` + } `json:"permitted"` + Spender string `json:"spender"` + Nonce string `json:"nonce"` + Deadline int64 `json:"deadline"` + Witness struct { + PartnerID int `json:"partner_id"` + Expiry int `json:"expiry"` + TakerAddress string `json:"taker_address"` + MakerAddresses []string `json:"maker_addresses"` + MakerNonces []string `json:"maker_nonces"` + TakerTokens [][]string `json:"taker_tokens"` + MakerTokens [][]string `json:"maker_tokens"` + TakerAmounts [][]string `json:"taker_amounts"` + MakerAmounts [][]string `json:"maker_amounts"` + Receiver string `json:"receiver"` + Commands string `json:"commands"` + HooksHash string `json:"hooksHash"` + } `json:"witness"` + } +) From 864cbebd711bad3944138268f6ec01855d474c03 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 30 Dec 2024 20:19:50 +0700 Subject: [PATCH 09/39] update --- .../balancer-v3/shared/abis.go | 4 +- .../balancer-v3/shared/abis/Vault.json | 1367 ++------ .../shared/abis/VaultExtension.json | 2847 +++++++++++++++++ .../balancer-v3/shared/constant.go | 14 +- .../balancer-v3/shared/embed.go | 3 + .../balancer-v3/stable/constant.go | 3 + .../balancer-v3/stable/pool_simulator.go | 16 +- .../balancer-v3/stable/pool_tracker.go | 168 +- .../balancer-v3/stable/pools_list_updater.go | 10 +- .../balancer-v3/stable/type.go | 58 +- .../balancer-v3/weighted/pool_tracker.go | 101 +- pkg/valueobject/exchange.go | 5 + 12 files changed, 3354 insertions(+), 1242 deletions(-) create mode 100644 pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json diff --git a/pkg/liquidity-source/balancer-v3/shared/abis.go b/pkg/liquidity-source/balancer-v3/shared/abis.go index 8cfd7d3f9..10a31e7db 100644 --- a/pkg/liquidity-source/balancer-v3/shared/abis.go +++ b/pkg/liquidity-source/balancer-v3/shared/abis.go @@ -7,7 +7,8 @@ import ( ) var ( - VaultABI abi.ABI + VaultABI abi.ABI + VaultExtensionABI abi.ABI ) func init() { @@ -16,6 +17,7 @@ func init() { data []byte }{ {&VaultABI, vaultJson}, + {&VaultExtensionABI, vaultExtensionJson}, } for _, b := range builder { diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json index 215a232a0..e0b7a3521 100644 --- a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json +++ b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json @@ -2,13 +2,18 @@ { "inputs": [ { - "internalType": "contract IVault", - "name": "mainVault", + "internalType": "contract IVaultExtension", + "name": "vaultExtension", "type": "address" }, { - "internalType": "contract IVaultAdmin", - "name": "vaultAdmin", + "internalType": "contract IAuthorizer", + "name": "authorizer", + "type": "address" + }, + { + "internalType": "contract IProtocolFeeController", + "name": "protocolFeeController", "type": "address" } ], @@ -57,6 +62,11 @@ "name": "AfterSwapHookFailed", "type": "error" }, + { + "inputs": [], + "name": "AllZeroInputs", + "type": "error" + }, { "inputs": [], "name": "AmountGivenZero", @@ -219,11 +229,6 @@ "name": "CannotSwapSameToken", "type": "error" }, - { - "inputs": [], - "name": "CodecOverflow", - "type": "error" - }, { "inputs": [], "name": "DoesNotSupportAddLiquidityCustom", @@ -335,11 +340,6 @@ "name": "ERC20InvalidSpender", "type": "error" }, - { - "inputs": [], - "name": "ErrorSelectorNotFound", - "type": "error" - }, { "inputs": [], "name": "FailedInnerCall", @@ -475,6 +475,38 @@ "name": "InvalidUnderlyingToken", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "invariantRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxInvariantRatio", + "type": "uint256" + } + ], + "name": "InvariantRatioAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "invariantRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minInvariantRatio", + "type": "uint256" + } + ], + "name": "InvariantRatioBelowMin", + "type": "error" + }, { "inputs": [ { @@ -501,6 +533,11 @@ "name": "MinTokens", "type": "error" }, + { + "inputs": [], + "name": "MultipleNonZeroInputs", + "type": "error" + }, { "inputs": [], "name": "NotEnoughBufferShares", @@ -558,11 +595,6 @@ "name": "NotVaultDelegateCall", "type": "error" }, - { - "inputs": [], - "name": "OutOfBounds", - "type": "error" - }, { "inputs": [], "name": "PauseBufferPeriodDurationTooLarge", @@ -708,20 +740,20 @@ "name": "ReentrancyGuardReentrantCall", "type": "error" }, + { + "inputs": [], + "name": "RouterNotTrusted", + "type": "error" + }, { "inputs": [ { - "internalType": "bytes", - "name": "result", - "type": "bytes" + "internalType": "int256", + "name": "value", + "type": "int256" } ], - "name": "Result", - "type": "error" - }, - { - "inputs": [], - "name": "RouterNotTrusted", + "name": "SafeCastOverflowedIntToUint", "type": "error" }, { @@ -735,6 +767,17 @@ "name": "SafeCastOverflowedUintToInt", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, { "inputs": [ { @@ -815,11 +858,6 @@ "name": "TokensMismatch", "type": "error" }, - { - "inputs": [], - "name": "TokensNotSorted", - "type": "error" - }, { "inputs": [], "name": "TradeAmountTooSmall", @@ -897,6 +935,11 @@ "name": "WrongVaultExtensionDeployment", "type": "error" }, + { + "inputs": [], + "name": "ZeroDivision", + "type": "error" + }, { "anonymous": false, "inputs": [ @@ -1645,56 +1688,115 @@ { "inputs": [ { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" + "components": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "maxAmountsIn", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "minBptAmountOut", + "type": "uint256" + }, + { + "internalType": "enum AddLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct AddLiquidityParams", + "name": "params", + "type": "tuple" } ], - "name": "allowance", + "name": "addLiquidity", "outputs": [ + { + "internalType": "uint256[]", + "name": "amountsIn", + "type": "uint256[]" + }, { "internalType": "uint256", - "name": "", + "name": "bptAmountOut", "type": "uint256" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "address", - "name": "owner", - "type": "address" - }, + "components": [ + { + "internalType": "enum SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "enum WrappingDirection", + "name": "direction", + "type": "uint8" + }, + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountGivenRaw", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limitRaw", + "type": "uint256" + } + ], + "internalType": "struct BufferWrapOrUnwrapParams", + "name": "params", + "type": "tuple" + } + ], + "name": "erc4626BufferWrapOrUnwrap", + "outputs": [ { - "internalType": "address", - "name": "spender", - "type": "address" + "internalType": "uint256", + "name": "amountCalculatedRaw", + "type": "uint256" }, { "internalType": "uint256", - "name": "amount", + "name": "amountInRaw", "type": "uint256" - } - ], - "name": "approve", - "outputs": [ + }, { - "internalType": "bool", - "name": "", - "type": "bool" + "internalType": "uint256", + "name": "amountOutRaw", + "type": "uint256" } ], "stateMutability": "nonpayable", @@ -1704,17 +1806,22 @@ "inputs": [ { "internalType": "address", - "name": "token", + "name": "pool", "type": "address" }, { - "internalType": "address", - "name": "account", + "internalType": "contract IERC20", + "name": "token", "type": "address" } ], - "name": "balanceOf", + "name": "getPoolTokenCountAndIndexOfToken", "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, { "internalType": "uint256", "name": "", @@ -1725,43 +1832,59 @@ "type": "function" }, { - "inputs": [ + "inputs": [], + "name": "getVaultExtension", + "outputs": [ { "internalType": "address", - "name": "pool", + "name": "", "type": "address" - }, + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reentrancyGuardEntered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "components": [ { - "internalType": "enum SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amountGivenScaled18", - "type": "uint256" + "internalType": "address", + "name": "pool", + "type": "address" }, { - "internalType": "uint256[]", - "name": "balancesScaled18", - "type": "uint256[]" + "internalType": "address", + "name": "from", + "type": "address" }, { "internalType": "uint256", - "name": "indexIn", + "name": "maxBptAmountIn", "type": "uint256" }, { - "internalType": "uint256", - "name": "indexOut", - "type": "uint256" + "internalType": "uint256[]", + "name": "minAmountsOut", + "type": "uint256[]" }, { - "internalType": "address", - "name": "router", - "type": "address" + "internalType": "enum RemoveLiquidityKind", + "name": "kind", + "type": "uint8" }, { "internalType": "bytes", @@ -1769,1008 +1892,142 @@ "type": "bytes" } ], - "internalType": "struct PoolSwapParams", - "name": "swapParams", + "internalType": "struct RemoveLiquidityParams", + "name": "params", "type": "tuple" } ], - "name": "computeDynamicSwapFeePercentage", + "name": "removeLiquidity", "outputs": [ { "internalType": "uint256", - "name": "dynamicSwapFeePercentage", + "name": "bptAmountIn", "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ + }, { - "internalType": "bytes32", - "name": "eventKey", - "type": "bytes32" + "internalType": "uint256[]", + "name": "amountsOut", + "type": "uint256[]" }, { "internalType": "bytes", - "name": "eventData", + "name": "returnData", "type": "bytes" } ], - "name": "emitAuxiliaryEvent", - "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getAddLiquidityCalledFlag", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getAggregateSwapFeeAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getAggregateYieldFeeAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAuthorizer", - "outputs": [ - { - "internalType": "contract IAuthorizer", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getBptRate", - "outputs": [ - { - "internalType": "uint256", - "name": "rate", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getCurrentLiveBalances", - "outputs": [ - { - "internalType": "uint256[]", - "name": "balancesLiveScaled18", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "getERC4626BufferAsset", - "outputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getHooksConfig", - "outputs": [ - { - "components": [ - { - "internalType": "bool", - "name": "enableHookAdjustedAmounts", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallComputeDynamicSwapFee", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "address", - "name": "hooksContract", - "type": "address" - } - ], - "internalType": "struct HooksConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNonzeroDeltaCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolConfig", - "outputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "bool", - "name": "disableUnbalancedLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableRemoveLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableDonation", - "type": "bool" - } - ], - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "staticSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aggregateSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aggregateYieldFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint40", - "name": "tokenDecimalDiffs", - "type": "uint40" - }, - { - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "isPoolRegistered", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolInitialized", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolPaused", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolInRecoveryMode", - "type": "bool" - } - ], - "internalType": "struct PoolConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolData", - "outputs": [ - { - "components": [ - { - "internalType": "PoolConfigBits", - "name": "poolConfigBits", - "type": "bytes32" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "components": [ - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "internalType": "struct TokenInfo[]", - "name": "tokenInfo", - "type": "tuple[]" - }, - { - "internalType": "uint256[]", - "name": "balancesRaw", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "balancesLiveScaled18", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "tokenRates", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "decimalScalingFactors", - "type": "uint256[]" - } - ], - "internalType": "struct PoolData", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolPausedState", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "", - "type": "uint32" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolRoleAccounts", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "internalType": "address", - "name": "swapFeeManager", - "type": "address" - }, - { - "internalType": "address", - "name": "poolCreator", - "type": "address" - } - ], - "internalType": "struct PoolRoleAccounts", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokenInfo", - "outputs": [ - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "components": [ - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "internalType": "struct TokenInfo[]", - "name": "tokenInfo", - "type": "tuple[]" - }, - { - "internalType": "uint256[]", - "name": "balancesRaw", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "lastBalancesLiveScaled18", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokenRates", - "outputs": [ - { - "internalType": "uint256[]", - "name": "decimalScalingFactors", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "tokenRates", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokens", - "outputs": [ - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getProtocolFeeController", - "outputs": [ - { - "internalType": "contract IProtocolFeeController", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getReservesOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getStaticSwapFeePercentage", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getTokenDelta", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getVaultAdmin", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "exactAmountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "minBptAmountOut", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "initialize", - "outputs": [ - { - "internalType": "uint256", - "name": "bptAmountOut", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "isERC4626BufferInitialized", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolInRecoveryMode", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolInitialized", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolPaused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, { "internalType": "address", - "name": "pool", + "name": "to", "type": "address" - } - ], - "name": "isPoolRegistered", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isQueryDisabled", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isQueryDisabledPermanently", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isUnlocked", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "quote", - "outputs": [ + }, { - "internalType": "bytes", - "name": "result", - "type": "bytes" + "internalType": "uint256", + "name": "amount", + "type": "uint256" } ], + "name": "sendTo", + "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "bytes", - "name": "data", - "type": "bytes" + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountHint", + "type": "uint256" } ], - "name": "quoteAndRevert", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "reentrancyGuardEntered", + "name": "settle", "outputs": [ { - "internalType": "bool", - "name": "", - "type": "bool" + "internalType": "uint256", + "name": "credit", + "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, { "components": [ { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", + "internalType": "enum SwapKind", + "name": "kind", "type": "uint8" }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "protocolFeeExempt", - "type": "bool" - }, - { - "components": [ { "internalType": "address", - "name": "pauseManager", + "name": "pool", "type": "address" }, { - "internalType": "address", - "name": "swapFeeManager", + "internalType": "contract IERC20", + "name": "tokenIn", "type": "address" }, { - "internalType": "address", - "name": "poolCreator", + "internalType": "contract IERC20", + "name": "tokenOut", "type": "address" - } - ], - "internalType": "struct PoolRoleAccounts", - "name": "roleAccounts", - "type": "tuple" - }, - { - "internalType": "address", - "name": "poolHooksContract", - "type": "address" - }, - { - "components": [ - { - "internalType": "bool", - "name": "disableUnbalancedLiquidity", - "type": "bool" }, { - "internalType": "bool", - "name": "enableAddLiquidityCustom", - "type": "bool" + "internalType": "uint256", + "name": "amountGivenRaw", + "type": "uint256" }, { - "internalType": "bool", - "name": "enableRemoveLiquidityCustom", - "type": "bool" + "internalType": "uint256", + "name": "limitRaw", + "type": "uint256" }, { - "internalType": "bool", - "name": "enableDonation", - "type": "bool" + "internalType": "bytes", + "name": "userData", + "type": "bytes" } ], - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", + "internalType": "struct VaultSwapParams", + "name": "vaultSwapParams", "type": "tuple" } ], - "name": "registerPool", - "outputs": [], + "name": "swap", + "outputs": [ + { + "internalType": "uint256", + "name": "amountCalculated", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], "stateMutability": "nonpayable", "type": "function" }, @@ -2778,31 +2035,26 @@ "inputs": [ { "internalType": "address", - "name": "pool", + "name": "owner", "type": "address" }, { "internalType": "address", - "name": "from", + "name": "to", "type": "address" }, { "internalType": "uint256", - "name": "exactBptAmountIn", + "name": "amount", "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "minAmountsOut", - "type": "uint256[]" } ], - "name": "removeLiquidityRecovery", + "name": "transfer", "outputs": [ { - "internalType": "uint256[]", - "name": "amountsOutRaw", - "type": "uint256[]" + "internalType": "bool", + "name": "", + "type": "bool" } ], "stateMutability": "nonpayable", @@ -2812,32 +2064,53 @@ "inputs": [ { "internalType": "address", - "name": "token", + "name": "spender", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" } ], - "name": "totalSupply", + "name": "transferFrom", "outputs": [ { - "internalType": "uint256", + "internalType": "bool", "name": "", - "type": "uint256" + "type": "bool" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [], - "name": "vault", + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlock", "outputs": [ { - "internalType": "contract IVault", - "name": "", - "type": "address" + "internalType": "bytes", + "name": "result", + "type": "bytes" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json b/pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json new file mode 100644 index 000000000..efccfd3c3 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json @@ -0,0 +1,2847 @@ +[ + { + "inputs": [ + { + "internalType": "contract IVault", + "name": "mainVault", + "type": "address" + }, + { + "internalType": "contract IVaultAdmin", + "name": "vaultAdmin", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "AfterAddLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AfterInitializeHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AfterRemoveLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AfterSwapHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "AmountGivenZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + } + ], + "name": "AmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + } + ], + "name": "AmountOutBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceNotSettled", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeAddLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeInitializeHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeRemoveLiquidityHookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "BeforeSwapHookFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + } + ], + "name": "BptAmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + } + ], + "name": "BptAmountOutBelowMin", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "BufferAlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "BufferNotInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "BufferSharesInvalidOwner", + "type": "error" + }, + { + "inputs": [], + "name": "BufferSharesInvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + } + ], + "name": "BufferTotalSupplyTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "CannotReceiveEth", + "type": "error" + }, + { + "inputs": [], + "name": "CannotSwapSameToken", + "type": "error" + }, + { + "inputs": [], + "name": "CodecOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportAddLiquidityCustom", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportDonation", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportRemoveLiquidityCustom", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportUnbalancedLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "DynamicSwapFeeHookFailed", + "type": "error" + }, + { + "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": "ErrorSelectorNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "FeePrecisionTooHigh", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + } + ], + "name": "HookAdjustedAmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + } + ], + "name": "HookAdjustedAmountOutBelowMin", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "HookAdjustedSwapLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolHooksContract", + "type": "address" + }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "poolFactory", + "type": "address" + } + ], + "name": "HookRegistrationFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRemoveLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidToken", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenConfiguration", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenDecimals", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "InvalidUnderlyingToken", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "issuedShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minIssuedShares", + "type": "uint256" + } + ], + "name": "IssuedSharesBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "MaxTokens", + "type": "error" + }, + { + "inputs": [], + "name": "MinTokens", + "type": "error" + }, + { + "inputs": [], + "name": "NotEnoughBufferShares", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "expectedUnderlyingAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualUnderlyingAmount", + "type": "uint256" + } + ], + "name": "NotEnoughUnderlying", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "expectedWrappedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualWrappedAmount", + "type": "uint256" + } + ], + "name": "NotEnoughWrapped", + "type": "error" + }, + { + "inputs": [], + "name": "NotStaticCall", + "type": "error" + }, + { + "inputs": [], + "name": "NotVaultDelegateCall", + "type": "error" + }, + { + "inputs": [], + "name": "OutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "PauseBufferPeriodDurationTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "PercentageAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPauseWindowExpired", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + } + ], + "name": "PoolTotalSupplyTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolFeesExceedTotalCollected", + "type": "error" + }, + { + "inputs": [], + "name": "QueriesDisabled", + "type": "error" + }, + { + "inputs": [], + "name": "QueriesDisabledPermanently", + "type": "error" + }, + { + "inputs": [], + "name": "QuoteResultSpoofed", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "name": "Result", + "type": "error" + }, + { + "inputs": [], + "name": "RouterNotTrusted", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintToInt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderIsNotVault", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFeePercentageTooLow", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "SwapLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "TokenAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "TokenNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "expectedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "actualToken", + "type": "address" + } + ], + "name": "TokensMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "TokensNotSorted", + "type": "error" + }, + { + "inputs": [], + "name": "TradeAmountTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "VaultBuffersArePaused", + "type": "error" + }, + { + "inputs": [], + "name": "VaultIsNotUnlocked", + "type": "error" + }, + { + "inputs": [], + "name": "VaultNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowDurationTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowExpired", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "WrapAmountTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "WrongProtocolFeeControllerDeployment", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "underlyingToken", + "type": "address" + } + ], + "name": "WrongUnderlyingToken", + "type": "error" + }, + { + "inputs": [], + "name": "WrongVaultAdminDeployment", + "type": "error" + }, + { + "inputs": [], + "name": "WrongVaultExtensionDeployment", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + } + ], + "name": "AggregateSwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + } + ], + "name": "AggregateYieldFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": true, + "internalType": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "AuthorizerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "burnedShares", + "type": "uint256" + } + ], + "name": "BufferSharesBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "issuedShares", + "type": "uint256" + } + ], + "name": "BufferSharesMinted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": true, + "internalType": "enum AddLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amountsAddedRaw", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "swapFeeAmountsRaw", + "type": "uint256[]" + } + ], + "name": "LiquidityAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWrapped", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "LiquidityAddedToBuffer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": true, + "internalType": "enum RemoveLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amountsRemovedRaw", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "swapFeeAmountsRaw", + "type": "uint256[]" + } + ], + "name": "LiquidityRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWrapped", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "LiquidityRemovedFromBuffer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInitialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PoolPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "recoveryMode", + "type": "bool" + } + ], + "name": "PoolRecoveryModeStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "internalType": "address", + "name": "swapFeeManager", + "type": "address" + }, + { + "internalType": "address", + "name": "poolCreator", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct PoolRoleAccounts", + "name": "roleAccounts", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "enableHookAdjustedAmounts", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallComputeDynamicSwapFee", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "address", + "name": "hooksContract", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct HooksConfig", + "name": "hooksConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableDonation", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IProtocolFeeController", + "name": "newProtocolFeeController", + "type": "address" + } + ], + "name": "ProtocolFeeControllerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeeAmount", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "SwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "burnedShares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "withdrawnUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "Unwrap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "eventKey", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "eventData", + "type": "bytes" + } + ], + "name": "VaultAuxiliary", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "VaultBuffersPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "VaultPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "VaultQueriesDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "VaultQueriesEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "depositedUnderlying", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintedShares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "bufferBalances", + "type": "bytes32" + } + ], + "name": "Wrap", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "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": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "components": [ + { + "internalType": "enum SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amountGivenScaled18", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "balancesScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "indexIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "indexOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct PoolSwapParams", + "name": "swapParams", + "type": "tuple" + } + ], + "name": "computeDynamicSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "dynamicSwapFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "eventKey", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "eventData", + "type": "bytes" + } + ], + "name": "emitAuxiliaryEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getAddLiquidityCalledFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getAggregateSwapFeeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getAggregateYieldFeeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "contract IAuthorizer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getBptRate", + "outputs": [ + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getCurrentLiveBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "getERC4626BufferAsset", + "outputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getHooksConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "enableHookAdjustedAmounts", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallComputeDynamicSwapFee", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "address", + "name": "hooksContract", + "type": "address" + } + ], + "internalType": "struct HooksConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNonzeroDeltaCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolConfig", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableDonation", + "type": "bool" + } + ], + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "staticSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint40", + "name": "tokenDecimalDiffs", + "type": "uint40" + }, + { + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isPoolRegistered", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolPaused", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInRecoveryMode", + "type": "bool" + } + ], + "internalType": "struct PoolConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolData", + "outputs": [ + { + "components": [ + { + "internalType": "PoolConfigBits", + "name": "poolConfigBits", + "type": "bytes32" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + } + ], + "internalType": "struct PoolData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolRoleAccounts", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "internalType": "address", + "name": "swapFeeManager", + "type": "address" + }, + { + "internalType": "address", + "name": "poolCreator", + "type": "address" + } + ], + "internalType": "struct PoolRoleAccounts", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokenInfo", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "lastBalancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokenRates", + "outputs": [ + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolFeeController", + "outputs": [ + { + "internalType": "contract IProtocolFeeController", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getReservesOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getStaticSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenDelta", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "exactAmountsIn", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "minBptAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [ + { + "internalType": "uint256", + "name": "bptAmountOut", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "isERC4626BufferInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInRecoveryMode", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isQueryDisabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isQueryDisabledPermanently", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isUnlocked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "quoteAndRevert", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reentrancyGuardEntered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "protocolFeeExempt", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "internalType": "address", + "name": "swapFeeManager", + "type": "address" + }, + { + "internalType": "address", + "name": "poolCreator", + "type": "address" + } + ], + "internalType": "struct PoolRoleAccounts", + "name": "roleAccounts", + "type": "tuple" + }, + { + "internalType": "address", + "name": "poolHooksContract", + "type": "address" + }, + { + "components": [ + { + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableDonation", + "type": "bool" + } + ], + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "name": "registerPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exactBptAmountIn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "minAmountsOut", + "type": "uint256[]" + } + ], + "name": "removeLiquidityRecovery", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amountsOutRaw", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vault", + "outputs": [ + { + "internalType": "contract IVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/shared/constant.go b/pkg/liquidity-source/balancer-v3/shared/constant.go index 843531f32..052c1fef3 100644 --- a/pkg/liquidity-source/balancer-v3/shared/constant.go +++ b/pkg/liquidity-source/balancer-v3/shared/constant.go @@ -3,9 +3,9 @@ package shared import "github.com/holiman/uint256" const ( - VaultMethodGetPoolTokens = "getPoolTokens" - PoolMethodGetSwapFeePercentage = "getSwapFeePercentage" - PoolMethodGetPausedState = "getPausedState" + PoolMethodGetTokenInfo = "getTokenInfo" + PoolMethodGetAggregateFeePercentages = "getAggregateFeePercentages" + PoolMethodGetAmplificationParameter = "getAmplificationParameter" PoolMethodGetVault = "getVault" PoolMethodVersion = "version" @@ -14,11 +14,17 @@ const ( PoolVersion2 = 2 ) -type Rounding int +type ( + Rounding int + TokenType int +) const ( ROUND_UP Rounding = iota ROUND_DOWN + + STANDARD TokenType = iota + WITH_RATE ) var ( diff --git a/pkg/liquidity-source/balancer-v3/shared/embed.go b/pkg/liquidity-source/balancer-v3/shared/embed.go index ee4150e76..3642bd358 100644 --- a/pkg/liquidity-source/balancer-v3/shared/embed.go +++ b/pkg/liquidity-source/balancer-v3/shared/embed.go @@ -4,3 +4,6 @@ import _ "embed" //go:embed abis/Vault.json var vaultJson []byte + +//go:embed abis/VaultExtension.json +var vaultExtensionJson []byte diff --git a/pkg/liquidity-source/balancer-v3/stable/constant.go b/pkg/liquidity-source/balancer-v3/stable/constant.go index 5f941008a..b5505392d 100644 --- a/pkg/liquidity-source/balancer-v3/stable/constant.go +++ b/pkg/liquidity-source/balancer-v3/stable/constant.go @@ -4,6 +4,9 @@ const ( DexType = "balancer-v2-stable" PoolType = "StablePool" + + poolMethodGetStablePoolDynamicData = "getStablePoolDynamicData" + poolMethodGetStablePoolImmutableData = "getStablePoolImmutableData" ) var ( diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index 03273f198..13b706ab5 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -39,8 +39,8 @@ type PoolSimulator struct { decimalScalingFactors []*uint256.Int tokenRates []*uint256.Int - isVaultLocked bool - isPaused bool + isInRecoveryMode bool + isPaused bool vault string @@ -83,8 +83,8 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { return &PoolSimulator{ Pool: poolpkg.Pool{Info: poolInfo}, isPaused: extra.IsPaused, - isVaultLocked: extra.IsVaultLocked, - swapFeePercentage: extra.SwapFeePercentage, + isInRecoveryMode: extra.IsInRecoveryMode, + swapFeePercentage: extra.StaticSwapFeePercentage, aggregateSwapFeePercentage: extra.AggregateSwapFeePercentage, amplificationParameter: extra.AmplificationParameter, balancesLiveScaled18: extra.BalancesLiveScaled18, @@ -97,10 +97,6 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { } func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { - if p.isVaultLocked { - return nil, ErrVaultIsLocked - } - if p.isPaused { return nil, ErrPoolIsPaused } @@ -263,10 +259,6 @@ func (p *PoolSimulator) computeInvariant(rounding shared.Rounding) (*uint256.Int } func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { - if p.isVaultLocked { - return nil, ErrVaultIsLocked - } - if p.isPaused { return nil, ErrPoolIsPaused } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index 2160f72d0..db53c8392 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -4,7 +4,6 @@ import ( "context" "errors" "math/big" - "strings" "time" "github.com/KyberNetwork/ethrpc" @@ -12,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" "github.com/holiman/uint256" + "github.com/samber/lo" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" @@ -72,61 +72,49 @@ func (t *PoolTracker) getNewPoolState( }).Info("Finish updating state.") }() - var staticExtra StaticExtra - if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error(err.Error()) + var extra Extra + err := json.Unmarshal([]byte(p.Extra), &extra) + if err != nil { + return p, err + } + res, err := t.queryRPC(ctx, p.Address, overrides) + if err != nil { return p, err } - var oldExtra Extra - if err := json.Unmarshal([]byte(p.Extra), &oldExtra); err != nil { + if len(p.Reserves) != len(res.BalancesRaw) { logger.WithFields(logger.Fields{ "dexId": t.config.DexID, "dexType": DexType, "poolAddress": p.Address, - }).Error(err.Error()) - - return p, err - } - scalingFactors := oldExtra.DecimalScalingFactors - - // call RPC - rpcRes, err := t.queryRPC(ctx, p.Address, staticExtra.Vault, staticExtra.PoolType, overrides) - if err != nil { + }).Error("can not fetch reserves") return p, err } var ( - amplificationParameter, _ = uint256.FromBig(rpcRes.Amp) - swapFeePercentage, _ = uint256.FromBig(rpcRes.SwapFeePercentage) - poolTokens = rpcRes.PoolTokens - pausedState = rpcRes.PausedState - blockNumber = rpcRes.BlockNumber + amplificationParameter, _ = uint256.FromBig(res.AmplificationParameter) + staticSwapFeePercentage, _ = uint256.FromBig(res.StaticSwapFeePercentage) + aggregateSwapFeePercentage, _ = uint256.FromBig(res.AggregateSwapFeePercentage) + balancesLiveScaled18 = lo.Map(res.BalancesLiveScaled18, func(v *big.Int, _ int) *uint256.Int { + r, _ := uint256.FromBig(v) + return r + }) + tokenRates = lo.Map(res.TokenRates, func(v *big.Int, _ int) *uint256.Int { + r, _ := uint256.FromBig(v) + return r + }) ) - // if staticExtra.PoolType == PoolTypeMetaStable { - // factors := make([]*uint256.Int, len(rpcRes.ScalingFactors)) - // for idx, factor := range rpcRes.ScalingFactors { - // factors[idx], _ = uint256.FromBig(factor) - // } + extra.AmplificationParameter = amplificationParameter + extra.StaticSwapFeePercentage = staticSwapFeePercentage + extra.AggregateSwapFeePercentage = aggregateSwapFeePercentage + extra.BalancesLiveScaled18 = balancesLiveScaled18 + extra.TokenRates = tokenRates + extra.IsPaused = res.IsPoolPaused + extra.IsInRecoveryMode = res.IsPoolInRecoveryMode - // scalingFactors = factors - // } - - // update pool - - extra := Extra{ - AmplificationParameter: amplificationParameter, - SwapFeePercentage: swapFeePercentage, - DecimalScalingFactors: scalingFactors, - IsPaused: !isNotPaused(pausedState), - } - extraBytes, err := json.Marshal(extra) + extraBytes, err := json.Marshal(&extra) if err != nil { logger.WithFields(logger.Fields{ "dexId": t.config.DexID, @@ -137,94 +125,47 @@ func (t *PoolTracker) getNewPoolState( return p, err } - reserves, err := t.initReserves(ctx, p, poolTokens) - if err != nil { - return p, err - } - - p.BlockNumber = blockNumber + p.BlockNumber = res.BlockNumber p.Extra = string(extraBytes) p.Timestamp = time.Now().Unix() - p.Reserves = reserves + p.Reserves = lo.Map(res.TokenRates, func(v *big.Int, _ int) string { + return v.String() + }) return p, nil } -func (t *PoolTracker) initReserves( - ctx context.Context, - p entity.Pool, - poolTokens PoolTokens, -) ([]string, error) { - reserveByToken := make(map[string]*big.Int) - for idx, token := range poolTokens.Tokens { - addr := strings.ToLower(token.Hex()) - reserveByToken[addr] = poolTokens.Balances[idx] - } - - reserves := make([]string, len(p.Tokens)) - for idx, token := range p.Tokens { - r, ok := reserveByToken[token.Address] - if !ok { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error("can not get reserve") - - return nil, ErrReserveNotFound - } - - reserves[idx] = r.String() - } - - return reserves, nil -} - func (t *PoolTracker) queryRPC( ctx context.Context, poolAddress string, - vault string, - poolType string, overrides map[common.Address]gethclient.OverrideAccount, -) (*rpcRes, error) { +) (*RpcResult, error) { var ( - poolTokens PoolTokens - swapFeePercentage *big.Int - pausedState PausedState - ampParams AmplificationParameter + aggregateFeePercentages AggregateFeePercentage + stablePoolDynamicData StablePoolDynamicData + poolTokenInfo PoolTokenInfo ) - req := t.ethrpcClient.R(). - SetContext(ctx). - SetRequireSuccess(true) + req := t.ethrpcClient.R().SetContext(ctx).SetRequireSuccess(true) if overrides != nil { req.SetOverrides(overrides) } - req.AddCall(ðrpc.Call{ - ABI: shared.VaultABI, - Target: vault, - Method: shared.VaultMethodGetPoolTokens, - Params: []interface{}{common.HexToHash(poolAddress)}, - }, []interface{}{&poolTokens}) - req.AddCall(ðrpc.Call{ ABI: poolABI, Target: poolAddress, - Method: shared.PoolMethodGetAmplificationParameter, - }, []interface{}{&Params}) - + Method: shared.PoolMethodGetAggregateFeePercentages, + }, []interface{}{&aggregateFeePercentages}) req.AddCall(ðrpc.Call{ ABI: poolABI, Target: poolAddress, - Method: shared.PoolMethodGetSwapFeePercentage, - }, []interface{}{&swapFeePercentage}) - + Method: poolMethodGetStablePoolDynamicData, + }, []interface{}{&stablePoolDynamicData}) req.AddCall(ðrpc.Call{ ABI: poolABI, Target: poolAddress, - Method: shared.PoolMethodGetPausedState, - }, []interface{}{&pausedState}) + Method: shared.PoolMethodGetTokenInfo, + }, []interface{}{&poolTokenInfo}) res, err := req.TryBlockAndAggregate() if err != nil { @@ -237,15 +178,16 @@ func (t *PoolTracker) queryRPC( return nil, err } - return &rpcRes{ - Amp: ampParams.Value, - PoolTokens: poolTokens, - SwapFeePercentage: swapFeePercentage, - PausedState: pausedState, - BlockNumber: res.BlockNumber.Uint64(), + return &RpcResult{ + Tokens: poolTokenInfo.Tokens, + BalancesRaw: poolTokenInfo.BalancesRaw, + BalancesLiveScaled18: stablePoolDynamicData.BalancesLiveScaled18, + TokenRates: stablePoolDynamicData.TokenRates, + StaticSwapFeePercentage: stablePoolDynamicData.StaticSwapFeePercentage, + AggregateSwapFeePercentage: aggregateFeePercentages.AggregateSwapFeePercentage, + AmplificationParameter: stablePoolDynamicData.AmplificationParameter, + IsPoolPaused: stablePoolDynamicData.IsPoolPaused, + IsPoolInRecoveryMode: stablePoolDynamicData.IsPoolInRecoveryMode, + BlockNumber: res.BlockNumber.Uint64(), }, nil } - -func isNotPaused(pausedState PausedState) bool { - return time.Now().Unix() > pausedState.BufferPeriodEndTime.Int64() || !pausedState.Paused -} diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index d46fb741c..d692b1e4a 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -156,18 +156,18 @@ func (u *PoolsListUpdater) initPool(subgraphPool *shared.SubgraphPool, vault str reserves[j] = "0" } - staticExtra := StaticExtra{ + staticExtraBytes, err := json.Marshal(&StaticExtra{ PoolType: PoolType, PoolVersion: poolVersion, Vault: vault, - } - staticExtraBytes, err := json.Marshal(staticExtra) + }) if err != nil { return entity.Pool{}, err } - extra := Extra{DecimalScalingFactors: scalingFactors} - extraBytes, err := json.Marshal(extra) + extraBytes, err := json.Marshal(&Extra{ + DecimalScalingFactors: scalingFactors, + }) if err != nil { return entity.Pool{}, err } diff --git a/pkg/liquidity-source/balancer-v3/stable/type.go b/pkg/liquidity-source/balancer-v3/stable/type.go index 31ce3055a..6c3649c85 100644 --- a/pkg/liquidity-source/balancer-v3/stable/type.go +++ b/pkg/liquidity-source/balancer-v3/stable/type.go @@ -3,6 +3,7 @@ package stable import ( "math/big" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" ) @@ -13,13 +14,13 @@ type Gas struct { type Extra struct { AmplificationParameter *uint256.Int `json:"amplificationParameter"` - SwapFeePercentage *uint256.Int `json:"swapFeePercentage"` + StaticSwapFeePercentage *uint256.Int `json:"staticSwapFeePercentage"` AggregateSwapFeePercentage *uint256.Int `json:"aggregateSwapFeePercentage"` BalancesLiveScaled18 []*uint256.Int `json:"balancesLiveScaled18"` DecimalScalingFactors []*uint256.Int `json:"decimalScalingFactors"` TokenRates []*uint256.Int `json:"tokenRates"` IsPaused bool `json:"isPaused"` - IsVaultLocked bool `json:"isVaultLocked"` + IsInRecoveryMode bool `json:"isInRecoveryMode"` } type StaticExtra struct { @@ -46,6 +47,41 @@ type AmplificationParameter struct { Precision *big.Int } +type AggregateFeePercentage struct { + AggregateSwapFeePercentage *big.Int + AggregateYieldFeePercentage *big.Int +} + +type StablePoolDynamicData struct { + BalancesLiveScaled18 []*big.Int + TokenRates []*big.Int + StaticSwapFeePercentage *big.Int + TotalSupply *big.Int + BptRate *big.Int + AmplificationParameter *big.Int + IsAmpUpdating bool + StartValue *big.Int + EndValue *big.Int + StartTime uint32 + EndTime uint32 + IsPoolInitialized bool + IsPoolPaused bool + IsPoolInRecoveryMode bool +} + +type PoolTokenInfo struct { + Tokens []common.Address + TokenInfo []TokenInfo + BalancesRaw []*big.Int + LastBalancesLiveScaled18 []*big.Int +} + +type TokenInfo struct { + TokenType shared.TokenType + IRateProvider common.Address + PaysYieldFees bool +} + type PoolMetaInfo struct { Vault string `json:"vault"` PoolType string `json:"poolType"` @@ -54,13 +90,17 @@ type PoolMetaInfo struct { BlockNumber uint64 `json:"blockNumber"` } -type rpcRes struct { - Amp *big.Int - PoolTokens PoolTokens - SwapFeePercentage *big.Int - ScalingFactors []*big.Int - PausedState PausedState - BlockNumber uint64 +type RpcResult struct { + Tokens []common.Address + BalancesRaw []*big.Int + BalancesLiveScaled18 []*big.Int + TokenRates []*big.Int + StaticSwapFeePercentage *big.Int + AggregateSwapFeePercentage *big.Int + AmplificationParameter *big.Int + IsPoolPaused bool + IsPoolInRecoveryMode bool + BlockNumber uint64 } type SwapInfo struct { diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go index 33c3fb06c..39cc39dfe 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go @@ -9,12 +9,9 @@ import ( "github.com/KyberNetwork/ethrpc" "github.com/KyberNetwork/logger" - "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" ) @@ -144,54 +141,56 @@ func (t *PoolTracker) queryRPC( poolID string, vault string, ) (*rpcRes, error) { - var ( - poolTokens PoolTokens - swapFeePercentage *big.Int - pausedState PausedState - ) - - req := t.ethrpcClient.R(). - SetContext(ctx). - SetRequireSuccess(true) - - req.AddCall(ðrpc.Call{ - ABI: shared.VaultABI, - Target: vault, - Method: shared.VaultMethodGetPoolTokens, - Params: []interface{}{common.HexToHash(poolID)}, - }, []interface{}{&poolTokens}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: shared.PoolMethodGetSwapFeePercentage, - }, []interface{}{&swapFeePercentage}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: shared.PoolMethodGetPausedState, - }, []interface{}{&pausedState}) - - res, err := req.TryBlockAndAggregate() - if err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": poolAddress, - }).Error(err.Error()) - - return nil, err - } - - swapFeePercentageU256, _ := uint256.FromBig(swapFeePercentage) - - return &rpcRes{ - PoolTokens: poolTokens, - SwapFeePercentage: swapFeePercentageU256, - PausedState: pausedState, - BlockNumber: res.BlockNumber.Uint64(), - }, nil + // var ( + // poolTokens PoolTokens + // swapFeePercentage *big.Int + // pausedState PausedState + // ) + + // req := t.ethrpcClient.R(). + // SetContext(ctx). + // SetRequireSuccess(true) + + // req.AddCall(ðrpc.Call{ + // ABI: shared.VaultABI, + // Target: vault, + // Method: shared.VaultMethodGetPoolTokens, + // Params: []interface{}{common.HexToHash(poolID)}, + // }, []interface{}{&poolTokens}) + + // req.AddCall(ðrpc.Call{ + // ABI: poolABI, + // Target: poolAddress, + // Method: shared.PoolMethodGetSwapFeePercentage, + // }, []interface{}{&swapFeePercentage}) + + // req.AddCall(ðrpc.Call{ + // ABI: poolABI, + // Target: poolAddress, + // Method: shared.PoolMethodGetPausedState, + // }, []interface{}{&pausedState}) + + // res, err := req.TryBlockAndAggregate() + // if err != nil { + // logger.WithFields(logger.Fields{ + // "dexId": t.config.DexID, + // "dexType": DexType, + // "poolAddress": poolAddress, + // }).Error(err.Error()) + + // return nil, err + // } + + // swapFeePercentageU256, _ := uint256.FromBig(swapFeePercentage) + + // return &rpcRes{ + // PoolTokens: poolTokens, + // SwapFeePercentage: swapFeePercentageU256, + // PausedState: pausedState, + // BlockNumber: res.BlockNumber.Uint64(), + // }, nil + + return nil, nil } func isNotPaused(pausedState PausedState) bool { diff --git a/pkg/valueobject/exchange.go b/pkg/valueobject/exchange.go index 5593bb0f4..03921b415 100644 --- a/pkg/valueobject/exchange.go +++ b/pkg/valueobject/exchange.go @@ -180,6 +180,9 @@ var ( ExchangeGyroscope3CLP Exchange = "gyroscope-3clp" ExchangeGyroscopeECLP Exchange = "gyroscope-eclp" + ExchangeBalancerV3Weighted Exchange = "balancer-v3-weighted" + ExchangeBalancerV3Stable Exchange = "balancer-v3-stable" + ExchangeSynthSwapPerp Exchange = "synthswap-perp" ExchangeSwapBasedPerp Exchange = "swapbased-perp" ExchangeBMX Exchange = "bmx" @@ -538,6 +541,8 @@ var AMMSourceSet = map[Exchange]struct{}{ ExchangeBalancerV2Weighted: {}, ExchangeBalancerV2Stable: {}, ExchangeBalancerV2ComposableStable: {}, + ExchangeBalancerV3Weighted: {}, + ExchangeBalancerV3Stable: {}, ExchangeBeethovenXWeighted: {}, ExchangeBeethovenXStable: {}, ExchangeBeethovenXComposableStable: {}, From fa7563f7324a3d150a1e8900ec0105539e3b3366 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Mon, 30 Dec 2024 22:27:05 +0700 Subject: [PATCH 10/39] tmp --- .../balancer-v3/math/weighted_math_test.go | 305 +++++++++--------- .../balancer-v3/shared/constant.go | 2 +- .../balancer-v3/shared/pools_list_updater.go | 2 +- .../balancer-v3/shared/subgraph.go | 6 +- .../balancer-v3/stable/constant.go | 2 +- .../balancer-v3/stable/pool_simulator.go | 4 +- .../balancer-v3/stable/pool_tracker.go | 14 +- .../balancer-v3/stable/pools_list_updater.go | 2 +- .../balancer-v3/stable/type.go | 34 +- pkg/pooltypes/pooltypes.go | 4 + 10 files changed, 185 insertions(+), 190 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go index ed5050a4e..f91cab3ae 100644 --- a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go @@ -1,158 +1,151 @@ package math -import ( - "testing" - - "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" -) - -func TestCalcOutGivenIn(t *testing.T) { - t.Run("1.should return OK", func(t *testing.T) { - // input - balanceIn := uint256.MustFromDecimal("2133741937219414819371293") - weightIn := uint256.MustFromDecimal("10") - balanceOut := uint256.MustFromDecimal("548471973423647283412313") - weightOut := uint256.MustFromDecimal("20") - amountIn := uint256.MustFromDecimal("21481937129313123729") - - // expected - expected := "2760912942840907991" - - // calculation - result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - - // assert - assert.Nil(t, err) - assert.Equal(t, expected, result.Dec()) - }) - - t.Run("2.should return OK", func(t *testing.T) { - // input - balanceIn := uint256.MustFromDecimal("92174932794319461529478329") - weightIn := uint256.MustFromDecimal("15") - balanceOut := uint256.MustFromDecimal("2914754379179427149231562") - weightOut := uint256.MustFromDecimal("5") - amountIn := uint256.MustFromDecimal("14957430248210") - - // expected - expected := "1389798609308" - - // calculation - result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - - // assert - assert.Nil(t, err) - assert.Equal(t, expected, result.Dec()) - }) - - t.Run("3.should return OK", func(t *testing.T) { - // input - balanceIn := uint256.MustFromDecimal("28430120665864259131432") - weightIn := uint256.MustFromDecimal("100000000000000000") - balanceOut := uint256.MustFromDecimal("10098902157921113397") - weightOut := uint256.MustFromDecimal("30000000000000000") - amountIn := uint256.MustFromDecimal("6125185803357185587126") - - // expected - expected := "4828780052665314529" - - // calculation - result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - - // assert - assert.Nil(t, err) - assert.Equal(t, expected, result.Dec()) - }) - - t.Run("4.should return error exceed amount in ratio", func(t *testing.T) { - // input - balanceIn := uint256.MustFromDecimal("92174932794319461529478329") - weightIn := uint256.MustFromDecimal("15") - balanceOut := uint256.MustFromDecimal("2914754379179427149231562") - weightOut := uint256.MustFromDecimal("5") - amountIn := uint256.MustFromDecimal("92174932794319461529478329") - - // calculation - _, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - - // assert - assert.ErrorIs(t, err, ErrMaxInRatio) - }) -} - -func Test_weightedMath_CalcInGivenOut(t *testing.T) { - type args struct { - balanceIn *uint256.Int - weightIn *uint256.Int - balanceOut *uint256.Int - weightOut *uint256.Int - amountOut *uint256.Int - } - tests := []struct { - name string - args args - want *uint256.Int - wantErr error - }{ - { - name: "1. should return OK", - args: args{ - balanceIn: uint256.MustFromDecimal("2133741937219414819371293"), - weightIn: uint256.MustFromDecimal("10"), - balanceOut: uint256.MustFromDecimal("548471973423647283412313"), - weightOut: uint256.MustFromDecimal("20"), - amountOut: uint256.MustFromDecimal("21481937129313123729"), - }, - want: uint256.MustFromDecimal("167153858139050441751"), - wantErr: nil, - }, - { - name: "2. should return OK", - args: args{ - balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), - weightIn: uint256.MustFromDecimal("15"), - balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), - weightOut: uint256.MustFromDecimal("5"), - amountOut: uint256.MustFromDecimal("14957430248210"), - }, - want: uint256.MustFromDecimal("158591027569670"), - wantErr: nil, - }, - { - name: "3. should return OK", - args: args{ - balanceIn: uint256.MustFromDecimal("28430120665864259131432"), - weightIn: uint256.MustFromDecimal("100000000000000000"), - balanceOut: uint256.MustFromDecimal("100989021579211133970000"), - weightOut: uint256.MustFromDecimal("30000000000000000"), - amountOut: uint256.MustFromDecimal("6125185803357185587126"), - }, - want: uint256.MustFromDecimal("538695515311227973058"), - wantErr: nil, - }, - { - name: "4. should return error exceed amount out ratio", - args: args{ - balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), - weightIn: uint256.MustFromDecimal("15"), - balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), - weightOut: uint256.MustFromDecimal("5"), - amountOut: uint256.MustFromDecimal("2914754379179427149231562"), - }, - want: nil, - wantErr: ErrMaxOutRatio, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := &weightedMath{} - got, err := l.CalcInGivenOut(tt.args.balanceIn, tt.args.weightIn, tt.args.balanceOut, tt.args.weightOut, tt.args.amountOut) - if err != nil { - assert.ErrorIsf(t, err, tt.wantErr, "weightedMath.CalcInGivenOut() error = %v, wantErr %v", err, tt.wantErr) - return - } - assert.Equalf(t, tt.want, got, "weightedMath.CalcInGivenOut() = %v, want %v", got, tt.want) - }) - } -} +// func TestCalcOutGivenIn(t *testing.T) { +// t.Run("1.should return OK", func(t *testing.T) { +// // input +// balanceIn := uint256.MustFromDecimal("2133741937219414819371293") +// weightIn := uint256.MustFromDecimal("10") +// balanceOut := uint256.MustFromDecimal("548471973423647283412313") +// weightOut := uint256.MustFromDecimal("20") +// amountIn := uint256.MustFromDecimal("21481937129313123729") + +// // expected +// expected := "2760912942840907991" + +// // calculation +// result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + +// // assert +// assert.Nil(t, err) +// assert.Equal(t, expected, result.Dec()) +// }) + +// t.Run("2.should return OK", func(t *testing.T) { +// // input +// balanceIn := uint256.MustFromDecimal("92174932794319461529478329") +// weightIn := uint256.MustFromDecimal("15") +// balanceOut := uint256.MustFromDecimal("2914754379179427149231562") +// weightOut := uint256.MustFromDecimal("5") +// amountIn := uint256.MustFromDecimal("14957430248210") + +// // expected +// expected := "1389798609308" + +// // calculation +// result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + +// // assert +// assert.Nil(t, err) +// assert.Equal(t, expected, result.Dec()) +// }) + +// t.Run("3.should return OK", func(t *testing.T) { +// // input +// balanceIn := uint256.MustFromDecimal("28430120665864259131432") +// weightIn := uint256.MustFromDecimal("100000000000000000") +// balanceOut := uint256.MustFromDecimal("10098902157921113397") +// weightOut := uint256.MustFromDecimal("30000000000000000") +// amountIn := uint256.MustFromDecimal("6125185803357185587126") + +// // expected +// expected := "4828780052665314529" + +// // calculation +// result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + +// // assert +// assert.Nil(t, err) +// assert.Equal(t, expected, result.Dec()) +// }) + +// t.Run("4.should return error exceed amount in ratio", func(t *testing.T) { +// // input +// balanceIn := uint256.MustFromDecimal("92174932794319461529478329") +// weightIn := uint256.MustFromDecimal("15") +// balanceOut := uint256.MustFromDecimal("2914754379179427149231562") +// weightOut := uint256.MustFromDecimal("5") +// amountIn := uint256.MustFromDecimal("92174932794319461529478329") + +// // calculation +// _, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) + +// // assert +// assert.ErrorIs(t, err, ErrMaxInRatio) +// }) +// } + +// func Test_weightedMath_CalcInGivenOut(t *testing.T) { +// type args struct { +// balanceIn *uint256.Int +// weightIn *uint256.Int +// balanceOut *uint256.Int +// weightOut *uint256.Int +// amountOut *uint256.Int +// } +// tests := []struct { +// name string +// args args +// want *uint256.Int +// wantErr error +// }{ +// { +// name: "1. should return OK", +// args: args{ +// balanceIn: uint256.MustFromDecimal("2133741937219414819371293"), +// weightIn: uint256.MustFromDecimal("10"), +// balanceOut: uint256.MustFromDecimal("548471973423647283412313"), +// weightOut: uint256.MustFromDecimal("20"), +// amountOut: uint256.MustFromDecimal("21481937129313123729"), +// }, +// want: uint256.MustFromDecimal("167153858139050441751"), +// wantErr: nil, +// }, +// { +// name: "2. should return OK", +// args: args{ +// balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), +// weightIn: uint256.MustFromDecimal("15"), +// balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), +// weightOut: uint256.MustFromDecimal("5"), +// amountOut: uint256.MustFromDecimal("14957430248210"), +// }, +// want: uint256.MustFromDecimal("158591027569670"), +// wantErr: nil, +// }, +// { +// name: "3. should return OK", +// args: args{ +// balanceIn: uint256.MustFromDecimal("28430120665864259131432"), +// weightIn: uint256.MustFromDecimal("100000000000000000"), +// balanceOut: uint256.MustFromDecimal("100989021579211133970000"), +// weightOut: uint256.MustFromDecimal("30000000000000000"), +// amountOut: uint256.MustFromDecimal("6125185803357185587126"), +// }, +// want: uint256.MustFromDecimal("538695515311227973058"), +// wantErr: nil, +// }, +// { +// name: "4. should return error exceed amount out ratio", +// args: args{ +// balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), +// weightIn: uint256.MustFromDecimal("15"), +// balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), +// weightOut: uint256.MustFromDecimal("5"), +// amountOut: uint256.MustFromDecimal("2914754379179427149231562"), +// }, +// want: nil, +// wantErr: ErrMaxOutRatio, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// l := &weightedMath{} +// got, err := l.CalcInGivenOut(tt.args.balanceIn, tt.args.weightIn, tt.args.balanceOut, tt.args.weightOut, tt.args.amountOut) +// if err != nil { +// assert.ErrorIsf(t, err, tt.wantErr, "weightedMath.CalcInGivenOut() error = %v, wantErr %v", err, tt.wantErr) +// return +// } +// assert.Equalf(t, tt.want, got, "weightedMath.CalcInGivenOut() = %v, want %v", got, tt.want) +// }) +// } +// } diff --git a/pkg/liquidity-source/balancer-v3/shared/constant.go b/pkg/liquidity-source/balancer-v3/shared/constant.go index 052c1fef3..cc24ee147 100644 --- a/pkg/liquidity-source/balancer-v3/shared/constant.go +++ b/pkg/liquidity-source/balancer-v3/shared/constant.go @@ -16,7 +16,7 @@ const ( type ( Rounding int - TokenType int + TokenType uint8 ) const ( diff --git a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go index 8494f5c22..8da36e004 100644 --- a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go @@ -92,7 +92,7 @@ func (u *PoolsListUpdater) querySubgraph(ctx context.Context, lastBlockTimestamp } if len(response.Pools) != 0 { - lastBlockTimestamp = response.Pools[len(response.Pools)-1].CreateTime + lastBlockTimestamp, _ = new(big.Int).SetString(response.Pools[len(response.Pools)-1].CreateTime, 10) } return response.Pools, lastBlockTimestamp, nil diff --git a/pkg/liquidity-source/balancer-v3/shared/subgraph.go b/pkg/liquidity-source/balancer-v3/shared/subgraph.go index 8694fd63a..f22c5d2f9 100644 --- a/pkg/liquidity-source/balancer-v3/shared/subgraph.go +++ b/pkg/liquidity-source/balancer-v3/shared/subgraph.go @@ -6,9 +6,9 @@ import ( ) type SubgraphPool struct { - ID string `json:"id"` - Address string `json:"address"` - CreateTime *big.Int `json:"blockTimestamp"` + ID string `json:"id"` + Address string `json:"address"` + CreateTime string `json:"blockTimestamp"` Tokens []struct { Address string `json:"address"` Decimals int `json:"decimals"` diff --git a/pkg/liquidity-source/balancer-v3/stable/constant.go b/pkg/liquidity-source/balancer-v3/stable/constant.go index b5505392d..016d8f88b 100644 --- a/pkg/liquidity-source/balancer-v3/stable/constant.go +++ b/pkg/liquidity-source/balancer-v3/stable/constant.go @@ -1,7 +1,7 @@ package stable const ( - DexType = "balancer-v2-stable" + DexType = "balancer-v3-stable" PoolType = "StablePool" diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index 13b706ab5..a3649371c 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -119,7 +119,7 @@ func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk IndexOut: indexOut, AmountGiven: amountOut, DecimalScalingFactor: p.decimalScalingFactors[indexOut], - TokenRate: p.decimalScalingFactors[indexOut], + TokenRate: p.tokenRates[indexOut], AmplificationParameter: p.amplificationParameter, SwapFeePercentage: p.swapFeePercentage, AggregateSwapFeePercentage: p.aggregateSwapFeePercentage, @@ -281,7 +281,7 @@ func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool IndexOut: indexOut, AmountGiven: amountIn, DecimalScalingFactor: p.decimalScalingFactors[indexIn], - TokenRate: p.decimalScalingFactors[indexIn], + TokenRate: p.tokenRates[indexIn], AmplificationParameter: p.amplificationParameter, SwapFeePercentage: p.swapFeePercentage, AggregateSwapFeePercentage: p.aggregateSwapFeePercentage, diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index db53c8392..e9b27d591 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -174,20 +174,18 @@ func (t *PoolTracker) queryRPC( "dexType": DexType, "poolAddress": poolAddress, }).Error(err.Error()) - return nil, err } return &RpcResult{ - Tokens: poolTokenInfo.Tokens, BalancesRaw: poolTokenInfo.BalancesRaw, - BalancesLiveScaled18: stablePoolDynamicData.BalancesLiveScaled18, - TokenRates: stablePoolDynamicData.TokenRates, - StaticSwapFeePercentage: stablePoolDynamicData.StaticSwapFeePercentage, + BalancesLiveScaled18: stablePoolDynamicData.Data.BalancesLiveScaled18, + TokenRates: stablePoolDynamicData.Data.TokenRates, + StaticSwapFeePercentage: stablePoolDynamicData.Data.StaticSwapFeePercentage, AggregateSwapFeePercentage: aggregateFeePercentages.AggregateSwapFeePercentage, - AmplificationParameter: stablePoolDynamicData.AmplificationParameter, - IsPoolPaused: stablePoolDynamicData.IsPoolPaused, - IsPoolInRecoveryMode: stablePoolDynamicData.IsPoolInRecoveryMode, + AmplificationParameter: stablePoolDynamicData.Data.AmplificationParameter, + IsPoolPaused: stablePoolDynamicData.Data.IsPoolPaused, + IsPoolInRecoveryMode: stablePoolDynamicData.Data.IsPoolInRecoveryMode, BlockNumber: res.BlockNumber.Uint64(), }, nil } diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index d692b1e4a..7e28d76f1 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -142,7 +142,7 @@ func (u *PoolsListUpdater) initPool(subgraphPool *shared.SubgraphPool, vault str ) for j, token := range subgraphPool.Tokens { - scalingFactors[j], err = uint256.FromHex(token.ScalingFactor) + scalingFactors[j], err = uint256.FromDecimal(token.ScalingFactor) if err != nil { return entity.Pool{}, err } diff --git a/pkg/liquidity-source/balancer-v3/stable/type.go b/pkg/liquidity-source/balancer-v3/stable/type.go index 6c3649c85..88efd2cd5 100644 --- a/pkg/liquidity-source/balancer-v3/stable/type.go +++ b/pkg/liquidity-source/balancer-v3/stable/type.go @@ -3,7 +3,6 @@ package stable import ( "math/big" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" ) @@ -53,20 +52,22 @@ type AggregateFeePercentage struct { } type StablePoolDynamicData struct { - BalancesLiveScaled18 []*big.Int - TokenRates []*big.Int - StaticSwapFeePercentage *big.Int - TotalSupply *big.Int - BptRate *big.Int - AmplificationParameter *big.Int - IsAmpUpdating bool - StartValue *big.Int - EndValue *big.Int - StartTime uint32 - EndTime uint32 - IsPoolInitialized bool - IsPoolPaused bool - IsPoolInRecoveryMode bool + Data struct { + BalancesLiveScaled18 []*big.Int + TokenRates []*big.Int + StaticSwapFeePercentage *big.Int + TotalSupply *big.Int + BptRate *big.Int + AmplificationParameter *big.Int + StartValue *big.Int + EndValue *big.Int + StartTime uint32 + EndTime uint32 + IsAmpUpdating bool + IsPoolInitialized bool + IsPoolPaused bool + IsPoolInRecoveryMode bool + } } type PoolTokenInfo struct { @@ -77,7 +78,7 @@ type PoolTokenInfo struct { } type TokenInfo struct { - TokenType shared.TokenType + TokenType uint8 IRateProvider common.Address PaysYieldFees bool } @@ -91,7 +92,6 @@ type PoolMetaInfo struct { } type RpcResult struct { - Tokens []common.Address BalancesRaw []*big.Int BalancesLiveScaled18 []*big.Int TokenRates []*big.Int diff --git a/pkg/pooltypes/pooltypes.go b/pkg/pooltypes/pooltypes.go index 7bf637985..6016f3153 100644 --- a/pkg/pooltypes/pooltypes.go +++ b/pkg/pooltypes/pooltypes.go @@ -6,6 +6,8 @@ import ( balancerv2composablestable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/composable-stable" balancerv2stable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/stable" balancerv2weighted "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/weighted" + balancerv3stable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/stable" + bancorv21 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bancor-v21" bancorv3 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bancor-v3" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bebop" @@ -212,6 +214,7 @@ type Types struct { BalancerV2Weighted string BalancerV2Stable string BalancerV2ComposableStable string + BalancerV3Stable string VelocoreV2CPMM string VelocoreV2WombatStable string Fulcrom string @@ -357,6 +360,7 @@ var ( BalancerV2Weighted: balancerv2weighted.DexType, BalancerV2Stable: balancerv2stable.DexType, BalancerV2ComposableStable: balancerv2composablestable.DexType, + BalancerV3Stable: balancerv3stable.DexType, VelocoreV2CPMM: velocorev2cpmm.DexType, VelocoreV2WombatStable: velocorev2wombatstable.DexType, Fulcrom: fulcrom.DexTypeFulcrom, From cd8ed285cc22de82afef966c4e54f0fe3cef5fc9 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Tue, 31 Dec 2024 15:22:05 +0700 Subject: [PATCH 11/39] tmp --- .../balancer-v3/hooks/base_hook.go | 18 + .../balancer-v3/hooks/directional_fee.go | 60 + .../balancer-v3/hooks/ihook.go | 12 + .../balancer-v3/hooks/stable_surge.go | 34 + .../balancer-v3/hooks/ve_BAL_fee_discount.go | 59 + .../balancer-v3/shared/abis.go | 6 +- .../balancer-v3/shared/abis/Vault.json | 2120 ------------ .../shared/abis/VaultExplorer.json | 1225 +++++++ .../shared/abis/VaultExtension.json | 2847 ----------------- .../balancer-v3/shared/constant.go | 19 +- .../balancer-v3/shared/embed.go | 7 +- .../balancer-v3/shared/error.go | 2 + .../balancer-v3/shared/pools_list_updater.go | 2 +- .../balancer-v3/shared/subgraph.go | 22 +- .../balancer-v3/shared/type.go | 85 +- .../balancer-v3/shared/vault.go | 128 - .../balancer-v3/stable/config.go | 10 +- .../balancer-v3/stable/constant.go | 3 +- .../balancer-v3/stable/pool_simulator.go | 91 +- .../balancer-v3/stable/pool_tracker.go | 127 +- .../balancer-v3/stable/pools_list_updater.go | 157 +- .../balancer-v3/stable/type.go | 32 +- .../balancer-v3/vault/const.go | 13 + .../balancer-v3/vault/error.go | 12 + .../{shared => vault}/scaling_helper.go | 2 +- .../balancer-v3/vault/vault.go | 180 ++ .../balancer-v3/vault/vault_hooks_lib.go | 41 + 27 files changed, 1962 insertions(+), 5352 deletions(-) create mode 100644 pkg/liquidity-source/balancer-v3/hooks/base_hook.go create mode 100644 pkg/liquidity-source/balancer-v3/hooks/directional_fee.go create mode 100644 pkg/liquidity-source/balancer-v3/hooks/ihook.go create mode 100644 pkg/liquidity-source/balancer-v3/hooks/stable_surge.go create mode 100644 pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go delete mode 100644 pkg/liquidity-source/balancer-v3/shared/abis/Vault.json create mode 100644 pkg/liquidity-source/balancer-v3/shared/abis/VaultExplorer.json delete mode 100644 pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json delete mode 100644 pkg/liquidity-source/balancer-v3/shared/vault.go create mode 100644 pkg/liquidity-source/balancer-v3/vault/const.go create mode 100644 pkg/liquidity-source/balancer-v3/vault/error.go rename pkg/liquidity-source/balancer-v3/{shared => vault}/scaling_helper.go (98%) create mode 100644 pkg/liquidity-source/balancer-v3/vault/vault.go create mode 100644 pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go diff --git a/pkg/liquidity-source/balancer-v3/hooks/base_hook.go b/pkg/liquidity-source/balancer-v3/hooks/base_hook.go new file mode 100644 index 000000000..fe4a30046 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/hooks/base_hook.go @@ -0,0 +1,18 @@ +package hooks + +import "github.com/holiman/uint256" + +type baseHook struct{} + +var BaseHook = &baseHook{} + +func (h *baseHook) OnBeforeSwap() {} + +func (h *baseHook) OnAfterSwap() {} + +func (h *baseHook) OnComputeDynamicSwapFeePercentage( + staticSwapFeePercentage, + amountGivenScaled18, + balanceIn, + balanceOut *uint256.Int, +) (bool, *uint256.Int, error) diff --git a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go new file mode 100644 index 000000000..e1cfb0c8f --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go @@ -0,0 +1,60 @@ +package hooks + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/holiman/uint256" +) + +type directionalFeeHook struct { + staticSwapFeePercentage *uint256.Int +} + +func NewDirectionalFeeHook(staticSwapFeePercentage *uint256.Int) *directionalFeeHook { + return &directionalFeeHook{ + staticSwapFeePercentage: staticSwapFeePercentage, + } +} + +func (h *directionalFeeHook) OnComputeDynamicSwapFeePercentage(param shared.PoolSwapParams) (bool, *uint256.Int, error) { + calculatedSwapFeePercentage, err := h.calculatedExpectedSwapFeePercentage(param.BalancesLiveScaled18[param.IndexIn], + param.BalancesLiveScaled18[param.IndexOut], param.AmountGivenScaled18) + if err != nil { + return false, nil, err + } + + if calculatedSwapFeePercentage.Gt(h.staticSwapFeePercentage) { + return true, calculatedSwapFeePercentage, nil + } + + return false, new(uint256.Int).Set(h.staticSwapFeePercentage), nil +} + +func (h *directionalFeeHook) calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, swapAmount *uint256.Int) (*uint256.Int, error) { + finalBalanceTokenIn, err := math.Add(balanceIn, swapAmount) + if err != nil { + return nil, err + } + + finalBalanceTokenOut, err := math.Sub(balanceOut, swapAmount) + if err != nil { + return nil, err + } + + if finalBalanceTokenIn.Gt(finalBalanceTokenOut) { + diff, err := math.Sub(finalBalanceTokenIn, finalBalanceTokenOut) + if err != nil { + return nil, err + } + + totalLiquidity, err := math.Add(finalBalanceTokenIn, finalBalanceTokenOut) + if err != nil { + return nil, err + } + + diff, err = math.DivDown(diff, totalLiquidity) + return diff, err + } + + return math.ZERO, nil +} diff --git a/pkg/liquidity-source/balancer-v3/hooks/ihook.go b/pkg/liquidity-source/balancer-v3/hooks/ihook.go new file mode 100644 index 000000000..4efc899fe --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/hooks/ihook.go @@ -0,0 +1,12 @@ +package hooks + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/holiman/uint256" +) + +type IHook interface { + OnBeforeSwap() + OnAfterSwap() + OnComputeDynamicSwapFeePercentage(param shared.PoolSwapParams) (bool, *uint256.Int, error) +} diff --git a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go new file mode 100644 index 000000000..1b4080cde --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go @@ -0,0 +1,34 @@ +package hooks + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/holiman/uint256" +) + +type StableSurgeHook struct{} + +func NewStableSurgeHook() *StableSurgeHook { + return &StableSurgeHook{} +} + +func (h *StableSurgeHook) OnComputeDynamicSwapFeePercentage( + staticSwapFeePercentage, + amountGivenScaled18, + balanceIn, + balanceOut *uint256.Int, +) (bool, *uint256.Int, error) { + calculatedSwapFeePercentage, err := h.calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, amountGivenScaled18) + if err != nil { + return false, nil, err + } + + if calculatedSwapFeePercentage.Gt(staticSwapFeePercentage) { + return true, calculatedSwapFeePercentage, nil + } + + return false, staticSwapFeePercentage, nil +} + +func (h *StableSurgeHook) getSurgeFeePercentage(params shared.VaultSwapParams) (*uint256.Int, error) { + // numTokens := +} diff --git a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go new file mode 100644 index 000000000..9c75c2b27 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go @@ -0,0 +1,59 @@ +package hooks + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/holiman/uint256" +) + +type VeBALFeeDiscountHook struct{} + +func NewVeBALFeeDiscountHook() *VeBALFeeDiscountHook { + return &VeBALFeeDiscountHook{} +} + +func (h *VeBALFeeDiscountHook) OnComputeDynamicSwapFeePercentage( + staticSwapFeePercentage, + amountGivenScaled18, + balanceIn, + balanceOut *uint256.Int, +) (bool, *uint256.Int, error) { + calculatedSwapFeePercentage, err := h.calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, amountGivenScaled18) + if err != nil { + return false, nil, err + } + + if calculatedSwapFeePercentage.Gt(staticSwapFeePercentage) { + return true, calculatedSwapFeePercentage, nil + } + + return false, staticSwapFeePercentage, nil +} + +func (h *VeBALFeeDiscountHook) calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, swapAmount *uint256.Int) (*uint256.Int, error) { + finalBalanceTokenIn, err := math.Add(balanceIn, swapAmount) + if err != nil { + return nil, err + } + + finalBalanceTokenOut, err := math.Sub(balanceOut, swapAmount) + if err != nil { + return nil, err + } + + if finalBalanceTokenIn.Gt(finalBalanceTokenOut) { + diff, err := math.Sub(finalBalanceTokenIn, finalBalanceTokenOut) + if err != nil { + return nil, err + } + + totalLiquidity, err := math.Add(finalBalanceTokenIn, finalBalanceTokenOut) + if err != nil { + return nil, err + } + + diff, err = math.DivDown(diff, totalLiquidity) + return diff, err + } + + return math.ZERO, nil +} diff --git a/pkg/liquidity-source/balancer-v3/shared/abis.go b/pkg/liquidity-source/balancer-v3/shared/abis.go index 10a31e7db..e564cd406 100644 --- a/pkg/liquidity-source/balancer-v3/shared/abis.go +++ b/pkg/liquidity-source/balancer-v3/shared/abis.go @@ -7,8 +7,7 @@ import ( ) var ( - VaultABI abi.ABI - VaultExtensionABI abi.ABI + VaultExplorerABI abi.ABI ) func init() { @@ -16,8 +15,7 @@ func init() { ABI *abi.ABI data []byte }{ - {&VaultABI, vaultJson}, - {&VaultExtensionABI, vaultExtensionJson}, + {&VaultExplorerABI, vaultExplorerJson}, } for _, b := range builder { diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json b/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json deleted file mode 100644 index e0b7a3521..000000000 --- a/pkg/liquidity-source/balancer-v3/shared/abis/Vault.json +++ /dev/null @@ -1,2120 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract IVaultExtension", - "name": "vaultExtension", - "type": "address" - }, - { - "internalType": "contract IAuthorizer", - "name": "authorizer", - "type": "address" - }, - { - "internalType": "contract IProtocolFeeController", - "name": "protocolFeeController", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AfterAddLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AfterInitializeHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AfterRemoveLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AfterSwapHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AllZeroInputs", - "type": "error" - }, - { - "inputs": [], - "name": "AmountGivenZero", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxAmountIn", - "type": "uint256" - } - ], - "name": "AmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOut", - "type": "uint256" - } - ], - "name": "AmountOutBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "BalanceNotSettled", - "type": "error" - }, - { - "inputs": [], - "name": "BalanceOverflow", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeAddLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeInitializeHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeRemoveLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeSwapHookFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxAmountIn", - "type": "uint256" - } - ], - "name": "BptAmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOut", - "type": "uint256" - } - ], - "name": "BptAmountOutBelowMin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "BufferAlreadyInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "BufferNotInitialized", - "type": "error" - }, - { - "inputs": [], - "name": "BufferSharesInvalidOwner", - "type": "error" - }, - { - "inputs": [], - "name": "BufferSharesInvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - } - ], - "name": "BufferTotalSupplyTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "CannotReceiveEth", - "type": "error" - }, - { - "inputs": [], - "name": "CannotSwapSameToken", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportAddLiquidityCustom", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportDonation", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportRemoveLiquidityCustom", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportUnbalancedLiquidity", - "type": "error" - }, - { - "inputs": [], - "name": "DynamicSwapFeeHookFailed", - "type": "error" - }, - { - "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": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "FeePrecisionTooHigh", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxAmountIn", - "type": "uint256" - } - ], - "name": "HookAdjustedAmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOut", - "type": "uint256" - } - ], - "name": "HookAdjustedAmountOutBelowMin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "HookAdjustedSwapLimit", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "poolHooksContract", - "type": "address" - }, - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "poolFactory", - "type": "address" - } - ], - "name": "HookRegistrationFailed", - "type": "error" - }, - { - "inputs": [], - "name": "InputLengthMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAddLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRemoveLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidToken", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenConfiguration", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenDecimals", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenType", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "InvalidUnderlyingToken", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "invariantRatio", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxInvariantRatio", - "type": "uint256" - } - ], - "name": "InvariantRatioAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "invariantRatio", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minInvariantRatio", - "type": "uint256" - } - ], - "name": "InvariantRatioBelowMin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "issuedShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minIssuedShares", - "type": "uint256" - } - ], - "name": "IssuedSharesBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "MaxTokens", - "type": "error" - }, - { - "inputs": [], - "name": "MinTokens", - "type": "error" - }, - { - "inputs": [], - "name": "MultipleNonZeroInputs", - "type": "error" - }, - { - "inputs": [], - "name": "NotEnoughBufferShares", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "expectedUnderlyingAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "actualUnderlyingAmount", - "type": "uint256" - } - ], - "name": "NotEnoughUnderlying", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "expectedWrappedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "actualWrappedAmount", - "type": "uint256" - } - ], - "name": "NotEnoughWrapped", - "type": "error" - }, - { - "inputs": [], - "name": "NotStaticCall", - "type": "error" - }, - { - "inputs": [], - "name": "NotVaultDelegateCall", - "type": "error" - }, - { - "inputs": [], - "name": "PauseBufferPeriodDurationTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "PercentageAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPauseWindowExpired", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - } - ], - "name": "PoolTotalSupplyTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "ProtocolFeesExceedTotalCollected", - "type": "error" - }, - { - "inputs": [], - "name": "QueriesDisabled", - "type": "error" - }, - { - "inputs": [], - "name": "QueriesDisabledPermanently", - "type": "error" - }, - { - "inputs": [], - "name": "QuoteResultSpoofed", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "RouterNotTrusted", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "int256", - "name": "value", - "type": "int256" - } - ], - "name": "SafeCastOverflowedIntToUint", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "SafeCastOverflowedUintToInt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "SenderIsNotVault", - "type": "error" - }, - { - "inputs": [], - "name": "SwapFeePercentageTooHigh", - "type": "error" - }, - { - "inputs": [], - "name": "SwapFeePercentageTooLow", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "SwapLimit", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "TokenAlreadyRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "TokenNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "expectedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "actualToken", - "type": "address" - } - ], - "name": "TokensMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "TradeAmountTooSmall", - "type": "error" - }, - { - "inputs": [], - "name": "VaultBuffersArePaused", - "type": "error" - }, - { - "inputs": [], - "name": "VaultIsNotUnlocked", - "type": "error" - }, - { - "inputs": [], - "name": "VaultNotPaused", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowDurationTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowExpired", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "WrapAmountTooSmall", - "type": "error" - }, - { - "inputs": [], - "name": "WrongProtocolFeeControllerDeployment", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "underlyingToken", - "type": "address" - } - ], - "name": "WrongUnderlyingToken", - "type": "error" - }, - { - "inputs": [], - "name": "WrongVaultAdminDeployment", - "type": "error" - }, - { - "inputs": [], - "name": "WrongVaultExtensionDeployment", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroDivision", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "aggregateSwapFeePercentage", - "type": "uint256" - } - ], - "name": "AggregateSwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "aggregateYieldFeePercentage", - "type": "uint256" - } - ], - "name": "AggregateYieldFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "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": true, - "internalType": "contract IAuthorizer", - "name": "newAuthorizer", - "type": "address" - } - ], - "name": "AuthorizerChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "burnedShares", - "type": "uint256" - } - ], - "name": "BufferSharesBurned", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "issuedShares", - "type": "uint256" - } - ], - "name": "BufferSharesMinted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "liquidityProvider", - "type": "address" - }, - { - "indexed": true, - "internalType": "enum AddLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "amountsAddedRaw", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "swapFeeAmountsRaw", - "type": "uint256[]" - } - ], - "name": "LiquidityAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountWrapped", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "LiquidityAddedToBuffer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "liquidityProvider", - "type": "address" - }, - { - "indexed": true, - "internalType": "enum RemoveLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "amountsRemovedRaw", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "swapFeeAmountsRaw", - "type": "uint256[]" - } - ], - "name": "LiquidityRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountWrapped", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "LiquidityRemovedFromBuffer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInitialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PoolPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "recoveryMode", - "type": "bool" - } - ], - "name": "PoolRecoveryModeStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "factory", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "components": [ - { - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "internalType": "address", - "name": "swapFeeManager", - "type": "address" - }, - { - "internalType": "address", - "name": "poolCreator", - "type": "address" - } - ], - "indexed": false, - "internalType": "struct PoolRoleAccounts", - "name": "roleAccounts", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "enableHookAdjustedAmounts", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallComputeDynamicSwapFee", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "address", - "name": "hooksContract", - "type": "address" - } - ], - "indexed": false, - "internalType": "struct HooksConfig", - "name": "hooksConfig", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "disableUnbalancedLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableRemoveLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableDonation", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "name": "PoolRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IProtocolFeeController", - "name": "newProtocolFeeController", - "type": "address" - } - ], - "name": "ProtocolFeeControllerChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeeAmount", - "type": "uint256" - } - ], - "name": "Swap", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "SwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "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": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "burnedShares", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "withdrawnUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "Unwrap", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "eventKey", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "eventData", - "type": "bytes" - } - ], - "name": "VaultAuxiliary", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "VaultBuffersPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "VaultPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [], - "name": "VaultQueriesDisabled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [], - "name": "VaultQueriesEnabled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "depositedUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "mintedShares", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "Wrap", - "type": "event" - }, - { - "stateMutability": "payable", - "type": "fallback" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "maxAmountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "minBptAmountOut", - "type": "uint256" - }, - { - "internalType": "enum AddLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct AddLiquidityParams", - "name": "params", - "type": "tuple" - } - ], - "name": "addLiquidity", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "bptAmountOut", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "returnData", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "enum WrappingDirection", - "name": "direction", - "type": "uint8" - }, - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountGivenRaw", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limitRaw", - "type": "uint256" - } - ], - "internalType": "struct BufferWrapOrUnwrapParams", - "name": "params", - "type": "tuple" - } - ], - "name": "erc4626BufferWrapOrUnwrap", - "outputs": [ - { - "internalType": "uint256", - "name": "amountCalculatedRaw", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountInRaw", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOutRaw", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getPoolTokenCountAndIndexOfToken", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getVaultExtension", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "reentrancyGuardEntered", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "maxBptAmountIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "minAmountsOut", - "type": "uint256[]" - }, - { - "internalType": "enum RemoveLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct RemoveLiquidityParams", - "name": "params", - "type": "tuple" - } - ], - "name": "removeLiquidity", - "outputs": [ - { - "internalType": "uint256", - "name": "bptAmountIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "amountsOut", - "type": "uint256[]" - }, - { - "internalType": "bytes", - "name": "returnData", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "sendTo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountHint", - "type": "uint256" - } - ], - "name": "settle", - "outputs": [ - { - "internalType": "uint256", - "name": "credit", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountGivenRaw", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limitRaw", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct VaultSwapParams", - "name": "vaultSwapParams", - "type": "tuple" - } - ], - "name": "swap", - "outputs": [ - { - "internalType": "uint256", - "name": "amountCalculated", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "unlock", - "outputs": [ - { - "internalType": "bytes", - "name": "result", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/VaultExplorer.json b/pkg/liquidity-source/balancer-v3/shared/abis/VaultExplorer.json new file mode 100644 index 000000000..2f3aafc02 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/shared/abis/VaultExplorer.json @@ -0,0 +1,1225 @@ +[ + { + "inputs": [ + { + "internalType": "contract IVault", + "name": "vault", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "tokenAllowance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "areBuffersPaused", + "outputs": [ + { + "internalType": "bool", + "name": "buffersPaused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "tokenBalance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "collectAggregateFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "components": [ + { + "internalType": "enum SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amountGivenScaled18", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "balancesScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "indexIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "indexOut", + "type": "uint256" + }, + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct PoolSwapParams", + "name": "swapParams", + "type": "tuple" + } + ], + "name": "computeDynamicSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "dynamicSwapFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getAddLiquidityCalledFlag", + "outputs": [ + { + "internalType": "bool", + "name": "liquidityAdded", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getAggregateFeePercentages", + "outputs": [ + { + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getAggregateSwapFeeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "swapFeeAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getAggregateYieldFeeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "yieldFeeAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "address", + "name": "authorizer", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getBptRate", + "outputs": [ + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "getBufferAsset", + "outputs": [ + { + "internalType": "address", + "name": "underlyingToken", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "getBufferBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "underlyingBalanceRaw", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "wrappedBalanceRaw", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBufferMinimumTotalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "bufferMinimumTotalSupply", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidityOwner", + "type": "address" + } + ], + "name": "getBufferOwnerShares", + "outputs": [ + { + "internalType": "uint256", + "name": "ownerShares", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBufferPeriodDuration", + "outputs": [ + { + "internalType": "uint32", + "name": "bufferPeriodDuration", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBufferPeriodEndTime", + "outputs": [ + { + "internalType": "uint32", + "name": "bufferPeriodEndTime", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC4626", + "name": "wrappedToken", + "type": "address" + } + ], + "name": "getBufferTotalShares", + "outputs": [ + { + "internalType": "uint256", + "name": "bufferShares", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getCurrentLiveBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getHooksConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "enableHookAdjustedAmounts", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallComputeDynamicSwapFee", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "address", + "name": "hooksContract", + "type": "address" + } + ], + "internalType": "struct HooksConfig", + "name": "hooksConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumPoolTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "maxTokens", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumPoolTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "minTokens", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumTradeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "minimumTradeAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumWrapAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "minimumWrapAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNonzeroDeltaCount", + "outputs": [ + { + "internalType": "uint256", + "name": "nonzeroDeltaCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPauseWindowEndTime", + "outputs": [ + { + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolConfig", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "bool", + "name": "disableUnbalancedLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableRemoveLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enableDonation", + "type": "bool" + } + ], + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "staticSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint40", + "name": "tokenDecimalDiffs", + "type": "uint40" + }, + { + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isPoolRegistered", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolPaused", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInRecoveryMode", + "type": "bool" + } + ], + "internalType": "struct PoolConfig", + "name": "poolConfig", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolData", + "outputs": [ + { + "components": [ + { + "internalType": "PoolConfigBits", + "name": "poolConfigBits", + "type": "bytes32" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + } + ], + "internalType": "struct PoolData", + "name": "poolData", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPoolMinimumTotalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "poolMinimumTotalSupply", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "poolPaused", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "poolPauseWindowEndTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "poolBufferPeriodEndTime", + "type": "uint32" + }, + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolRoleAccounts", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "internalType": "address", + "name": "swapFeeManager", + "type": "address" + }, + { + "internalType": "address", + "name": "poolCreator", + "type": "address" + } + ], + "internalType": "struct PoolRoleAccounts", + "name": "roleAccounts", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getPoolTokenCountAndIndexOfToken", + "outputs": [ + { + "internalType": "uint256", + "name": "tokenCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokenInfo", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "lastBalancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokenRates", + "outputs": [ + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolFeeController", + "outputs": [ + { + "internalType": "address", + "name": "protocolFeeController", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getReservesOf", + "outputs": [ + { + "internalType": "uint256", + "name": "reserveAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getStaticSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenDelta", + "outputs": [ + { + "internalType": "int256", + "name": "tokenDelta", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultAdmin", + "outputs": [ + { + "internalType": "address", + "name": "vaultAdmin", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultExtension", + "outputs": [ + { + "internalType": "address", + "name": "vaultExtension", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "vaultPaused", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "vaultPauseWindowEndTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "vaultBufferPeriodEndTime", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInRecoveryMode", + "outputs": [ + { + "internalType": "bool", + "name": "inRecoveryMode", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolPaused", + "outputs": [ + { + "internalType": "bool", + "name": "poolPaused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "registered", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isQueryDisabled", + "outputs": [ + { + "internalType": "bool", + "name": "queryDisabled", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isQueryDisabledPermanently", + "outputs": [ + { + "internalType": "bool", + "name": "queryDisabledPermanently", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isUnlocked", + "outputs": [ + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVaultPaused", + "outputs": [ + { + "internalType": "bool", + "name": "vaultPaused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "tokenTotalSupply", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json b/pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json deleted file mode 100644 index efccfd3c3..000000000 --- a/pkg/liquidity-source/balancer-v3/shared/abis/VaultExtension.json +++ /dev/null @@ -1,2847 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract IVault", - "name": "mainVault", - "type": "address" - }, - { - "internalType": "contract IVaultAdmin", - "name": "vaultAdmin", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AfterAddLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AfterInitializeHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AfterRemoveLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AfterSwapHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "AmountGivenZero", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxAmountIn", - "type": "uint256" - } - ], - "name": "AmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOut", - "type": "uint256" - } - ], - "name": "AmountOutBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "BalanceNotSettled", - "type": "error" - }, - { - "inputs": [], - "name": "BalanceOverflow", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeAddLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeInitializeHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeRemoveLiquidityHookFailed", - "type": "error" - }, - { - "inputs": [], - "name": "BeforeSwapHookFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxAmountIn", - "type": "uint256" - } - ], - "name": "BptAmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOut", - "type": "uint256" - } - ], - "name": "BptAmountOutBelowMin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "BufferAlreadyInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "BufferNotInitialized", - "type": "error" - }, - { - "inputs": [], - "name": "BufferSharesInvalidOwner", - "type": "error" - }, - { - "inputs": [], - "name": "BufferSharesInvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - } - ], - "name": "BufferTotalSupplyTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "CannotReceiveEth", - "type": "error" - }, - { - "inputs": [], - "name": "CannotSwapSameToken", - "type": "error" - }, - { - "inputs": [], - "name": "CodecOverflow", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportAddLiquidityCustom", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportDonation", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportRemoveLiquidityCustom", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportUnbalancedLiquidity", - "type": "error" - }, - { - "inputs": [], - "name": "DynamicSwapFeeHookFailed", - "type": "error" - }, - { - "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": "ErrorSelectorNotFound", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "FeePrecisionTooHigh", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxAmountIn", - "type": "uint256" - } - ], - "name": "HookAdjustedAmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOut", - "type": "uint256" - } - ], - "name": "HookAdjustedAmountOutBelowMin", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "HookAdjustedSwapLimit", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "poolHooksContract", - "type": "address" - }, - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "poolFactory", - "type": "address" - } - ], - "name": "HookRegistrationFailed", - "type": "error" - }, - { - "inputs": [], - "name": "InputLengthMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAddLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRemoveLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidToken", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenConfiguration", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenDecimals", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenType", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "InvalidUnderlyingToken", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "issuedShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minIssuedShares", - "type": "uint256" - } - ], - "name": "IssuedSharesBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "MaxTokens", - "type": "error" - }, - { - "inputs": [], - "name": "MinTokens", - "type": "error" - }, - { - "inputs": [], - "name": "NotEnoughBufferShares", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "expectedUnderlyingAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "actualUnderlyingAmount", - "type": "uint256" - } - ], - "name": "NotEnoughUnderlying", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "expectedWrappedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "actualWrappedAmount", - "type": "uint256" - } - ], - "name": "NotEnoughWrapped", - "type": "error" - }, - { - "inputs": [], - "name": "NotStaticCall", - "type": "error" - }, - { - "inputs": [], - "name": "NotVaultDelegateCall", - "type": "error" - }, - { - "inputs": [], - "name": "OutOfBounds", - "type": "error" - }, - { - "inputs": [], - "name": "PauseBufferPeriodDurationTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "PercentageAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPauseWindowExpired", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - } - ], - "name": "PoolTotalSupplyTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "ProtocolFeesExceedTotalCollected", - "type": "error" - }, - { - "inputs": [], - "name": "QueriesDisabled", - "type": "error" - }, - { - "inputs": [], - "name": "QueriesDisabledPermanently", - "type": "error" - }, - { - "inputs": [], - "name": "QuoteResultSpoofed", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "result", - "type": "bytes" - } - ], - "name": "Result", - "type": "error" - }, - { - "inputs": [], - "name": "RouterNotTrusted", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "SafeCastOverflowedUintToInt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "SenderIsNotVault", - "type": "error" - }, - { - "inputs": [], - "name": "SwapFeePercentageTooHigh", - "type": "error" - }, - { - "inputs": [], - "name": "SwapFeePercentageTooLow", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "SwapLimit", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "TokenAlreadyRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "TokenNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "expectedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "actualToken", - "type": "address" - } - ], - "name": "TokensMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "TokensNotSorted", - "type": "error" - }, - { - "inputs": [], - "name": "TradeAmountTooSmall", - "type": "error" - }, - { - "inputs": [], - "name": "VaultBuffersArePaused", - "type": "error" - }, - { - "inputs": [], - "name": "VaultIsNotUnlocked", - "type": "error" - }, - { - "inputs": [], - "name": "VaultNotPaused", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowDurationTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowExpired", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "WrapAmountTooSmall", - "type": "error" - }, - { - "inputs": [], - "name": "WrongProtocolFeeControllerDeployment", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "underlyingToken", - "type": "address" - } - ], - "name": "WrongUnderlyingToken", - "type": "error" - }, - { - "inputs": [], - "name": "WrongVaultAdminDeployment", - "type": "error" - }, - { - "inputs": [], - "name": "WrongVaultExtensionDeployment", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "aggregateSwapFeePercentage", - "type": "uint256" - } - ], - "name": "AggregateSwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "aggregateYieldFeePercentage", - "type": "uint256" - } - ], - "name": "AggregateYieldFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "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": true, - "internalType": "contract IAuthorizer", - "name": "newAuthorizer", - "type": "address" - } - ], - "name": "AuthorizerChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "burnedShares", - "type": "uint256" - } - ], - "name": "BufferSharesBurned", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "issuedShares", - "type": "uint256" - } - ], - "name": "BufferSharesMinted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "liquidityProvider", - "type": "address" - }, - { - "indexed": true, - "internalType": "enum AddLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "amountsAddedRaw", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "swapFeeAmountsRaw", - "type": "uint256[]" - } - ], - "name": "LiquidityAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountWrapped", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "LiquidityAddedToBuffer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "liquidityProvider", - "type": "address" - }, - { - "indexed": true, - "internalType": "enum RemoveLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "totalSupply", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "amountsRemovedRaw", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "swapFeeAmountsRaw", - "type": "uint256[]" - } - ], - "name": "LiquidityRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountWrapped", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "LiquidityRemovedFromBuffer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInitialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PoolPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "recoveryMode", - "type": "bool" - } - ], - "name": "PoolRecoveryModeStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "factory", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "components": [ - { - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "internalType": "address", - "name": "swapFeeManager", - "type": "address" - }, - { - "internalType": "address", - "name": "poolCreator", - "type": "address" - } - ], - "indexed": false, - "internalType": "struct PoolRoleAccounts", - "name": "roleAccounts", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "enableHookAdjustedAmounts", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallComputeDynamicSwapFee", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "address", - "name": "hooksContract", - "type": "address" - } - ], - "indexed": false, - "internalType": "struct HooksConfig", - "name": "hooksConfig", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "disableUnbalancedLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableRemoveLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableDonation", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "name": "PoolRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IProtocolFeeController", - "name": "newProtocolFeeController", - "type": "address" - } - ], - "name": "ProtocolFeeControllerChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeeAmount", - "type": "uint256" - } - ], - "name": "Swap", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "SwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "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": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "burnedShares", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "withdrawnUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "Unwrap", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "eventKey", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "eventData", - "type": "bytes" - } - ], - "name": "VaultAuxiliary", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "VaultBuffersPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "VaultPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [], - "name": "VaultQueriesDisabled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [], - "name": "VaultQueriesEnabled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "depositedUnderlying", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "mintedShares", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "bufferBalances", - "type": "bytes32" - } - ], - "name": "Wrap", - "type": "event" - }, - { - "stateMutability": "payable", - "type": "fallback" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "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": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "components": [ - { - "internalType": "enum SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amountGivenScaled18", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "balancesScaled18", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "indexIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "indexOut", - "type": "uint256" - }, - { - "internalType": "address", - "name": "router", - "type": "address" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct PoolSwapParams", - "name": "swapParams", - "type": "tuple" - } - ], - "name": "computeDynamicSwapFeePercentage", - "outputs": [ - { - "internalType": "uint256", - "name": "dynamicSwapFeePercentage", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "eventKey", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "eventData", - "type": "bytes" - } - ], - "name": "emitAuxiliaryEvent", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getAddLiquidityCalledFlag", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getAggregateSwapFeeAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getAggregateYieldFeeAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAuthorizer", - "outputs": [ - { - "internalType": "contract IAuthorizer", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getBptRate", - "outputs": [ - { - "internalType": "uint256", - "name": "rate", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getCurrentLiveBalances", - "outputs": [ - { - "internalType": "uint256[]", - "name": "balancesLiveScaled18", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "getERC4626BufferAsset", - "outputs": [ - { - "internalType": "address", - "name": "asset", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getHooksConfig", - "outputs": [ - { - "components": [ - { - "internalType": "bool", - "name": "enableHookAdjustedAmounts", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallComputeDynamicSwapFee", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "address", - "name": "hooksContract", - "type": "address" - } - ], - "internalType": "struct HooksConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNonzeroDeltaCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolConfig", - "outputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "bool", - "name": "disableUnbalancedLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableRemoveLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableDonation", - "type": "bool" - } - ], - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "staticSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aggregateSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "aggregateYieldFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint40", - "name": "tokenDecimalDiffs", - "type": "uint40" - }, - { - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "isPoolRegistered", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolInitialized", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolPaused", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolInRecoveryMode", - "type": "bool" - } - ], - "internalType": "struct PoolConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolData", - "outputs": [ - { - "components": [ - { - "internalType": "PoolConfigBits", - "name": "poolConfigBits", - "type": "bytes32" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "components": [ - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "internalType": "struct TokenInfo[]", - "name": "tokenInfo", - "type": "tuple[]" - }, - { - "internalType": "uint256[]", - "name": "balancesRaw", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "balancesLiveScaled18", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "tokenRates", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "decimalScalingFactors", - "type": "uint256[]" - } - ], - "internalType": "struct PoolData", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolPausedState", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "", - "type": "uint32" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolRoleAccounts", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "internalType": "address", - "name": "swapFeeManager", - "type": "address" - }, - { - "internalType": "address", - "name": "poolCreator", - "type": "address" - } - ], - "internalType": "struct PoolRoleAccounts", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokenInfo", - "outputs": [ - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "components": [ - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "internalType": "struct TokenInfo[]", - "name": "tokenInfo", - "type": "tuple[]" - }, - { - "internalType": "uint256[]", - "name": "balancesRaw", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "lastBalancesLiveScaled18", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokenRates", - "outputs": [ - { - "internalType": "uint256[]", - "name": "decimalScalingFactors", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "tokenRates", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokens", - "outputs": [ - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getProtocolFeeController", - "outputs": [ - { - "internalType": "contract IProtocolFeeController", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getReservesOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getStaticSwapFeePercentage", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getTokenDelta", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getVaultAdmin", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "exactAmountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "minBptAmountOut", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "initialize", - "outputs": [ - { - "internalType": "uint256", - "name": "bptAmountOut", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC4626", - "name": "wrappedToken", - "type": "address" - } - ], - "name": "isERC4626BufferInitialized", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolInRecoveryMode", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolInitialized", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolPaused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolRegistered", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isQueryDisabled", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isQueryDisabledPermanently", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isUnlocked", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "quote", - "outputs": [ - { - "internalType": "bytes", - "name": "result", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "quoteAndRevert", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "reentrancyGuardEntered", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "paysYieldFees", - "type": "bool" - } - ], - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "protocolFeeExempt", - "type": "bool" - }, - { - "components": [ - { - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "internalType": "address", - "name": "swapFeeManager", - "type": "address" - }, - { - "internalType": "address", - "name": "poolCreator", - "type": "address" - } - ], - "internalType": "struct PoolRoleAccounts", - "name": "roleAccounts", - "type": "tuple" - }, - { - "internalType": "address", - "name": "poolHooksContract", - "type": "address" - }, - { - "components": [ - { - "internalType": "bool", - "name": "disableUnbalancedLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableRemoveLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "enableDonation", - "type": "bool" - } - ], - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "name": "registerPool", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "exactBptAmountIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "minAmountsOut", - "type": "uint256[]" - } - ], - "name": "removeLiquidityRecovery", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amountsOutRaw", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "vault", - "outputs": [ - { - "internalType": "contract IVault", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/shared/constant.go b/pkg/liquidity-source/balancer-v3/shared/constant.go index cc24ee147..bda7e6a07 100644 --- a/pkg/liquidity-source/balancer-v3/shared/constant.go +++ b/pkg/liquidity-source/balancer-v3/shared/constant.go @@ -3,20 +3,24 @@ package shared import "github.com/holiman/uint256" const ( - PoolMethodGetTokenInfo = "getTokenInfo" - PoolMethodGetAggregateFeePercentages = "getAggregateFeePercentages" - PoolMethodGetAmplificationParameter = "getAmplificationParameter" - PoolMethodGetVault = "getVault" PoolMethodVersion = "version" - PoolVersion1 = 1 - PoolVersion2 = 2 + VaultMethodGetStaticSwapFeePercentage = "getStaticSwapFeePercentage" + VaultMethodGetAggregateFeePercentages = "getAggregateFeePercentages" + VaultMethodGetPoolTokenRates = "getPoolTokenRates" + VaultMethodGetPoolData = "getPoolData" + VaultMethodGetHooksConfig = "getHooksConfig" + + VaultMethodIsPoolPaused = "isPoolPaused" + VaultMethodIsVaultPaused = "isVaultPaused" + VaultMethodIsPoolInRecoveryMode = "isPoolInRecoveryMode" ) type ( Rounding int TokenType uint8 + SwapKind int ) const ( @@ -25,6 +29,9 @@ const ( STANDARD TokenType = iota WITH_RATE + + EXACT_IN SwapKind = iota + EXACT_OUT ) var ( diff --git a/pkg/liquidity-source/balancer-v3/shared/embed.go b/pkg/liquidity-source/balancer-v3/shared/embed.go index 3642bd358..ed35e7377 100644 --- a/pkg/liquidity-source/balancer-v3/shared/embed.go +++ b/pkg/liquidity-source/balancer-v3/shared/embed.go @@ -2,8 +2,5 @@ package shared import _ "embed" -//go:embed abis/Vault.json -var vaultJson []byte - -//go:embed abis/VaultExtension.json -var vaultExtensionJson []byte +//go:embed abis/VaultExplorer.json +var vaultExplorerJson []byte diff --git a/pkg/liquidity-source/balancer-v3/shared/error.go b/pkg/liquidity-source/balancer-v3/shared/error.go index e421487ad..a423933e2 100644 --- a/pkg/liquidity-source/balancer-v3/shared/error.go +++ b/pkg/liquidity-source/balancer-v3/shared/error.go @@ -5,4 +5,6 @@ import "errors" var ( ErrTradeAmountTooSmall = errors.New("trade amount is too small") ErrProtocolFeesExceedTotalCollected = errors.New("protocolFees exceed totalCollected") + ErrVaultIsPaused = errors.New("vault is paused") + ErrPoolIsPaused = errors.New("pool is paused") ) diff --git a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go index 8da36e004..7cf4460c6 100644 --- a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go @@ -92,7 +92,7 @@ func (u *PoolsListUpdater) querySubgraph(ctx context.Context, lastBlockTimestamp } if len(response.Pools) != 0 { - lastBlockTimestamp, _ = new(big.Int).SetString(response.Pools[len(response.Pools)-1].CreateTime, 10) + lastBlockTimestamp, _ = new(big.Int).SetString(response.Pools[len(response.Pools)-1].BlockTimestamp, 10) } return response.Pools, lastBlockTimestamp, nil diff --git a/pkg/liquidity-source/balancer-v3/shared/subgraph.go b/pkg/liquidity-source/balancer-v3/shared/subgraph.go index f22c5d2f9..4a5092d20 100644 --- a/pkg/liquidity-source/balancer-v3/shared/subgraph.go +++ b/pkg/liquidity-source/balancer-v3/shared/subgraph.go @@ -6,14 +6,17 @@ import ( ) type SubgraphPool struct { - ID string `json:"id"` - Address string `json:"address"` - CreateTime string `json:"blockTimestamp"` - Tokens []struct { - Address string `json:"address"` - Decimals int `json:"decimals"` - ScalingFactor string `json:"scalingFactor"` + ID string `json:"id"` + Address string `json:"address"` + BlockTimestamp string `json:"blockTimestamp"` + Factory string `json:"factory"` + Tokens []struct { + Address string `json:"address"` + Decimals int `json:"decimals"` } `json:"tokens"` + Vault struct { + ID string `json:"id"` + } `json:"vault"` } func BuildSubgraphPoolsQuery( @@ -34,10 +37,13 @@ func BuildSubgraphPoolsQuery( id address blockTimestamp + factory tokens { address decimals - scalingFactor + } + vault { + id } } }` diff --git a/pkg/liquidity-source/balancer-v3/shared/type.go b/pkg/liquidity-source/balancer-v3/shared/type.go index 2c9c22160..1f8fc9e89 100644 --- a/pkg/liquidity-source/balancer-v3/shared/type.go +++ b/pkg/liquidity-source/balancer-v3/shared/type.go @@ -1,22 +1,77 @@ package shared -import "github.com/holiman/uint256" +import ( + "math/big" -type PoolInfo struct { - Name string `json:"-"` - Version int `json:"version"` - Deployment string `json:"-"` + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" +) + +type AggregateFeePercentage struct { + AggregateSwapFeePercentage *big.Int + AggregateYieldFeePercentage *big.Int } type VaultSwapParams struct { - IsExactIn bool - IndexIn int - IndexOut int - AmountGiven *uint256.Int - DecimalScalingFactor *uint256.Int - TokenRate *uint256.Int - AmplificationParameter *uint256.Int - SwapFeePercentage *uint256.Int - AggregateSwapFeePercentage *uint256.Int - BalancesLiveScaled18 []*uint256.Int + Kind SwapKind + IndexIn int + IndexOut int + AmountGivenRaw *uint256.Int + LimitRaw *uint256.Int + // HooksConfig HooksConfig + // DecimalScalingFactor *uint256.Int + // TokenRate *uint256.Int + // AmplificationParameter *uint256.Int + // SwapFeePercentage *uint256.Int + // AggregateSwapFeePercentage *uint256.Int + // BalancesLiveScaled18 []*uint256.Int +} + +type PoolSwapParams struct { + Kind SwapKind + SwapFeePercentage *uint256.Int + AmountGivenScaled18 *uint256.Int + BalancesLiveScaled18 []*uint256.Int + IndexIn int + IndexOut int +} + +type TokenInfo struct { + TokenType uint8 + RateProvider common.Address + PaysYieldFees bool +} + +type PoolDataRPC struct { + Data struct { + PoolConfigBits [32]byte + Tokens []common.Address + TokenInfo []TokenInfo + BalancesRaw []*big.Int + BalancesLiveScaled18 []*big.Int + TokenRates []*big.Int + DecimalScalingFactors []*big.Int + } +} + +type HooksConfig struct { + ShouldCallComputeDynamicSwapFee bool `json:"shouldCallComputeDynamicSwapFee"` + ShouldCallBeforeSwap bool `json:"shouldCallBeforeSwap"` + ShouldCallAfterSwap bool `json:"shouldCallAfterSwap"` +} + +type HooksConfigRPC struct { + Data struct { + EnableHookAdjustedAmounts bool + ShouldCallBeforeInitialize bool + ShouldCallAfterInitialize bool + ShouldCallComputeDynamicSwapFee bool + ShouldCallBeforeSwap bool + ShouldCallAfterSwap bool + ShouldCallBeforeAddLiquidity bool + ShouldCallAfterAddLiquidity bool + ShouldCallBeforeRemoveLiquidity bool + ShouldCallAfterRemoveLiquidity bool + HooksContract common.Address + } } diff --git a/pkg/liquidity-source/balancer-v3/shared/vault.go b/pkg/liquidity-source/balancer-v3/shared/vault.go deleted file mode 100644 index ea0e99e4b..000000000 --- a/pkg/liquidity-source/balancer-v3/shared/vault.go +++ /dev/null @@ -1,128 +0,0 @@ -package shared - -import ( - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" - "github.com/holiman/uint256" -) - -type vault struct{} - -var Vault *vault - -func init() { - Vault = &vault{} -} - -func (v *vault) Swap( - param VaultSwapParams, - onSwap func(_ bool, _, _ int, _ *uint256.Int) (*uint256.Int, error), -) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - amountGivenScaled18, err := v.ComputeAmountGivenScaled18(true, param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) - if err != nil { - return nil, nil, nil, err - } - - swapFeeScaled18, err := math.MulUp(amountGivenScaled18, param.SwapFeePercentage) - if err != nil { - return nil, nil, nil, err - } - - amountGivenScaled18, err = math.Sub(amountGivenScaled18, swapFeeScaled18) - if err != nil { - return nil, nil, nil, err - } - - if amountGivenScaled18.Lt(MINIMUM_TRADE_AMOUNT) { - return nil, nil, nil, ErrTradeAmountTooSmall - } - - amountCalculatedScaled18, err := onSwap(param.IsExactIn, param.IndexIn, param.IndexOut, amountGivenScaled18) - if err != nil { - return nil, nil, nil, err - } - - if amountCalculatedScaled18.Lt(MINIMUM_TRADE_AMOUNT) { - return nil, nil, nil, ErrTradeAmountTooSmall - } - - amountCalculated, err := v.ComputeAmountCalculatedRaw(true, amountGivenScaled18, param.SwapFeePercentage, param.DecimalScalingFactor, param.TokenRate) - if err != nil { - return nil, nil, nil, err - } - - totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(true, swapFeeScaled18, param.AggregateSwapFeePercentage, - param.DecimalScalingFactor, param.TokenRate) - if err != nil { - return nil, nil, nil, err - } - - return amountCalculated, totalSwapFee, aggregateFee, nil -} - -func (v *vault) ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - if isExactIn { - return toScaled18ApplyRateRoundDown(amountGiven, decimalScalingFactor, tokenRate) - } - - return toScaled18ApplyRateRoundUp(amountGiven, decimalScalingFactor, computeRateRoundUp(tokenRate)) -} - -func (v *vault) ComputeAmountCalculatedRaw( - isExactIn bool, - amountCalculatedScaled18, swapFeePercentage, - decimalScalingFactor, tokenRate *uint256.Int, -) (*uint256.Int, error) { - if isExactIn { - return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, computeRateRoundUp(tokenRate)) - } - - totalSwapFeeAmountScaled18, err := math.MulDivUp(amountCalculatedScaled18, swapFeePercentage, math.Complement(swapFeePercentage)) - if err != nil { - return nil, err - } - - amountCalculatedScaled18, err = math.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) - if err != nil { - return nil, err - } - - return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, tokenRate) -} - -func (v *vault) ComputeAggregateSwapFees( - isExactIn bool, - totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, - decimalScalingFactor, tokenRate *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - if totalSwapFeeAmountScaled18.IsZero() { - return math.ZERO, math.ZERO, nil - } - - totalSwapFeeAmountRaw, err := toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) - if err != nil { - return nil, nil, err - } - - // should check if pool is in Recovery Mode - aggregateFeeAmountRaw, err := math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) - if err != nil { - return nil, nil, err - } - - if aggregateFeeAmountRaw.Gt(totalSwapFeeAmountRaw) { - return nil, nil, ErrProtocolFeesExceedTotalCollected - } - - return totalSwapFeeAmountRaw, aggregateFeeAmountRaw, nil -} - -func (v *vault) UpdateLiveBalance( - param VaultSwapParams, - rounding Rounding, -) (*uint256.Int, error) { - if rounding == ROUND_UP { - return toScaled18ApplyRateRoundUp(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) - } - - return toScaled18ApplyRateRoundDown(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) -} diff --git a/pkg/liquidity-source/balancer-v3/stable/config.go b/pkg/liquidity-source/balancer-v3/stable/config.go index 79a3b7b69..1ce80543c 100644 --- a/pkg/liquidity-source/balancer-v3/stable/config.go +++ b/pkg/liquidity-source/balancer-v3/stable/config.go @@ -3,8 +3,10 @@ package stable import "net/http" type Config struct { - DexID string `json:"dexID"` - SubgraphAPI string `json:"subgraphAPI"` - SubgraphHeaders http.Header `json:"subgraphHeaders"` - NewPoolLimit int `json:"newPoolLimit"` + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` + VaultExplorer string `json:"vaultExplorer"` + Factories map[string]string `json:"factories"` } diff --git a/pkg/liquidity-source/balancer-v3/stable/constant.go b/pkg/liquidity-source/balancer-v3/stable/constant.go index 016d8f88b..90ad454ba 100644 --- a/pkg/liquidity-source/balancer-v3/stable/constant.go +++ b/pkg/liquidity-source/balancer-v3/stable/constant.go @@ -5,8 +5,7 @@ const ( PoolType = "StablePool" - poolMethodGetStablePoolDynamicData = "getStablePoolDynamicData" - poolMethodGetStablePoolImmutableData = "getStablePoolImmutableData" + poolMethodGetAmplificationParameter = "getAmplificationParameter" ) var ( diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index a3649371c..b30339eeb 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -11,6 +11,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" @@ -19,13 +20,8 @@ import ( var ( ErrInvalidSwapFeePercentage = errors.New("invalid swap fee percentage") - ErrPoolIsPaused = errors.New("pool is paused") ErrInvalidAmp = errors.New("invalid amp") ErrNotTwoTokens = errors.New("not two tokens") - - ErrTradeAmountTooSmall = errors.New("trade amount is too small") - - ErrVaultIsLocked = errors.New("vault is locked") ) type PoolSimulator struct { @@ -38,11 +34,15 @@ type PoolSimulator struct { balancesLiveScaled18 []*uint256.Int decimalScalingFactors []*uint256.Int tokenRates []*uint256.Int + vault *vault.Vault - isInRecoveryMode bool - isPaused bool + hooksConfig shared.HooksConfig - vault string + isVaultPaused bool + isPoolPaused bool + isPoolInRecoveryMode bool + + vaultAddress string poolType string poolVersion int @@ -82,23 +82,28 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { return &PoolSimulator{ Pool: poolpkg.Pool{Info: poolInfo}, - isPaused: extra.IsPaused, - isInRecoveryMode: extra.IsInRecoveryMode, + isVaultPaused: extra.IsVaultPaused, + isPoolPaused: extra.IsPoolPaused, + isPoolInRecoveryMode: extra.IsPoolInRecoveryMode, swapFeePercentage: extra.StaticSwapFeePercentage, aggregateSwapFeePercentage: extra.AggregateSwapFeePercentage, amplificationParameter: extra.AmplificationParameter, balancesLiveScaled18: extra.BalancesLiveScaled18, tokenRates: extra.TokenRates, decimalScalingFactors: extra.DecimalScalingFactors, - vault: staticExtra.Vault, + hooksConfig: extra.HooksConfig, + vaultAddress: staticExtra.Vault, poolType: staticExtra.PoolType, - poolVersion: staticExtra.PoolVersion, }, nil } func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { - if p.isPaused { - return nil, ErrPoolIsPaused + if p.isVaultPaused { + return nil, shared.ErrVaultIsPaused + } + + if p.isPoolPaused { + return nil, shared.ErrPoolIsPaused } tokenAmountOut, tokenIn := params.TokenAmountOut, params.TokenIn @@ -113,17 +118,11 @@ func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk return nil, ErrInvalidAmountOut } - amountIn, totalSwapFee, aggregateSwapFee, err := shared.Vault.Swap(shared.VaultSwapParams{ - IsExactIn: false, - IndexIn: indexIn, - IndexOut: indexOut, - AmountGiven: amountOut, - DecimalScalingFactor: p.decimalScalingFactors[indexOut], - TokenRate: p.tokenRates[indexOut], - AmplificationParameter: p.amplificationParameter, - SwapFeePercentage: p.swapFeePercentage, - AggregateSwapFeePercentage: p.aggregateSwapFeePercentage, - BalancesLiveScaled18: p.balancesLiveScaled18, + amountIn, totalSwapFee, aggregateSwapFee, err := p.vault.Swap(shared.VaultSwapParams{ + Kind: shared.EXACT_OUT, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGivenRaw: amountOut, }, p.OnSwap) if err != nil { return nil, err @@ -175,7 +174,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { TokenRate: p.tokenRates[tokenIndexIn], } - updatedLiveBalanceIn, err := shared.Vault.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + updatedLiveBalanceIn, err := p.vault.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return @@ -212,29 +211,29 @@ func (p *PoolSimulator) computeBalance(tokenInIndex int, invariantRatio *uint256 return math.StableMath.ComputeBalance(p.amplificationParameter, p.balancesLiveScaled18, newInvariant, tokenInIndex) } -func (p *PoolSimulator) OnSwap(isExactIn bool, indexIn, indexOut int, amountInScaled18 *uint256.Int) (*uint256.Int, error) { +func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error) { invariant, err := p.computeInvariant(shared.ROUND_DOWN) if err != nil { return nil, err } var amountOutScaled18 *uint256.Int - if isExactIn { + if param.Kind == shared.EXACT_IN { amountOutScaled18, err = math.StableMath.ComputeOutGivenExactIn( p.amplificationParameter, p.balancesLiveScaled18, - indexIn, - indexOut, - amountInScaled18, + param.IndexIn, + param.IndexOut, + param.AmountGivenScaled18, invariant, ) } else { amountOutScaled18, err = math.StableMath.ComputeInGivenExactOut( p.amplificationParameter, p.balancesLiveScaled18, - indexIn, - indexOut, - amountInScaled18, + param.IndexIn, + param.IndexOut, + param.AmountGivenScaled18, invariant, ) } @@ -259,8 +258,12 @@ func (p *PoolSimulator) computeInvariant(rounding shared.Rounding) (*uint256.Int } func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { - if p.isPaused { - return nil, ErrPoolIsPaused + if p.isVaultPaused { + return nil, shared.ErrVaultIsPaused + } + + if p.isPoolPaused { + return nil, shared.ErrPoolIsPaused } tokenAmountIn, tokenOut := params.TokenAmountIn, params.TokenOut @@ -275,17 +278,11 @@ func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool return nil, ErrInvalidAmountIn } - amountOut, totalSwapFee, aggregateFee, err := shared.Vault.Swap(shared.VaultSwapParams{ - IsExactIn: true, - IndexIn: indexIn, - IndexOut: indexOut, - AmountGiven: amountIn, - DecimalScalingFactor: p.decimalScalingFactors[indexIn], - TokenRate: p.tokenRates[indexIn], - AmplificationParameter: p.amplificationParameter, - SwapFeePercentage: p.swapFeePercentage, - AggregateSwapFeePercentage: p.aggregateSwapFeePercentage, - BalancesLiveScaled18: p.balancesLiveScaled18, + amountOut, totalSwapFee, aggregateFee, err := p.vault.Swap(shared.VaultSwapParams{ + Kind: shared.EXACT_IN, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGivenRaw: amountIn, }, p.OnSwap) if err != nil { return nil, err diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index e9b27d591..3b2fa21b6 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -72,12 +72,6 @@ func (t *PoolTracker) getNewPoolState( }).Info("Finish updating state.") }() - var extra Extra - err := json.Unmarshal([]byte(p.Extra), &extra) - if err != nil { - return p, err - } - res, err := t.queryRPC(ctx, p.Address, overrides) if err != nil { return p, err @@ -96,25 +90,29 @@ func (t *PoolTracker) getNewPoolState( amplificationParameter, _ = uint256.FromBig(res.AmplificationParameter) staticSwapFeePercentage, _ = uint256.FromBig(res.StaticSwapFeePercentage) aggregateSwapFeePercentage, _ = uint256.FromBig(res.AggregateSwapFeePercentage) - balancesLiveScaled18 = lo.Map(res.BalancesLiveScaled18, func(v *big.Int, _ int) *uint256.Int { - r, _ := uint256.FromBig(v) - return r + + balancesLiveScaled18 = lo.Map(res.BalancesLiveScaled18, func(v *big.Int, _ int) *uint256.Int { + return uint256.MustFromBig(v) }) tokenRates = lo.Map(res.TokenRates, func(v *big.Int, _ int) *uint256.Int { - r, _ := uint256.FromBig(v) - return r + return uint256.MustFromBig(v) + }) + decimalScalingFactors = lo.Map(res.DecimalScalingFactors, func(v *big.Int, _ int) *uint256.Int { + return uint256.MustFromBig(v) }) ) - extra.AmplificationParameter = amplificationParameter - extra.StaticSwapFeePercentage = staticSwapFeePercentage - extra.AggregateSwapFeePercentage = aggregateSwapFeePercentage - extra.BalancesLiveScaled18 = balancesLiveScaled18 - extra.TokenRates = tokenRates - extra.IsPaused = res.IsPoolPaused - extra.IsInRecoveryMode = res.IsPoolInRecoveryMode - - extraBytes, err := json.Marshal(&extra) + extraBytes, err := json.Marshal(&Extra{ + AmplificationParameter: amplificationParameter, + StaticSwapFeePercentage: staticSwapFeePercentage, + AggregateSwapFeePercentage: aggregateSwapFeePercentage, + BalancesLiveScaled18: balancesLiveScaled18, + DecimalScalingFactors: decimalScalingFactors, + TokenRates: tokenRates, + IsVaultPaused: res.IsVaultPaused, + IsPoolPaused: res.IsPoolPaused, + IsPoolInRecoveryMode: res.IsPoolInRecoveryMode, + }) if err != nil { logger.WithFields(logger.Fields{ "dexId": t.config.DexID, @@ -141,9 +139,16 @@ func (t *PoolTracker) queryRPC( overrides map[common.Address]gethclient.OverrideAccount, ) (*RpcResult, error) { var ( - aggregateFeePercentages AggregateFeePercentage - stablePoolDynamicData StablePoolDynamicData - poolTokenInfo PoolTokenInfo + aggregateFeePercentages shared.AggregateFeePercentage + hooksConfig shared.HooksConfigRPC + poolData shared.PoolDataRPC + + amplificationParameter AmplificationParameter + staticSwapFeePercentage *big.Int + + isVaultPaused bool + isPoolPaused bool + isPoolInRecoveryMode bool ) req := t.ethrpcClient.R().SetContext(ctx).SetRequireSuccess(true) @@ -152,20 +157,58 @@ func (t *PoolTracker) queryRPC( } req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: shared.PoolMethodGetAggregateFeePercentages, + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetAggregateFeePercentages, + Params: []interface{}{common.HexToAddress(poolAddress)}, }, []interface{}{&aggregateFeePercentages}) + req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetStablePoolDynamicData, - }, []interface{}{&stablePoolDynamicData}) + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetStaticSwapFeePercentage, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&staticSwapFeePercentage}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetPoolData, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&poolData}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetHooksConfig, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&hooksConfig}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodIsVaultPaused, + }, []interface{}{&isVaultPaused}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodIsPoolPaused, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&isPoolPaused}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodIsPoolInRecoveryMode, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&isPoolInRecoveryMode}) + req.AddCall(ðrpc.Call{ ABI: poolABI, Target: poolAddress, - Method: shared.PoolMethodGetTokenInfo, - }, []interface{}{&poolTokenInfo}) + Method: poolMethodGetAmplificationParameter, + }, []interface{}{&lificationParameter}) res, err := req.TryBlockAndAggregate() if err != nil { @@ -178,14 +221,20 @@ func (t *PoolTracker) queryRPC( } return &RpcResult{ - BalancesRaw: poolTokenInfo.BalancesRaw, - BalancesLiveScaled18: stablePoolDynamicData.Data.BalancesLiveScaled18, - TokenRates: stablePoolDynamicData.Data.TokenRates, - StaticSwapFeePercentage: stablePoolDynamicData.Data.StaticSwapFeePercentage, + HooksConfig: shared.HooksConfig{ + ShouldCallComputeDynamicSwapFee: hooksConfig.Data.ShouldCallComputeDynamicSwapFee, + ShouldCallBeforeSwap: hooksConfig.Data.ShouldCallBeforeSwap, + ShouldCallAfterSwap: hooksConfig.Data.ShouldCallAfterSwap, + }, + BalancesRaw: poolData.Data.BalancesRaw, + BalancesLiveScaled18: poolData.Data.BalancesLiveScaled18, + TokenRates: poolData.Data.TokenRates, + StaticSwapFeePercentage: staticSwapFeePercentage, AggregateSwapFeePercentage: aggregateFeePercentages.AggregateSwapFeePercentage, - AmplificationParameter: stablePoolDynamicData.Data.AmplificationParameter, - IsPoolPaused: stablePoolDynamicData.Data.IsPoolPaused, - IsPoolInRecoveryMode: stablePoolDynamicData.Data.IsPoolInRecoveryMode, + AmplificationParameter: amplificationParameter.Value, + IsVaultPaused: isVaultPaused, + IsPoolPaused: isPoolPaused, + IsPoolInRecoveryMode: isPoolInRecoveryMode, BlockNumber: res.BlockNumber.Uint64(), }, nil } diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index 7e28d76f1..dc7802229 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -2,14 +2,13 @@ package stable import ( "context" + "errors" "strings" "time" "github.com/KyberNetwork/ethrpc" "github.com/KyberNetwork/logger" - "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" @@ -48,17 +47,21 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte }).Infof("Finish updating pools list.") }() - subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) - if err != nil { - return nil, nil, err + if u.config.Factories == nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Error("factories config is empty") + + return nil, nil, errors.New("PoolTypeByFactory config is empty") } - vaults, poolVersions, err := u.getPoolInfos(ctx, subgraphPools) + subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) if err != nil { return nil, nil, err } - pools, err := u.initPools(subgraphPools, vaults, poolVersions) + pools, err := u.initPools(subgraphPools) if err != nil { logger.WithFields(logger.Fields{ "dexId": u.config.DexID, @@ -71,115 +74,51 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte return pools, newMetadataBytes, nil } -func (u *PoolsListUpdater) getPoolInfos(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]string, []int, error) { - var ( - vaultAddresses = make([]common.Address, len(subgraphPools)) - vaults = make([]string, len(subgraphPools)) - poolInfos = make([]string, len(subgraphPools)) - poolVersions = make([]int, len(subgraphPools)) - ) - - req := u.ethrpcClient.R().SetContext(ctx) - for idx, subgraphPool := range subgraphPools { - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: subgraphPool.Address, - Method: shared.PoolMethodGetVault, - }, []interface{}{&vaultAddresses[idx]}) - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: subgraphPool.Address, - Method: shared.PoolMethodVersion, - }, []interface{}{&poolInfos[idx]}) - } - if _, err := req.Aggregate(); err != nil { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Errorf("failed to getPoolInfos: %v", err) - return nil, nil, err - } - - for idx, addr := range vaultAddresses { - var poolInfo shared.PoolInfo - err := json.Unmarshal([]byte(poolInfos[idx]), &poolInfo) - if err != nil { +func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]entity.Pool, error) { + pools := make([]entity.Pool, 0, len(subgraphPools)) + for _, subgraphPool := range subgraphPools { + poolType, found := u.config.Factories[subgraphPool.Factory] + if !found && subgraphPool.Factory != "" { logger.WithFields(logger.Fields{ "dexId": u.config.DexID, "dexType": DexType, - }).Warnf("invalid pool version data, fallback to %v", err) - - poolInfo.Version = shared.PoolVersion1 // temporary + }).Warnf("detected a new factory that hasn't been configured : %s", subgraphPool.Factory) + continue } - poolVersions[idx] = poolInfo.Version - vaults[idx] = strings.ToLower(addr.Hex()) - } - - return vaults, poolVersions, nil -} - -func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool, vaults []string, poolVersions []int) ([]entity.Pool, error) { - pools := make([]entity.Pool, 0, len(subgraphPools)) - for idx := range subgraphPools { - pool, err := u.initPool(subgraphPools[idx], vaults[idx], poolVersions[idx]) - if err != nil { - return nil, err + if strings.EqualFold(PoolType, poolType) { + staticExtraBytes, err := json.Marshal(&StaticExtra{ + PoolType: PoolType, + Vault: subgraphPool.Vault.ID, + }) + if err != nil { + return nil, err + } + + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + ) + for i, token := range subgraphPool.Tokens { + poolTokens[i] = &entity.PoolToken{ + Address: token.Address, + Weight: 1, + Swappable: true, + } + reserves[i] = "0" + } + + pools = append(pools, entity.Pool{ + Address: subgraphPool.Address, + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + }) } - - pools = append(pools, pool) } return pools, nil } - -func (u *PoolsListUpdater) initPool(subgraphPool *shared.SubgraphPool, vault string, poolVersion int) (entity.Pool, error) { - var ( - poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) - reserves = make([]string, len(subgraphPool.Tokens)) - scalingFactors = make([]*uint256.Int, len(subgraphPool.Tokens)) - err error - ) - - for j, token := range subgraphPool.Tokens { - scalingFactors[j], err = uint256.FromDecimal(token.ScalingFactor) - if err != nil { - return entity.Pool{}, err - } - - poolTokens[j] = &entity.PoolToken{ - Address: token.Address, - Weight: 1, - Swappable: true, - } - - reserves[j] = "0" - } - - staticExtraBytes, err := json.Marshal(&StaticExtra{ - PoolType: PoolType, - PoolVersion: poolVersion, - Vault: vault, - }) - if err != nil { - return entity.Pool{}, err - } - - extraBytes, err := json.Marshal(&Extra{ - DecimalScalingFactors: scalingFactors, - }) - if err != nil { - return entity.Pool{}, err - } - - return entity.Pool{ - Address: subgraphPool.Address, - Exchange: u.config.DexID, - Type: DexType, - Timestamp: time.Now().Unix(), - Tokens: poolTokens, - Reserves: reserves, - StaticExtra: string(staticExtraBytes), - Extra: string(extraBytes), - }, nil -} diff --git a/pkg/liquidity-source/balancer-v3/stable/type.go b/pkg/liquidity-source/balancer-v3/stable/type.go index 88efd2cd5..bbd187087 100644 --- a/pkg/liquidity-source/balancer-v3/stable/type.go +++ b/pkg/liquidity-source/balancer-v3/stable/type.go @@ -3,6 +3,7 @@ package stable import ( "math/big" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" ) @@ -12,20 +13,21 @@ type Gas struct { } type Extra struct { - AmplificationParameter *uint256.Int `json:"amplificationParameter"` - StaticSwapFeePercentage *uint256.Int `json:"staticSwapFeePercentage"` - AggregateSwapFeePercentage *uint256.Int `json:"aggregateSwapFeePercentage"` - BalancesLiveScaled18 []*uint256.Int `json:"balancesLiveScaled18"` - DecimalScalingFactors []*uint256.Int `json:"decimalScalingFactors"` - TokenRates []*uint256.Int `json:"tokenRates"` - IsPaused bool `json:"isPaused"` - IsInRecoveryMode bool `json:"isInRecoveryMode"` + HooksConfig shared.HooksConfig `json:"hooksConfig"` + AmplificationParameter *uint256.Int `json:"amplificationParameter"` + StaticSwapFeePercentage *uint256.Int `json:"staticSwapFeePercentage"` + AggregateSwapFeePercentage *uint256.Int `json:"aggregateSwapFeePercentage"` + BalancesLiveScaled18 []*uint256.Int `json:"balancesLiveScaled18"` + DecimalScalingFactors []*uint256.Int `json:"decimalScalingFactors"` + TokenRates []*uint256.Int `json:"tokenRates"` + IsVaultPaused bool `json:"isVaultPaused"` + IsPoolPaused bool `json:"isPoolPaused"` + IsPoolInRecoveryMode bool `json:"isPoolInRecoveryMode"` } type StaticExtra struct { - PoolType string `json:"poolType"` - PoolVersion int `json:"poolVersion"` - Vault string `json:"vault"` + PoolType string `json:"poolType"` + Vault string `json:"vault"` } type PoolTokens struct { @@ -46,11 +48,6 @@ type AmplificationParameter struct { Precision *big.Int } -type AggregateFeePercentage struct { - AggregateSwapFeePercentage *big.Int - AggregateYieldFeePercentage *big.Int -} - type StablePoolDynamicData struct { Data struct { BalancesLiveScaled18 []*big.Int @@ -92,12 +89,15 @@ type PoolMetaInfo struct { } type RpcResult struct { + HooksConfig shared.HooksConfig BalancesRaw []*big.Int BalancesLiveScaled18 []*big.Int TokenRates []*big.Int + DecimalScalingFactors []*big.Int StaticSwapFeePercentage *big.Int AggregateSwapFeePercentage *big.Int AmplificationParameter *big.Int + IsVaultPaused bool IsPoolPaused bool IsPoolInRecoveryMode bool BlockNumber uint64 diff --git a/pkg/liquidity-source/balancer-v3/vault/const.go b/pkg/liquidity-source/balancer-v3/vault/const.go new file mode 100644 index 000000000..f83073a47 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/vault/const.go @@ -0,0 +1,13 @@ +package vault + +import ( + "github.com/holiman/uint256" +) + +var ( + // to be more general, this value should be queried from the VaultAdmin contract + MINIMUM_TRADE_AMOUNT = uint256.NewInt(1000000) + + MAX_FEE_PERCENTAGE, _ = uint256.FromDecimal("999999000000000000") // 99.9999e16; // 99.9999% + +) diff --git a/pkg/liquidity-source/balancer-v3/vault/error.go b/pkg/liquidity-source/balancer-v3/vault/error.go new file mode 100644 index 000000000..282d52969 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/vault/error.go @@ -0,0 +1,12 @@ +package vault + +import "errors" + +var ( + ErrTradeAmountTooSmall = errors.New("trade amount is too small") + ErrProtocolFeesExceedTotalCollected = errors.New("protocolFees exceed totalCollected") + ErrVaultIsPaused = errors.New("vault is paused") + ErrPoolIsPaused = errors.New("pool is paused") + ErrDynamicSwapFeeHookFailed = errors.New("dynamicSwapFeeHook is failed") + ErrPercentageAboveMax = errors.New("percentage above max") +) diff --git a/pkg/liquidity-source/balancer-v3/shared/scaling_helper.go b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go similarity index 98% rename from pkg/liquidity-source/balancer-v3/shared/scaling_helper.go rename to pkg/liquidity-source/balancer-v3/vault/scaling_helper.go index 6f1a26786..44e610a8f 100644 --- a/pkg/liquidity-source/balancer-v3/shared/scaling_helper.go +++ b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go @@ -1,4 +1,4 @@ -package shared +package vault import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go new file mode 100644 index 000000000..fe78baebf --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -0,0 +1,180 @@ +package vault + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/holiman/uint256" +) + +type Vault struct { + hook hooks.IHook + hooksConfig shared.HooksConfig + decimalScalingFactors []*uint256.Int + tokenRates []*uint256.Int + balancesLiveScaled18 []*uint256.Int + amplificationParameter *uint256.Int + swapFeePercentage *uint256.Int + aggregateSwapFeePercentage *uint256.Int +} + +func NewVault(hook hooks.IHook, hooksConfig shared.HooksConfig, + decimalScalingFactors, tokenRates, balancesLiveScaled18 []*uint256.Int, + amplificationParameter, swapFeePercentage, aggregateSwapFeePercentage *uint256.Int, +) *Vault { + return &Vault{ + hook: hook, + hooksConfig: hooksConfig, + decimalScalingFactors: decimalScalingFactors, + tokenRates: tokenRates, + balancesLiveScaled18: balancesLiveScaled18, + amplificationParameter: amplificationParameter, + swapFeePercentage: swapFeePercentage, + aggregateSwapFeePercentage: aggregateSwapFeePercentage, + } +} + +func (v *Vault) Swap( + vaultSwapParams shared.VaultSwapParams, + onSwap func(param shared.PoolSwapParams) (*uint256.Int, error), +) (*uint256.Int, *uint256.Int, *uint256.Int, error) { + amountGivenScaled18, err := v.ComputeAmountGivenScaled18(true, vaultSwapParams.AmountGivenRaw, + v.decimalScalingFactors[vaultSwapParams.IndexOut], v.tokenRates[vaultSwapParams.IndexOut]) + if err != nil { + return nil, nil, nil, err + } + + var poolSwapParams = shared.PoolSwapParams{ + Kind: vaultSwapParams.Kind, + AmountGivenScaled18: amountGivenScaled18, + BalancesLiveScaled18: v.balancesLiveScaled18, + IndexIn: vaultSwapParams.IndexIn, + IndexOut: vaultSwapParams.IndexOut, + } + + if v.hooksConfig.ShouldCallBeforeSwap { + v.hook.OnBeforeSwap() + + } + if v.hooksConfig.ShouldCallComputeDynamicSwapFee { + swapFeePercentage, err := v.callComputeDynamicSwapFeeHook(poolSwapParams) + if err != nil { + return nil, nil, nil, err + } + + poolSwapParams.SwapFeePercentage = swapFeePercentage + } + + if vaultSwapParams.Kind == shared.EXACT_IN { + totalSwapFeeAmountScaled18, err := math.MulUp(poolSwapParams.AmountGivenScaled18, poolSwapParams.SwapFeePercentage) + if err != nil { + return nil, nil, nil, err + } + + poolSwapParams.AmountGivenScaled18, err = math.Sub(poolSwapParams.AmountGivenScaled18, totalSwapFeeAmountScaled18) + if err != nil { + return nil, nil, nil, err + } + } + + // _ensureValidSwapAmount + if amountGivenScaled18.Lt(MINIMUM_TRADE_AMOUNT) { + return nil, nil, nil, ErrTradeAmountTooSmall + } + + amountCalculatedScaled18, err := onSwap(poolSwapParams) + if err != nil { + return nil, nil, nil, err + } + + // _ensureValidSwapAmount + if amountCalculatedScaled18.Lt(MINIMUM_TRADE_AMOUNT) { + return nil, nil, nil, ErrTradeAmountTooSmall + } + + if vaultSwapParams.Kind == shared.EXACT_IN { + + } + + amountCalculated, err := v.ComputeAmountCalculatedRaw(true, amountGivenScaled18, v.swapFeePercentage, + v.decimalScalingFactors[param.IndexOut], v.tokenRates[param.IndexOut]) + if err != nil { + return nil, nil, nil, err + } + + totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(true, swapFeeScaled18, v.aggregateSwapFeePercentage, + param.DecimalScalingFactor, param.TokenRate) + if err != nil { + return nil, nil, nil, err + } + + return amountCalculated, totalSwapFee, aggregateFee, nil +} + +func (v *Vault) ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + if isExactIn { + return toScaled18ApplyRateRoundDown(amountGiven, decimalScalingFactor, tokenRate) + } + + return toScaled18ApplyRateRoundUp(amountGiven, decimalScalingFactor, computeRateRoundUp(tokenRate)) +} + +func (v *Vault) ComputeAmountCalculatedRaw( + isExactIn bool, + amountCalculatedScaled18, swapFeePercentage, + decimalScalingFactor, tokenRate *uint256.Int, +) (*uint256.Int, error) { + if isExactIn { + return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, computeRateRoundUp(tokenRate)) + } + + totalSwapFeeAmountScaled18, err := math.MulDivUp(amountCalculatedScaled18, swapFeePercentage, math.Complement(swapFeePercentage)) + if err != nil { + return nil, err + } + + amountCalculatedScaled18, err = math.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) + if err != nil { + return nil, err + } + + return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, tokenRate) +} + +func (v *Vault) ComputeAggregateSwapFees( + isExactIn bool, + totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, + decimalScalingFactor, tokenRate *uint256.Int, +) (*uint256.Int, *uint256.Int, error) { + if totalSwapFeeAmountScaled18.IsZero() { + return math.ZERO, math.ZERO, nil + } + + totalSwapFeeAmountRaw, err := toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) + if err != nil { + return nil, nil, err + } + + // should check if pool is in Recovery Mode + aggregateFeeAmountRaw, err := math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) + if err != nil { + return nil, nil, err + } + + if aggregateFeeAmountRaw.Gt(totalSwapFeeAmountRaw) { + return nil, nil, ErrProtocolFeesExceedTotalCollected + } + + return totalSwapFeeAmountRaw, aggregateFeeAmountRaw, nil +} + +func (v *Vault) UpdateLiveBalance( + param shared.VaultSwapParams, + rounding shared.Rounding, +) (*uint256.Int, error) { + if rounding == shared.ROUND_UP { + return toScaled18ApplyRateRoundUp(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) + } + + return toScaled18ApplyRateRoundDown(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) +} diff --git a/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go b/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go new file mode 100644 index 000000000..02e586f34 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go @@ -0,0 +1,41 @@ +package vault + +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/holiman/uint256" +) + +func (v *Vault) callComputeDynamicSwapFeeHook(poolSwapParams shared.PoolSwapParams) (*uint256.Int, error) { + success, swapFeePercentage, err := v.hook.OnComputeDynamicSwapFeePercentage(poolSwapParams) + if err != nil { + return nil, err + } + + if !success { + return nil, ErrDynamicSwapFeeHookFailed + } + + if swapFeePercentage.Gt(MAX_FEE_PERCENTAGE) { + return nil, ErrDynamicSwapFeeHookFailed + } + + return swapFeePercentage, nil +} + +// (bool success, uint256 swapFeePercentage) = hooksContract.onComputeDynamicSwapFeePercentage( +// swapParams, +// pool, +// staticSwapFeePercentage +// ); + +// if (success == false) { +// revert IVaultErrors.DynamicSwapFeeHookFailed(); +// } + +// // A 100% fee is not supported. In the ExactOut case, the Vault divides by the complement of the swap fee. +// // The minimum precision constraint provides an additional buffer. +// if (swapFeePercentage > MAX_FEE_PERCENTAGE) { +// revert IVaultErrors.PercentageAboveMax(); +// } + +// return swapFeePercentage; From 2c030ebd853d811458ed2ce8d7c638d836194d1a Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Tue, 31 Dec 2024 18:27:19 +0700 Subject: [PATCH 12/39] tmp --- .../balancer-v3/hooks/base_hook.go | 28 ++-- .../balancer-v3/hooks/ihook.go | 4 +- .../balancer-v3/hooks/stable_surge.go | 11 +- .../balancer-v3/shared/type.go | 21 +-- .../balancer-v3/stable/pool_simulator.go | 107 ++++++-------- .../balancer-v3/stable/pool_simulator_test.go | 60 +++++--- .../balancer-v3/stable/pool_tracker.go | 1 + .../balancer-v3/vault/error.go | 4 + .../balancer-v3/vault/vault.go | 130 ++++++++++-------- .../balancer-v3/vault/vault_hooks_lib.go | 49 +++++++ 10 files changed, 241 insertions(+), 174 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/hooks/base_hook.go b/pkg/liquidity-source/balancer-v3/hooks/base_hook.go index fe4a30046..f4f61aa6c 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/base_hook.go +++ b/pkg/liquidity-source/balancer-v3/hooks/base_hook.go @@ -1,18 +1,26 @@ package hooks -import "github.com/holiman/uint256" +import ( + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/holiman/uint256" +) type baseHook struct{} -var BaseHook = &baseHook{} +func NewBaseHook() *baseHook { + return &baseHook{} +} -func (h *baseHook) OnBeforeSwap() {} +func (h *baseHook) OnBeforeSwap(shared.PoolSwapParams) (bool, error) { + return false, nil +} -func (h *baseHook) OnAfterSwap() {} +func (h *baseHook) OnAfterSwap(shared.AfterSwapParams) (bool, *uint256.Int, error) { + return false, math.ZERO, nil -func (h *baseHook) OnComputeDynamicSwapFeePercentage( - staticSwapFeePercentage, - amountGivenScaled18, - balanceIn, - balanceOut *uint256.Int, -) (bool, *uint256.Int, error) +} + +func (h *baseHook) OnComputeDynamicSwapFeePercentage(shared.PoolSwapParams) (bool, *uint256.Int, error) { + return false, math.ZERO, nil +} diff --git a/pkg/liquidity-source/balancer-v3/hooks/ihook.go b/pkg/liquidity-source/balancer-v3/hooks/ihook.go index 4efc899fe..21d7819a4 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/ihook.go +++ b/pkg/liquidity-source/balancer-v3/hooks/ihook.go @@ -6,7 +6,7 @@ import ( ) type IHook interface { - OnBeforeSwap() - OnAfterSwap() + OnBeforeSwap(param shared.PoolSwapParams) (bool, error) + OnAfterSwap(param shared.AfterSwapParams) (bool, *uint256.Int, error) OnComputeDynamicSwapFeePercentage(param shared.PoolSwapParams) (bool, *uint256.Int, error) } diff --git a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go index 1b4080cde..68978011b 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go +++ b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go @@ -17,18 +17,9 @@ func (h *StableSurgeHook) OnComputeDynamicSwapFeePercentage( balanceIn, balanceOut *uint256.Int, ) (bool, *uint256.Int, error) { - calculatedSwapFeePercentage, err := h.calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, amountGivenScaled18) - if err != nil { - return false, nil, err - } - - if calculatedSwapFeePercentage.Gt(staticSwapFeePercentage) { - return true, calculatedSwapFeePercentage, nil - } - return false, staticSwapFeePercentage, nil } func (h *StableSurgeHook) getSurgeFeePercentage(params shared.VaultSwapParams) (*uint256.Int, error) { - // numTokens := + return nil, nil } diff --git a/pkg/liquidity-source/balancer-v3/shared/type.go b/pkg/liquidity-source/balancer-v3/shared/type.go index 1f8fc9e89..8d98c744d 100644 --- a/pkg/liquidity-source/balancer-v3/shared/type.go +++ b/pkg/liquidity-source/balancer-v3/shared/type.go @@ -17,14 +17,6 @@ type VaultSwapParams struct { IndexIn int IndexOut int AmountGivenRaw *uint256.Int - LimitRaw *uint256.Int - // HooksConfig HooksConfig - // DecimalScalingFactor *uint256.Int - // TokenRate *uint256.Int - // AmplificationParameter *uint256.Int - // SwapFeePercentage *uint256.Int - // AggregateSwapFeePercentage *uint256.Int - // BalancesLiveScaled18 []*uint256.Int } type PoolSwapParams struct { @@ -36,6 +28,18 @@ type PoolSwapParams struct { IndexOut int } +type AfterSwapParams struct { + Kind SwapKind + IndexIn int + IndexOut int + AmountInScaled18 *uint256.Int + AmountOutScaled18 *uint256.Int + TokenInBalanceScaled18 *uint256.Int + TokenOutBalanceScaled18 *uint256.Int + AmountCalculatedScaled18 *uint256.Int + AmountCalculatedRaw *uint256.Int +} + type TokenInfo struct { TokenType uint8 RateProvider common.Address @@ -55,6 +59,7 @@ type PoolDataRPC struct { } type HooksConfig struct { + EnableHookAdjustedAmounts bool `json:"enableHookAdjustedAmounts"` ShouldCallComputeDynamicSwapFee bool `json:"shouldCallComputeDynamicSwapFee"` ShouldCallBeforeSwap bool `json:"shouldCallBeforeSwap"` ShouldCallAfterSwap bool `json:"shouldCallAfterSwap"` diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index b30339eeb..1de5e735a 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -6,9 +6,9 @@ import ( "github.com/goccy/go-json" "github.com/holiman/uint256" - "github.com/samber/lo" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" @@ -27,14 +27,7 @@ var ( type PoolSimulator struct { poolpkg.Pool - swapFeePercentage *uint256.Int - aggregateSwapFeePercentage *uint256.Int - - amplificationParameter *uint256.Int - balancesLiveScaled18 []*uint256.Int - decimalScalingFactors []*uint256.Int - tokenRates []*uint256.Int - vault *vault.Vault + vault *vault.Vault hooksConfig shared.HooksConfig @@ -70,6 +63,12 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) } + // Need to detect the current using hook of pool + hook := hooks.NewBaseHook() + + vault := vault.New(hook, extra.HooksConfig, extra.IsPoolInRecoveryMode, extra.DecimalScalingFactors, extra.TokenRates, + extra.BalancesLiveScaled18, extra.AmplificationParameter, extra.StaticSwapFeePercentage, extra.AggregateSwapFeePercentage) + poolInfo := poolpkg.PoolInfo{ Address: entityPool.Address, Exchange: entityPool.Exchange, @@ -81,19 +80,14 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { } return &PoolSimulator{ - Pool: poolpkg.Pool{Info: poolInfo}, - isVaultPaused: extra.IsVaultPaused, - isPoolPaused: extra.IsPoolPaused, - isPoolInRecoveryMode: extra.IsPoolInRecoveryMode, - swapFeePercentage: extra.StaticSwapFeePercentage, - aggregateSwapFeePercentage: extra.AggregateSwapFeePercentage, - amplificationParameter: extra.AmplificationParameter, - balancesLiveScaled18: extra.BalancesLiveScaled18, - tokenRates: extra.TokenRates, - decimalScalingFactors: extra.DecimalScalingFactors, - hooksConfig: extra.HooksConfig, - vaultAddress: staticExtra.Vault, - poolType: staticExtra.PoolType, + Pool: poolpkg.Pool{Info: poolInfo}, + isVaultPaused: extra.IsVaultPaused, + isPoolPaused: extra.IsPoolPaused, + isPoolInRecoveryMode: extra.IsPoolInRecoveryMode, + vault: vault, + hooksConfig: extra.HooksConfig, + vaultAddress: staticExtra.Vault, + poolType: staticExtra.PoolType, }, nil } @@ -146,7 +140,7 @@ func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { return PoolMetaInfo{ - Vault: s.vault, + Vault: s.vaultAddress, PoolType: s.poolType, PoolVersion: s.poolVersion, TokenOutIndex: s.GetTokenIndex(tokenOut), @@ -168,47 +162,27 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { updatedRawBalanceIn.Sub(updatedRawBalanceIn, swapInfo.AggregateFee) p.Info.Reserves[tokenIndexIn] = updatedRawBalanceIn - vaultParams := shared.VaultSwapParams{ - AmountGiven: uint256.MustFromBig(updatedRawBalanceIn), - DecimalScalingFactor: p.decimalScalingFactors[tokenIndexIn], - TokenRate: p.tokenRates[tokenIndexIn], - } + amountGivenRaw := uint256.MustFromBig(updatedRawBalanceIn) - updatedLiveBalanceIn, err := p.vault.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + updatedLiveBalanceIn, err := p.vault.UpdateLiveBalance(tokenIndexIn, amountGivenRaw, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return } - p.balancesLiveScaled18[tokenIndexIn] = updatedLiveBalanceIn + p.vault.BalancesLiveScaled18[tokenIndexIn] = updatedLiveBalanceIn updatedRawBalanceOut := new(big.Int) updatedRawBalanceOut.Sub(p.Info.Reserves[tokenIndexOut], params.TokenAmountOut.Amount) p.Info.Reserves[tokenIndexOut] = updatedRawBalanceOut - vaultParams.AmountGiven.SetFromBig(updatedRawBalanceOut) - vaultParams.DecimalScalingFactor = p.decimalScalingFactors[tokenIndexOut] - vaultParams.TokenRate = p.tokenRates[tokenIndexOut] + amountGivenRaw.SetFromBig(updatedRawBalanceOut) - updatedLiveBalanceOut, err := shared.Vault.UpdateLiveBalance(vaultParams, shared.ROUND_DOWN) + updatedLiveBalanceOut, err := p.vault.UpdateLiveBalance(tokenIndexOut, amountGivenRaw, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return } - p.balancesLiveScaled18[tokenIndexOut] = updatedLiveBalanceOut -} - -func (p *PoolSimulator) computeBalance(tokenInIndex int, invariantRatio *uint256.Int) (*uint256.Int, error) { - invariant, err := p.computeInvariant(shared.ROUND_UP) - if err != nil { - return nil, err - } - - newInvariant, err := math.MulUp(invariant, invariantRatio) - if err != nil { - return nil, err - } - - return math.StableMath.ComputeBalance(p.amplificationParameter, p.balancesLiveScaled18, newInvariant, tokenInIndex) + p.vault.BalancesLiveScaled18[tokenIndexOut] = updatedLiveBalanceOut } func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error) { @@ -220,8 +194,8 @@ func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error var amountOutScaled18 *uint256.Int if param.Kind == shared.EXACT_IN { amountOutScaled18, err = math.StableMath.ComputeOutGivenExactIn( - p.amplificationParameter, - p.balancesLiveScaled18, + p.vault.AmplificationParameter, + p.vault.BalancesLiveScaled18, param.IndexIn, param.IndexOut, param.AmountGivenScaled18, @@ -229,8 +203,8 @@ func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error ) } else { amountOutScaled18, err = math.StableMath.ComputeInGivenExactOut( - p.amplificationParameter, - p.balancesLiveScaled18, + p.vault.AmplificationParameter, + p.vault.BalancesLiveScaled18, param.IndexIn, param.IndexOut, param.AmountGivenScaled18, @@ -245,7 +219,7 @@ func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error } func (p *PoolSimulator) computeInvariant(rounding shared.Rounding) (*uint256.Int, error) { - invariant, err := math.StableMath.ComputeInvariant(p.amplificationParameter, p.balancesLiveScaled18) + invariant, err := math.StableMath.ComputeInvariant(p.vault.AmplificationParameter, p.vault.BalancesLiveScaled18) if err != nil { return nil, err } @@ -306,17 +280,18 @@ func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { cloned := *p - cloned.swapFeePercentage = p.swapFeePercentage.Clone() - cloned.aggregateSwapFeePercentage = p.aggregateSwapFeePercentage.Clone() - cloned.amplificationParameter = p.amplificationParameter.Clone() - cloned.balancesLiveScaled18 = lo.Map(p.balancesLiveScaled18, func(v *uint256.Int, _ int) *uint256.Int { - return new(uint256.Int).Set(v) - }) - cloned.decimalScalingFactors = lo.Map(p.decimalScalingFactors, func(v *uint256.Int, _ int) *uint256.Int { - return new(uint256.Int).Set(v) - }) - cloned.tokenRates = lo.Map(p.tokenRates, func(v *uint256.Int, _ int) *uint256.Int { - return new(uint256.Int).Set(v) - }) + // cloned.swapFeePercentage = p.swapFeePercentage.Clone() + // cloned.aggregateSwapFeePercentage = p.aggregateSwapFeePercentage.Clone() + // cloned.amplificationParameter = p.amplificationParameter.Clone() + // cloned.balancesLiveScaled18 = lo.Map(p.balancesLiveScaled18, func(v *uint256.Int, _ int) *uint256.Int { + // return new(uint256.Int).Set(v) + // }) + // cloned.decimalScalingFactors = lo.Map(p.decimalScalingFactors, func(v *uint256.Int, _ int) *uint256.Int { + // return new(uint256.Int).Set(v) + // }) + // cloned.tokenRates = lo.Map(p.tokenRates, func(v *uint256.Int, _ int) *uint256.Int { + // return new(uint256.Int).Set(v) + // }) + return &cloned } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index 3a3c80593..9fb494426 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -9,8 +9,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" ) @@ -33,15 +35,19 @@ func TestCalcAmountOut(t *testing.T) { }, }, }, - swapFeePercentage: uint256.NewInt(50000000000000), - aggregateSwapFeePercentage: uint256.NewInt(500000000000000000), - amplificationParameter: uint256.NewInt(5000), - tokenRates: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1), uint256.NewInt(100)}, - decimalScalingFactors: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1), uint256.NewInt(100)}, - balancesLiveScaled18: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1), uint256.NewInt(100)}, - - poolType: PoolType, - poolVersion: shared.PoolVersion1, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 + uint256.NewInt(1000000), + uint256.NewInt(30000000000000), + uint256.NewInt(500000000000000000), + ), + + poolType: PoolType, } tokenAmountIn := poolpkg.TokenAmount{ @@ -74,15 +80,19 @@ func TestCalcAmountOut(t *testing.T) { }, }, }, - swapFeePercentage: uint256.NewInt(30000000000000), - aggregateSwapFeePercentage: uint256.NewInt(500000000000000000), - amplificationParameter: uint256.NewInt(1000000), - tokenRates: []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, - decimalScalingFactors: []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, - balancesLiveScaled18: []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, - - poolType: PoolType, - poolVersion: shared.PoolVersion1, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 + uint256.NewInt(1000000), + uint256.NewInt(30000000000000), + uint256.NewInt(500000000000000000), + ), + + poolType: PoolType, } tokenAmountIn := poolpkg.TokenAmount{ @@ -124,9 +134,17 @@ func TestCalcAmountOut(t *testing.T) { }, }, }, - swapFeePercentage: uint256.NewInt(53332221119995), - amplificationParameter: uint256.NewInt(1390000), - decimalScalingFactors: []*uint256.Int{uint256.NewInt(100), uint256.NewInt(1000), uint256.NewInt(100)}, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 + uint256.NewInt(1000000), + uint256.NewInt(30000000000000), + uint256.NewInt(500000000000000000), + ), poolType: PoolType, poolVersion: 1, diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index 3b2fa21b6..f26f0a174 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -222,6 +222,7 @@ func (t *PoolTracker) queryRPC( return &RpcResult{ HooksConfig: shared.HooksConfig{ + EnableHookAdjustedAmounts: hooksConfig.Data.EnableHookAdjustedAmounts, ShouldCallComputeDynamicSwapFee: hooksConfig.Data.ShouldCallComputeDynamicSwapFee, ShouldCallBeforeSwap: hooksConfig.Data.ShouldCallBeforeSwap, ShouldCallAfterSwap: hooksConfig.Data.ShouldCallAfterSwap, diff --git a/pkg/liquidity-source/balancer-v3/vault/error.go b/pkg/liquidity-source/balancer-v3/vault/error.go index 282d52969..9b1c4b42b 100644 --- a/pkg/liquidity-source/balancer-v3/vault/error.go +++ b/pkg/liquidity-source/balancer-v3/vault/error.go @@ -9,4 +9,8 @@ var ( ErrPoolIsPaused = errors.New("pool is paused") ErrDynamicSwapFeeHookFailed = errors.New("dynamicSwapFeeHook is failed") ErrPercentageAboveMax = errors.New("percentage above max") + ErrSwapLimit = errors.New("swap limit error") + ErrHookAdjustedSwapLimit = errors.New("hook adjusted swap limit error") + ErrBeforeSwapHookFailed = errors.New("beforeSwapHook is failed") + ErrAfterSwapHookFailed = errors.New("afterSwapHook is failed") ) diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index fe78baebf..7d1e04df1 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -8,17 +8,21 @@ import ( ) type Vault struct { - hook hooks.IHook - hooksConfig shared.HooksConfig + AmplificationParameter *uint256.Int + BalancesLiveScaled18 []*uint256.Int + decimalScalingFactors []*uint256.Int tokenRates []*uint256.Int - balancesLiveScaled18 []*uint256.Int - amplificationParameter *uint256.Int swapFeePercentage *uint256.Int aggregateSwapFeePercentage *uint256.Int + + hook hooks.IHook + hooksConfig shared.HooksConfig + + isPoolInRecoveryMode bool } -func NewVault(hook hooks.IHook, hooksConfig shared.HooksConfig, +func New(hook hooks.IHook, hooksConfig shared.HooksConfig, isPoolInRecoveryMode bool, decimalScalingFactors, tokenRates, balancesLiveScaled18 []*uint256.Int, amplificationParameter, swapFeePercentage, aggregateSwapFeePercentage *uint256.Int, ) *Vault { @@ -27,8 +31,8 @@ func NewVault(hook hooks.IHook, hooksConfig shared.HooksConfig, hooksConfig: hooksConfig, decimalScalingFactors: decimalScalingFactors, tokenRates: tokenRates, - balancesLiveScaled18: balancesLiveScaled18, - amplificationParameter: amplificationParameter, + BalancesLiveScaled18: balancesLiveScaled18, + AmplificationParameter: amplificationParameter, swapFeePercentage: swapFeePercentage, aggregateSwapFeePercentage: aggregateSwapFeePercentage, } @@ -46,16 +50,21 @@ func (v *Vault) Swap( var poolSwapParams = shared.PoolSwapParams{ Kind: vaultSwapParams.Kind, + SwapFeePercentage: v.swapFeePercentage, AmountGivenScaled18: amountGivenScaled18, - BalancesLiveScaled18: v.balancesLiveScaled18, + BalancesLiveScaled18: v.BalancesLiveScaled18, IndexIn: vaultSwapParams.IndexIn, IndexOut: vaultSwapParams.IndexOut, } if v.hooksConfig.ShouldCallBeforeSwap { - v.hook.OnBeforeSwap() + if err := v.callBeforeSwapHook(poolSwapParams); err != nil { + return nil, nil, nil, err + } + // WARN: some states can be changed after hook } + if v.hooksConfig.ShouldCallComputeDynamicSwapFee { swapFeePercentage, err := v.callComputeDynamicSwapFeeHook(poolSwapParams) if err != nil { @@ -65,8 +74,9 @@ func (v *Vault) Swap( poolSwapParams.SwapFeePercentage = swapFeePercentage } + var totalSwapFeeAmountScaled18 *uint256.Int if vaultSwapParams.Kind == shared.EXACT_IN { - totalSwapFeeAmountScaled18, err := math.MulUp(poolSwapParams.AmountGivenScaled18, poolSwapParams.SwapFeePercentage) + totalSwapFeeAmountScaled18, err = math.MulUp(poolSwapParams.AmountGivenScaled18, poolSwapParams.SwapFeePercentage) if err != nil { return nil, nil, nil, err } @@ -92,20 +102,47 @@ func (v *Vault) Swap( return nil, nil, nil, ErrTradeAmountTooSmall } + var amountCalculated *uint256.Int if vaultSwapParams.Kind == shared.EXACT_IN { + if amountCalculated, err = toRawUndoRateRoundDown( + amountCalculatedScaled18, + v.decimalScalingFactors[poolSwapParams.IndexOut], + computeRateRoundUp(v.tokenRates[poolSwapParams.IndexOut]), + ); err != nil { + return nil, nil, nil, err + } + } else { + totalSwapFeeAmountScaled18, err := math.MulDivUp(amountCalculatedScaled18, v.swapFeePercentage, math.Complement(v.swapFeePercentage)) + if err != nil { + return nil, nil, nil, err + } + amountCalculatedScaled18, err = math.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) + if err != nil { + return nil, nil, nil, err + } + + if amountCalculated, err = toRawUndoRateRoundDown( + amountCalculatedScaled18, + v.decimalScalingFactors[poolSwapParams.IndexIn], + v.tokenRates[poolSwapParams.IndexIn], + ); err != nil { + return nil, nil, nil, err + } } - amountCalculated, err := v.ComputeAmountCalculatedRaw(true, amountGivenScaled18, v.swapFeePercentage, - v.decimalScalingFactors[param.IndexOut], v.tokenRates[param.IndexOut]) + totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(totalSwapFeeAmountScaled18, v.aggregateSwapFeePercentage, + v.decimalScalingFactors[poolSwapParams.IndexIn], v.tokenRates[poolSwapParams.IndexIn]) if err != nil { return nil, nil, nil, err } - totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(true, swapFeeScaled18, v.aggregateSwapFeePercentage, - param.DecimalScalingFactor, param.TokenRate) - if err != nil { - return nil, nil, nil, err + if v.hooksConfig.ShouldCallAfterSwap { + amountCalculated, err = v.callAfterSwapHook(vaultSwapParams, poolSwapParams.AmountGivenScaled18, + amountCalculatedScaled18, amountCalculated) + if err != nil { + return nil, nil, nil, err + } } return amountCalculated, totalSwapFee, aggregateFee, nil @@ -119,62 +156,41 @@ func (v *Vault) ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalS return toScaled18ApplyRateRoundUp(amountGiven, decimalScalingFactor, computeRateRoundUp(tokenRate)) } -func (v *Vault) ComputeAmountCalculatedRaw( - isExactIn bool, - amountCalculatedScaled18, swapFeePercentage, - decimalScalingFactor, tokenRate *uint256.Int, -) (*uint256.Int, error) { - if isExactIn { - return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, computeRateRoundUp(tokenRate)) - } - - totalSwapFeeAmountScaled18, err := math.MulDivUp(amountCalculatedScaled18, swapFeePercentage, math.Complement(swapFeePercentage)) - if err != nil { - return nil, err - } - - amountCalculatedScaled18, err = math.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) - if err != nil { - return nil, err - } - - return toRawUndoRateRoundDown(amountCalculatedScaled18, decimalScalingFactor, tokenRate) -} - func (v *Vault) ComputeAggregateSwapFees( - isExactIn bool, totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, decimalScalingFactor, tokenRate *uint256.Int, ) (*uint256.Int, *uint256.Int, error) { - if totalSwapFeeAmountScaled18.IsZero() { - return math.ZERO, math.ZERO, nil - } + if totalSwapFeeAmountScaled18.Sign() > 0 { + totalSwapFeeAmountRaw, err := toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) + if err != nil { + return nil, nil, err + } - totalSwapFeeAmountRaw, err := toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) - if err != nil { - return nil, nil, err - } + if !v.isPoolInRecoveryMode { + aggregateSwapFeeAmountRaw, err := math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) + if err != nil { + return nil, nil, err + } - // should check if pool is in Recovery Mode - aggregateFeeAmountRaw, err := math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) - if err != nil { - return nil, nil, err - } + if aggregateSwapFeeAmountRaw.Gt(totalSwapFeeAmountRaw) { + return nil, nil, ErrProtocolFeesExceedTotalCollected + } - if aggregateFeeAmountRaw.Gt(totalSwapFeeAmountRaw) { - return nil, nil, ErrProtocolFeesExceedTotalCollected + return totalSwapFeeAmountRaw, aggregateSwapFeeAmountRaw, nil + } } - return totalSwapFeeAmountRaw, aggregateFeeAmountRaw, nil + return math.ZERO, math.ZERO, nil } func (v *Vault) UpdateLiveBalance( - param shared.VaultSwapParams, + index int, + amountGivenRaw *uint256.Int, rounding shared.Rounding, ) (*uint256.Int, error) { if rounding == shared.ROUND_UP { - return toScaled18ApplyRateRoundUp(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) + return toScaled18ApplyRateRoundUp(amountGivenRaw, v.decimalScalingFactors[index], v.tokenRates[index]) } - return toScaled18ApplyRateRoundDown(param.AmountGiven, param.DecimalScalingFactor, param.TokenRate) + return toScaled18ApplyRateRoundDown(amountGivenRaw, v.decimalScalingFactors[index], v.tokenRates[index]) } diff --git a/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go b/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go index 02e586f34..89338106b 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go @@ -5,6 +5,55 @@ import ( "github.com/holiman/uint256" ) +func (v *Vault) callBeforeSwapHook(poolSwapParams shared.PoolSwapParams) error { + success, err := v.hook.OnBeforeSwap(poolSwapParams) + if err != nil { + return err + } + + if !success { + return ErrBeforeSwapHookFailed + } + + return nil +} + +func (v *Vault) callAfterSwapHook( + vaultParamSwap shared.VaultSwapParams, + amountGivenScaled18, + amountCalculatedScaled18, amountCalculatedRaw *uint256.Int, +) (*uint256.Int, error) { + amountInScaled18, amountOutScaled18 := amountCalculatedScaled18, amountGivenScaled18 + if vaultParamSwap.Kind == shared.EXACT_IN { + amountInScaled18, amountOutScaled18 = amountGivenScaled18, amountCalculatedScaled18 + } + + success, hookAdjustedAmountCalculatedRaw, err := v.hook.OnAfterSwap(shared.AfterSwapParams{ + Kind: vaultParamSwap.Kind, + IndexIn: vaultParamSwap.IndexIn, + IndexOut: vaultParamSwap.IndexOut, + AmountInScaled18: amountInScaled18, + AmountOutScaled18: amountOutScaled18, + TokenInBalanceScaled18: v.BalancesLiveScaled18[vaultParamSwap.IndexIn], + TokenOutBalanceScaled18: v.BalancesLiveScaled18[vaultParamSwap.IndexOut], + AmountCalculatedScaled18: amountCalculatedScaled18, + AmountCalculatedRaw: amountCalculatedRaw, + }) + if err != nil { + return nil, err + } + + if !success { + return nil, ErrAfterSwapHookFailed + } + + if !v.hooksConfig.EnableHookAdjustedAmounts { + return amountCalculatedRaw, nil + } + + return hookAdjustedAmountCalculatedRaw, nil +} + func (v *Vault) callComputeDynamicSwapFeeHook(poolSwapParams shared.PoolSwapParams) (*uint256.Int, error) { success, swapFeePercentage, err := v.hook.OnComputeDynamicSwapFeePercentage(poolSwapParams) if err != nil { From d46fd9fef0bb1fbb15520ce3dd1e746452ccb793 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 1 Jan 2025 17:29:03 +0700 Subject: [PATCH 13/39] restructure --- .../balancer-v3/hooks/base_hook.go | 14 +- .../balancer-v3/hooks/directional_fee.go | 2 + .../balancer-v3/hooks/fee_taking.go | 1 + .../balancer-v3/hooks/stable_surge.go | 4 +- .../balancer-v3/hooks/ve_BAL_fee_discount.go | 4 +- .../balancer-v3/stable/pool_simulator.go | 142 +++++++++--------- .../balancer-v3/stable/pool_simulator_test.go | 16 +- .../balancer-v3/vault/vault.go | 41 ++--- .../balancer-v3/vault/vault_hooks_lib.go | 22 +-- 9 files changed, 121 insertions(+), 125 deletions(-) create mode 100644 pkg/liquidity-source/balancer-v3/hooks/fee_taking.go diff --git a/pkg/liquidity-source/balancer-v3/hooks/base_hook.go b/pkg/liquidity-source/balancer-v3/hooks/base_hook.go index f4f61aa6c..813cef80a 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/base_hook.go +++ b/pkg/liquidity-source/balancer-v3/hooks/base_hook.go @@ -6,21 +6,23 @@ import ( "github.com/holiman/uint256" ) -type baseHook struct{} +type BaseHook struct{} -func NewBaseHook() *baseHook { - return &baseHook{} +var _ IHook = (*BaseHook)(nil) + +func NewBaseHook() *BaseHook { + return &BaseHook{} } -func (h *baseHook) OnBeforeSwap(shared.PoolSwapParams) (bool, error) { +func (h *BaseHook) OnBeforeSwap(shared.PoolSwapParams) (bool, error) { return false, nil } -func (h *baseHook) OnAfterSwap(shared.AfterSwapParams) (bool, *uint256.Int, error) { +func (h *BaseHook) OnAfterSwap(shared.AfterSwapParams) (bool, *uint256.Int, error) { return false, math.ZERO, nil } -func (h *baseHook) OnComputeDynamicSwapFeePercentage(shared.PoolSwapParams) (bool, *uint256.Int, error) { +func (h *BaseHook) OnComputeDynamicSwapFeePercentage(shared.PoolSwapParams) (bool, *uint256.Int, error) { return false, math.ZERO, nil } diff --git a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go index e1cfb0c8f..69bc83dd1 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go +++ b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go @@ -7,6 +7,8 @@ import ( ) type directionalFeeHook struct { + BaseHook + staticSwapFeePercentage *uint256.Int } diff --git a/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go b/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go new file mode 100644 index 000000000..6de678e59 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go @@ -0,0 +1 @@ +package hooks diff --git a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go index 68978011b..3ae6e95a0 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go +++ b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go @@ -5,7 +5,9 @@ import ( "github.com/holiman/uint256" ) -type StableSurgeHook struct{} +type StableSurgeHook struct { + BaseHook +} func NewStableSurgeHook() *StableSurgeHook { return &StableSurgeHook{} diff --git a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go index 9c75c2b27..8adcbe5f3 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go +++ b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go @@ -5,7 +5,9 @@ import ( "github.com/holiman/uint256" ) -type VeBALFeeDiscountHook struct{} +type VeBALFeeDiscountHook struct { + BaseHook +} func NewVeBALFeeDiscountHook() *VeBALFeeDiscountHook { return &VeBALFeeDiscountHook{} diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index 1de5e735a..a1f5dba62 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -27,7 +27,8 @@ var ( type PoolSimulator struct { poolpkg.Pool - vault *vault.Vault + vault *vault.Vault + currentAmp *uint256.Int hooksConfig shared.HooksConfig @@ -63,11 +64,11 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) } - // Need to detect the current using hook of pool + // Need to detect the current hook of pool hook := hooks.NewBaseHook() vault := vault.New(hook, extra.HooksConfig, extra.IsPoolInRecoveryMode, extra.DecimalScalingFactors, extra.TokenRates, - extra.BalancesLiveScaled18, extra.AmplificationParameter, extra.StaticSwapFeePercentage, extra.AggregateSwapFeePercentage) + extra.BalancesLiveScaled18, extra.StaticSwapFeePercentage, extra.AggregateSwapFeePercentage) poolInfo := poolpkg.PoolInfo{ Address: entityPool.Address, @@ -86,11 +87,59 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { isPoolInRecoveryMode: extra.IsPoolInRecoveryMode, vault: vault, hooksConfig: extra.HooksConfig, + currentAmp: extra.AmplificationParameter, vaultAddress: staticExtra.Vault, poolType: staticExtra.PoolType, }, nil } +func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { + if p.isVaultPaused { + return nil, shared.ErrVaultIsPaused + } + + if p.isPoolPaused { + return nil, shared.ErrPoolIsPaused + } + + tokenAmountIn, tokenOut := params.TokenAmountIn, params.TokenOut + + indexIn, indexOut := p.GetTokenIndex(tokenAmountIn.Token), p.GetTokenIndex(tokenOut) + if indexIn < 0 || indexOut < 0 { + return nil, ErrTokenNotRegistered + } + + amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) + if overflow { + return nil, ErrInvalidAmountIn + } + + amountOut, totalSwapFee, aggregateFee, err := p.vault.Swap(shared.VaultSwapParams{ + Kind: shared.EXACT_IN, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGivenRaw: amountIn, + }, p.OnSwap) + if err != nil { + return nil, err + } + + return &poolpkg.CalcAmountOutResult{ + TokenAmountOut: &poolpkg.TokenAmount{ + Token: tokenOut, + Amount: amountOut.ToBig(), + }, + Fee: &poolpkg.TokenAmount{ + Token: tokenAmountIn.Token, + Amount: totalSwapFee.ToBig(), + }, + SwapInfo: SwapInfo{ + AggregateFee: aggregateFee.ToBig(), + }, + Gas: defaultGas.Swap, + }, nil +} + func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { if p.isVaultPaused { return nil, shared.ErrVaultIsPaused @@ -138,16 +187,6 @@ func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk }, nil } -func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { - return PoolMetaInfo{ - Vault: s.vaultAddress, - PoolType: s.poolType, - PoolVersion: s.poolVersion, - TokenOutIndex: s.GetTokenIndex(tokenOut), - BlockNumber: s.Info.BlockNumber, - } -} - func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { tokenIndexIn := p.GetTokenIndex(params.TokenAmountIn.Token) tokenIndexOut := p.GetTokenIndex(params.TokenAmountOut.Token) @@ -164,12 +203,11 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { amountGivenRaw := uint256.MustFromBig(updatedRawBalanceIn) - updatedLiveBalanceIn, err := p.vault.UpdateLiveBalance(tokenIndexIn, amountGivenRaw, shared.ROUND_DOWN) + _, err := p.vault.UpdateLiveBalance(tokenIndexIn, amountGivenRaw, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return } - p.vault.BalancesLiveScaled18[tokenIndexIn] = updatedLiveBalanceIn updatedRawBalanceOut := new(big.Int) updatedRawBalanceOut.Sub(p.Info.Reserves[tokenIndexOut], params.TokenAmountOut.Amount) @@ -177,16 +215,25 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { amountGivenRaw.SetFromBig(updatedRawBalanceOut) - updatedLiveBalanceOut, err := p.vault.UpdateLiveBalance(tokenIndexOut, amountGivenRaw, shared.ROUND_DOWN) + _, err = p.vault.UpdateLiveBalance(tokenIndexOut, amountGivenRaw, shared.ROUND_DOWN) if err != nil { logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) return } - p.vault.BalancesLiveScaled18[tokenIndexOut] = updatedLiveBalanceOut +} + +func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { + return PoolMetaInfo{ + Vault: s.vaultAddress, + PoolType: s.poolType, + PoolVersion: s.poolVersion, + TokenOutIndex: s.GetTokenIndex(tokenOut), + BlockNumber: s.Info.BlockNumber, + } } func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error) { - invariant, err := p.computeInvariant(shared.ROUND_DOWN) + invariant, err := p.computeInvariant(param.BalancesLiveScaled18, shared.ROUND_DOWN) if err != nil { return nil, err } @@ -194,8 +241,8 @@ func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error var amountOutScaled18 *uint256.Int if param.Kind == shared.EXACT_IN { amountOutScaled18, err = math.StableMath.ComputeOutGivenExactIn( - p.vault.AmplificationParameter, - p.vault.BalancesLiveScaled18, + p.currentAmp, + param.BalancesLiveScaled18, param.IndexIn, param.IndexOut, param.AmountGivenScaled18, @@ -203,8 +250,8 @@ func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error ) } else { amountOutScaled18, err = math.StableMath.ComputeInGivenExactOut( - p.vault.AmplificationParameter, - p.vault.BalancesLiveScaled18, + p.currentAmp, + param.BalancesLiveScaled18, param.IndexIn, param.IndexOut, param.AmountGivenScaled18, @@ -218,8 +265,8 @@ func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error return amountOutScaled18, nil } -func (p *PoolSimulator) computeInvariant(rounding shared.Rounding) (*uint256.Int, error) { - invariant, err := math.StableMath.ComputeInvariant(p.vault.AmplificationParameter, p.vault.BalancesLiveScaled18) +func (p *PoolSimulator) computeInvariant(balancesLiveScaled18 []*uint256.Int, rounding shared.Rounding) (*uint256.Int, error) { + invariant, err := math.StableMath.ComputeInvariant(p.currentAmp, balancesLiveScaled18) if err != nil { return nil, err } @@ -231,53 +278,6 @@ func (p *PoolSimulator) computeInvariant(rounding shared.Rounding) (*uint256.Int return invariant, nil } -func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { - if p.isVaultPaused { - return nil, shared.ErrVaultIsPaused - } - - if p.isPoolPaused { - return nil, shared.ErrPoolIsPaused - } - - tokenAmountIn, tokenOut := params.TokenAmountIn, params.TokenOut - - indexIn, indexOut := p.GetTokenIndex(tokenAmountIn.Token), p.GetTokenIndex(tokenOut) - if indexIn < 0 || indexOut < 0 { - return nil, ErrTokenNotRegistered - } - - amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) - if overflow { - return nil, ErrInvalidAmountIn - } - - amountOut, totalSwapFee, aggregateFee, err := p.vault.Swap(shared.VaultSwapParams{ - Kind: shared.EXACT_IN, - IndexIn: indexIn, - IndexOut: indexOut, - AmountGivenRaw: amountIn, - }, p.OnSwap) - if err != nil { - return nil, err - } - - return &poolpkg.CalcAmountOutResult{ - TokenAmountOut: &poolpkg.TokenAmount{ - Token: tokenOut, - Amount: amountOut.ToBig(), - }, - Fee: &poolpkg.TokenAmount{ - Token: tokenAmountIn.Token, - Amount: totalSwapFee.ToBig(), - }, - SwapInfo: SwapInfo{ - AggregateFee: aggregateFee.ToBig(), - }, - Gas: defaultGas.Swap, - }, nil -} - func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { cloned := *p // cloned.swapFeePercentage = p.swapFeePercentage.Clone() diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index 9fb494426..9500c844d 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -42,12 +42,11 @@ func TestCalcAmountOut(t *testing.T) { []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(1000000), - uint256.NewInt(30000000000000), - uint256.NewInt(500000000000000000), + uint256.NewInt(30000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - - poolType: PoolType, + currentAmp: uint256.NewInt(1000000), + poolType: PoolType, } tokenAmountIn := poolpkg.TokenAmount{ @@ -87,12 +86,11 @@ func TestCalcAmountOut(t *testing.T) { []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(1000000), uint256.NewInt(30000000000000), uint256.NewInt(500000000000000000), ), - - poolType: PoolType, + currentAmp: uint256.NewInt(1000000), + poolType: PoolType, } tokenAmountIn := poolpkg.TokenAmount{ @@ -141,10 +139,10 @@ func TestCalcAmountOut(t *testing.T) { []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(1000000), uint256.NewInt(30000000000000), uint256.NewInt(500000000000000000), ), + currentAmp: uint256.NewInt(1000000), poolType: PoolType, poolVersion: 1, diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index 7d1e04df1..53d52eca1 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -8,9 +8,7 @@ import ( ) type Vault struct { - AmplificationParameter *uint256.Int - BalancesLiveScaled18 []*uint256.Int - + balancesLiveScaled18 []*uint256.Int decimalScalingFactors []*uint256.Int tokenRates []*uint256.Int swapFeePercentage *uint256.Int @@ -24,15 +22,14 @@ type Vault struct { func New(hook hooks.IHook, hooksConfig shared.HooksConfig, isPoolInRecoveryMode bool, decimalScalingFactors, tokenRates, balancesLiveScaled18 []*uint256.Int, - amplificationParameter, swapFeePercentage, aggregateSwapFeePercentage *uint256.Int, + swapFeePercentage, aggregateSwapFeePercentage *uint256.Int, ) *Vault { return &Vault{ hook: hook, hooksConfig: hooksConfig, decimalScalingFactors: decimalScalingFactors, tokenRates: tokenRates, - BalancesLiveScaled18: balancesLiveScaled18, - AmplificationParameter: amplificationParameter, + balancesLiveScaled18: balancesLiveScaled18, swapFeePercentage: swapFeePercentage, aggregateSwapFeePercentage: aggregateSwapFeePercentage, } @@ -42,7 +39,7 @@ func (v *Vault) Swap( vaultSwapParams shared.VaultSwapParams, onSwap func(param shared.PoolSwapParams) (*uint256.Int, error), ) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - amountGivenScaled18, err := v.ComputeAmountGivenScaled18(true, vaultSwapParams.AmountGivenRaw, + amountGivenScaled18, err := v.ComputeAmountGivenScaled18(vaultSwapParams.Kind, vaultSwapParams.AmountGivenRaw, v.decimalScalingFactors[vaultSwapParams.IndexOut], v.tokenRates[vaultSwapParams.IndexOut]) if err != nil { return nil, nil, nil, err @@ -52,7 +49,7 @@ func (v *Vault) Swap( Kind: vaultSwapParams.Kind, SwapFeePercentage: v.swapFeePercentage, AmountGivenScaled18: amountGivenScaled18, - BalancesLiveScaled18: v.BalancesLiveScaled18, + BalancesLiveScaled18: v.balancesLiveScaled18, IndexIn: vaultSwapParams.IndexIn, IndexOut: vaultSwapParams.IndexOut, } @@ -148,8 +145,8 @@ func (v *Vault) Swap( return amountCalculated, totalSwapFee, aggregateFee, nil } -func (v *Vault) ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - if isExactIn { +func (v *Vault) ComputeAmountGivenScaled18(kind shared.SwapKind, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { + if kind == shared.EXACT_IN { return toScaled18ApplyRateRoundDown(amountGiven, decimalScalingFactor, tokenRate) } @@ -159,15 +156,15 @@ func (v *Vault) ComputeAmountGivenScaled18(isExactIn bool, amountGiven, decimalS func (v *Vault) ComputeAggregateSwapFees( totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, decimalScalingFactor, tokenRate *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { +) (totalSwapFeeAmountRaw, aggregateSwapFeeAmountRaw *uint256.Int, err error) { if totalSwapFeeAmountScaled18.Sign() > 0 { - totalSwapFeeAmountRaw, err := toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) + totalSwapFeeAmountRaw, err = toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) if err != nil { return nil, nil, err } if !v.isPoolInRecoveryMode { - aggregateSwapFeeAmountRaw, err := math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) + aggregateSwapFeeAmountRaw, err = math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) if err != nil { return nil, nil, err } @@ -176,8 +173,10 @@ func (v *Vault) ComputeAggregateSwapFees( return nil, nil, ErrProtocolFeesExceedTotalCollected } - return totalSwapFeeAmountRaw, aggregateSwapFeeAmountRaw, nil + return } + + return totalSwapFeeAmountRaw, math.ZERO, nil } return math.ZERO, math.ZERO, nil @@ -187,10 +186,18 @@ func (v *Vault) UpdateLiveBalance( index int, amountGivenRaw *uint256.Int, rounding shared.Rounding, -) (*uint256.Int, error) { +) (newBalanceLiveScaled18 *uint256.Int, err error) { if rounding == shared.ROUND_UP { - return toScaled18ApplyRateRoundUp(amountGivenRaw, v.decimalScalingFactors[index], v.tokenRates[index]) + newBalanceLiveScaled18, err = toScaled18ApplyRateRoundUp(amountGivenRaw, v.decimalScalingFactors[index], v.tokenRates[index]) + } else { + newBalanceLiveScaled18, err = toScaled18ApplyRateRoundDown(amountGivenRaw, v.decimalScalingFactors[index], v.tokenRates[index]) } - return toScaled18ApplyRateRoundDown(amountGivenRaw, v.decimalScalingFactors[index], v.tokenRates[index]) + if err != nil { + return nil, err + } + + v.balancesLiveScaled18[index] = newBalanceLiveScaled18 + + return } diff --git a/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go b/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go index 89338106b..b6279b19a 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault_hooks_lib.go @@ -34,8 +34,8 @@ func (v *Vault) callAfterSwapHook( IndexOut: vaultParamSwap.IndexOut, AmountInScaled18: amountInScaled18, AmountOutScaled18: amountOutScaled18, - TokenInBalanceScaled18: v.BalancesLiveScaled18[vaultParamSwap.IndexIn], - TokenOutBalanceScaled18: v.BalancesLiveScaled18[vaultParamSwap.IndexOut], + TokenInBalanceScaled18: v.balancesLiveScaled18[vaultParamSwap.IndexIn], + TokenOutBalanceScaled18: v.balancesLiveScaled18[vaultParamSwap.IndexOut], AmountCalculatedScaled18: amountCalculatedScaled18, AmountCalculatedRaw: amountCalculatedRaw, }) @@ -70,21 +70,3 @@ func (v *Vault) callComputeDynamicSwapFeeHook(poolSwapParams shared.PoolSwapPara return swapFeePercentage, nil } - -// (bool success, uint256 swapFeePercentage) = hooksContract.onComputeDynamicSwapFeePercentage( -// swapParams, -// pool, -// staticSwapFeePercentage -// ); - -// if (success == false) { -// revert IVaultErrors.DynamicSwapFeeHookFailed(); -// } - -// // A 100% fee is not supported. In the ExactOut case, the Vault divides by the complement of the swap fee. -// // The minimum precision constraint provides an additional buffer. -// if (swapFeePercentage > MAX_FEE_PERCENTAGE) { -// revert IVaultErrors.PercentageAboveMax(); -// } - -// return swapFeePercentage; From d82d528f8e8b809afd1f57a129554e3179c3d0e9 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 1 Jan 2025 20:06:22 +0700 Subject: [PATCH 14/39] cleanup --- pkg/liquidity-source/balancer-v3/gyro/abis.go | 28 - .../gyro/abis/ComposableStablePool.json | 1422 ----------------- .../balancer-v3/gyro/bpt_swap.go | 862 ---------- .../balancer-v3/gyro/config.go | 12 - .../balancer-v3/gyro/constant.go | 38 - .../balancer-v3/gyro/embed.go | 6 - .../balancer-v3/gyro/error.go | 12 - .../balancer-v3/gyro/pool_simulator.go | 298 ---- .../balancer-v3/gyro/pool_simulator_test.go | 1041 ------------ .../balancer-v3/gyro/pool_tracker.go | 478 ------ .../balancer-v3/gyro/pools_list_updater.go | 197 --- .../balancer-v3/gyro/regular_swap.go | 206 --- pkg/liquidity-source/balancer-v3/gyro/type.go | 107 -- .../balancer-v3/hooks/directional_fee.go | 10 +- .../balancer-v3/hooks/ihook.go | 13 + .../balancer-v3/hooks/stable_surge.go | 18 - .../balancer-v3/hooks/ve_BAL_fee_discount.go | 52 - .../math/{constant.go => const.go} | 2 - .../balancer-v3/math/error.go | 11 - .../balancer-v3/math/fixed_point_math.go | 165 ++ .../balancer-v3/math/log_exp_math.go | 120 ++ pkg/liquidity-source/balancer-v3/math/math.go | 108 -- .../balancer-v3/math/stable_math.go | 4 +- .../balancer-v3/math/weighted_math.go | 284 ++-- .../balancer-v3/shared/error.go | 7 + .../balancer-v3/shared/type.go | 4 + .../balancer-v3/stable/constant.go | 4 +- .../balancer-v3/stable/error.go | 12 - .../balancer-v3/stable/pool_simulator.go | 29 +- .../balancer-v3/stable/pool_simulator_test.go | 5 - .../balancer-v3/stable/pool_tracker.go | 8 + .../balancer-v3/stable/pools_list_updater.go | 3 +- .../balancer-v3/stable/type.go | 39 +- .../balancer-v3/vault/scaling_helper.go | 12 +- .../balancer-v3/vault/vault.go | 21 +- .../balancer-v3/weighted/config.go | 10 +- .../balancer-v3/weighted/constant.go | 8 + .../balancer-v3/weighted/error.go | 7 + .../balancer-v3/weighted/pool_simulator.go | 479 ++---- .../weighted/pool_simulator_test.go | 776 +++------ .../balancer-v3/weighted/pool_tracker.go | 275 ++-- .../weighted/pools_list_updater.go | 166 +- .../balancer-v3/weighted/type.go | 72 +- 43 files changed, 1068 insertions(+), 6363 deletions(-) delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/abis.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/config.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/constant.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/embed.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/error.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/regular_swap.go delete mode 100644 pkg/liquidity-source/balancer-v3/gyro/type.go rename pkg/liquidity-source/balancer-v3/math/{constant.go => const.go} (80%) delete mode 100644 pkg/liquidity-source/balancer-v3/math/error.go create mode 100644 pkg/liquidity-source/balancer-v3/math/fixed_point_math.go create mode 100644 pkg/liquidity-source/balancer-v3/math/log_exp_math.go delete mode 100644 pkg/liquidity-source/balancer-v3/math/math.go delete mode 100644 pkg/liquidity-source/balancer-v3/stable/error.go create mode 100644 pkg/liquidity-source/balancer-v3/weighted/error.go diff --git a/pkg/liquidity-source/balancer-v3/gyro/abis.go b/pkg/liquidity-source/balancer-v3/gyro/abis.go deleted file mode 100644 index 80b4b26f7..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/abis.go +++ /dev/null @@ -1,28 +0,0 @@ -package composablestable - -import ( - "bytes" - - "github.com/ethereum/go-ethereum/accounts/abi" -) - -var ( - poolABI abi.ABI -) - -func init() { - builder := []struct { - ABI *abi.ABI - data []byte - }{ - {&poolABI, poolJson}, - } - - for _, b := range builder { - var err error - *b.ABI, err = abi.JSON(bytes.NewReader(b.data)) - if err != nil { - panic(err) - } - } -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json b/pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json deleted file mode 100644 index fa35778fc..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/abis/ComposableStablePool.json +++ /dev/null @@ -1,1422 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { - "internalType": "contract IVault", - "name": "vault", - "type": "address" - }, - { - "internalType": "contract IProtocolFeePercentagesProvider", - "name": "protocolFeeProvider", - "type": "address" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "internalType": "contract IRateProvider[]", - "name": "rateProviders", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "tokenRateCacheDurations", - "type": "uint256[]" - }, - { - "internalType": "bool", - "name": "exemptFromYieldProtocolFeeFlag", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "amplificationParameter", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pauseWindowDuration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "bufferPeriodDuration", - "type": "uint256" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "string", - "name": "version", - "type": "string" - } - ], - "internalType": "struct ComposableStablePool.NewPoolParams", - "name": "params", - "type": "tuple" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "startValue", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "endValue", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "startTime", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - } - ], - "name": "AmpUpdateStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "currentValue", - "type": "uint256" - } - ], - "name": "AmpUpdateStopped", - "type": "event" - }, - { - "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": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "feeType", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "protocolFeePercentage", - "type": "uint256" - } - ], - "name": "ProtocolFeePercentageCacheUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "enabled", - "type": "bool" - } - ], - "name": "RecoveryModeStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "SwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "tokenIndex", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "rate", - "type": "uint256" - } - ], - "name": "TokenRateCacheUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "tokenIndex", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "contract IRateProvider", - "name": "provider", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cacheDuration", - "type": "uint256" - } - ], - "name": "TokenRateProviderSet", - "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" - }, - { - "inputs": [], - "name": "DELEGATE_PROTOCOL_SWAP_FEES_SENTINEL", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DOMAIN_SEPARATOR", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "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": "amount", - "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": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "decreaseAllowance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "disableRecoveryMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "enableRecoveryMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "getActionId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getActualSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAmplificationParameter", - "outputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isUpdating", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "precision", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAuthorizer", - "outputs": [ - { - "internalType": "contract IAuthorizer", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getBptIndex", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getDomainSeparator", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getLastJoinExitData", - "outputs": [ - { - "internalType": "uint256", - "name": "lastJoinExitAmplification", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastPostJoinExitInvariant", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getMinimumBpt", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "getNextNonce", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getPausedState", - "outputs": [ - { - "internalType": "bool", - "name": "paused", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "pauseWindowEndTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "bufferPeriodEndTime", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getPoolId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "feeType", - "type": "uint256" - } - ], - "name": "getProtocolFeePercentageCache", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getProtocolFeesCollector", - "outputs": [ - { - "internalType": "contract IProtocolFeesCollector", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getProtocolSwapFeeDelegation", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getRate", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getRateProviders", - "outputs": [ - { - "internalType": "contract IRateProvider[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getScalingFactors", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getSwapFeePercentage", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getTokenRate", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getTokenRateCache", - "outputs": [ - { - "internalType": "uint256", - "name": "rate", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "oldRate", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "duration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "expires", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getVault", - "outputs": [ - { - "internalType": "contract IVault", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "inRecoveryMode", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "addedValue", - "type": "uint256" - } - ], - "name": "increaseAllowance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "isExemptFromYieldProtocolFee", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "isTokenExemptFromYieldProtocolFee", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "nonces", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "onExitPool", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "onJoinPool", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum IVault.SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct IPoolSwapStructs.SwapRequest", - "name": "swapRequest", - "type": "tuple" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "indexIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "indexOut", - "type": "uint256" - } - ], - "name": "onSwap", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "permit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "queryExit", - "outputs": [ - { - "internalType": "uint256", - "name": "bptIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "amountsOut", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "queryJoin", - "outputs": [ - { - "internalType": "uint256", - "name": "bptOut", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "amountsIn", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "bytes", - "name": "poolConfig", - "type": "bytes" - } - ], - "name": "setAssetManagerPoolConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "setSwapFeePercentage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "duration", - "type": "uint256" - } - ], - "name": "setTokenRateCacheDuration", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "rawEndValue", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - } - ], - "name": "startAmplificationParameterUpdate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "stopAmplificationParameterUpdate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "updateProtocolFeePercentageCache", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "updateTokenRateCache", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - } -] \ No newline at end of file diff --git a/pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go b/pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go deleted file mode 100644 index d61ea694f..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/bpt_swap.go +++ /dev/null @@ -1,862 +0,0 @@ -package composablestable - -import ( - "math/big" - - "github.com/holiman/uint256" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" - poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/valueobject" -) - -type bptSimulator struct { - poolpkg.Pool - - bptIndex int - bptTotalSupply *uint256.Int - amp *uint256.Int - scalingFactors []*uint256.Int - lastJoinExit LastJoinExitData - rateProviders []string - tokenRateCaches []TokenRateCache - - swapFeePercentage *uint256.Int - protocolFeePercentageCache map[int]*uint256.Int - tokenExemptFromYieldProtocolFee []bool - exemptFromYieldProtocolFee bool // >= V5 - inRecoveryMode bool - - poolTypeVer int -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L301 -/** - * @dev Perform a swap involving the BPT token, equivalent to a single-token join or exit. As with the standard - * joins and swaps, we first pay any protocol fees pending from swaps that occurred since the previous join or - * exit, then perform the operation (joinSwap or exitSwap), and finally store the "post operation" invariant and - * amp, which establishes the new basis for protocol fees. - * - * At this point, the scaling factors (including rates) have been computed by the base class, but not yet applied - * to the balances. - */ -func (s *bptSimulator) _swapWithBpt( - isGivenIn bool, - swapRequestAmount *uint256.Int, - balances []*uint256.Int, - registeredIndexIn int, - registeredIndexOut int, -) (*uint256.Int, *poolpkg.TokenAmount, *SwapInfo, error) { - balances, err := _upscaleArray(balances, s.scalingFactors) - if err != nil { - return nil, nil, nil, err - } - - var swapRequestTokenIndex int - if isGivenIn { - swapRequestTokenIndex = registeredIndexIn - } else { - swapRequestTokenIndex = registeredIndexOut - } - - swapRequestAmount, err = _upscale(swapRequestAmount, s.scalingFactors[swapRequestTokenIndex]) - if err != nil { - return nil, nil, nil, err - } - - preJoinExitSupply, balances, currentAmp, preJoinExitInvariant, err := s._beforeJoinExit(balances) - if err != nil { - return nil, nil, nil, err - } - - var amountCalculated, postJoinExitSupply *uint256.Int - if registeredIndexOut == s.bptIndex { - amountCalculated, postJoinExitSupply, err = s._doJoinSwap( - isGivenIn, swapRequestAmount, balances, _skipBptIndex(registeredIndexIn, s.bptIndex), currentAmp, preJoinExitSupply, preJoinExitInvariant, - ) - } else { - amountCalculated, postJoinExitSupply, err = s._doExitSwap( - isGivenIn, swapRequestAmount, balances, _skipBptIndex(registeredIndexOut, s.bptIndex), currentAmp, preJoinExitSupply, preJoinExitInvariant, - ) - } - if err != nil { - return nil, nil, nil, err - } - - var downscaledAmountCalculated *uint256.Int - if isGivenIn { - downscaledAmountCalculated, err = _downscaleDown(amountCalculated, s.scalingFactors[registeredIndexOut]) - if err != nil { - return nil, nil, nil, err - } - } else { - downscaledAmountCalculated, err = _downscaleUp(amountCalculated, s.scalingFactors[registeredIndexIn]) - if err != nil { - return nil, nil, nil, err - } - } - - swapInfo, err := s.initSwapInfo( - currentAmp, - balances, - preJoinExitInvariant, - preJoinExitSupply, - postJoinExitSupply, - ) - if err != nil { - return nil, nil, nil, err - } - - return downscaledAmountCalculated, &poolpkg.TokenAmount{}, swapInfo, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L362 -/** - * @dev This mutates `balances` so that they become the post-joinswap balances. The StableMath interfaces - * are different depending on the swap direction, so we forward to the appropriate low-level join function. - */ -func (s *bptSimulator) _doJoinSwap( - isGivenIn bool, - amount *uint256.Int, - balances []*uint256.Int, - indexIn int, - currentAmp *uint256.Int, - actualSupply *uint256.Int, - preJoinExitInvariant *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - if isGivenIn { - return s._joinSwapExactTokenInForBptOut( - amount, - balances, - indexIn, - currentAmp, - actualSupply, - preJoinExitInvariant, - ) - } - - return s._joinSwapExactBptOutForTokenIn( - amount, - balances, - indexIn, - currentAmp, - actualSupply, - preJoinExitInvariant, - ) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L396 -/** - * @dev Since this is a join, we know the tokenOut is BPT. Since it is GivenIn, we know the tokenIn amount, - * and must calculate the BPT amount out. - * We are moving preminted BPT out of the Vault, which increases the virtual supply. - */ -func (s *bptSimulator) _joinSwapExactTokenInForBptOut( - amountIn *uint256.Int, - balances []*uint256.Int, - indexIn int, - currentAmp *uint256.Int, - actualSupply *uint256.Int, - preJoinExitInvariant *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - amountsIn := make([]*uint256.Int, len(balances)) - for i := 0; i < len(balances); i++ { - amountsIn[i] = uint256.NewInt(0) - } - amountsIn[indexIn] = amountIn - - bptOut, err := math.StableMath.CalcBptOutGivenExactTokensIn( - currentAmp, - balances, - amountsIn, - actualSupply, - preJoinExitInvariant, - s.swapFeePercentage, - ) - if err != nil { - return nil, nil, err - } - - balances[indexIn], err = math.FixedPoint.Add(balances[indexIn], amountIn) - if err != nil { - return nil, nil, err - } - - postJoinExitSupply, err := math.FixedPoint.Add(actualSupply, bptOut) - if err != nil { - return nil, nil, err - } - - return bptOut, postJoinExitSupply, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L429 -/** - * @dev Since this is a join, we know the tokenOut is BPT. Since it is GivenOut, we know the BPT amount, - * and must calculate the token amount in. - * We are moving preminted BPT out of the Vault, which increases the virtual supply. - */ -func (s *bptSimulator) _joinSwapExactBptOutForTokenIn( - bptOut *uint256.Int, - balances []*uint256.Int, - indexIn int, - currentAmp *uint256.Int, - actualSupply *uint256.Int, - preJoinExitInvariant *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - amountIn, err := math.StableMath.CalcTokenInGivenExactBptOut( - currentAmp, - balances, - indexIn, - bptOut, - actualSupply, - preJoinExitInvariant, - s.swapFeePercentage, - ) - if err != nil { - return nil, nil, err - } - - balances[indexIn], err = math.FixedPoint.Add(balances[indexIn], amountIn) - if err != nil { - return nil, nil, err - } - - postJoinExitSupply, err := math.FixedPoint.Add(actualSupply, bptOut) - if err != nil { - return nil, nil, err - } - - return amountIn, postJoinExitSupply, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L457 -/** - * @dev This mutates balances so that they become the post-exitswap balances. The StableMath interfaces are - * different depending on the swap direction, so we forward to the appropriate low-level exit function. - */ -func (s *bptSimulator) _doExitSwap( - isGivenIn bool, - amount *uint256.Int, - balances []*uint256.Int, - indexOut int, - currentAmp *uint256.Int, - actualSupply *uint256.Int, - preJoinExitInvariant *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - if isGivenIn { - return s._exitSwapExactBptInForTokenOut( - amount, - balances, - indexOut, - currentAmp, - actualSupply, - preJoinExitInvariant, - ) - } - - return s._exitSwapExactTokenOutForBptIn( - amount, - balances, - indexOut, - currentAmp, - actualSupply, - preJoinExitInvariant, - ) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L491 -/** - * @dev Since this is an exit, we know the tokenIn is BPT. Since it is GivenIn, we know the BPT amount, - * and must calculate the token amount out. - * We are moving BPT out of circulation and into the Vault, which decreases the virtual supply. - */ -func (s *bptSimulator) _exitSwapExactBptInForTokenOut( - bptAmount *uint256.Int, - balances []*uint256.Int, - indexOut int, - currentAmp *uint256.Int, - actualSupply *uint256.Int, - preJoinExitInvariant *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - amountOut, err := math.StableMath.CalcTokenOutGivenExactBptIn( - currentAmp, - balances, - indexOut, - bptAmount, - actualSupply, - preJoinExitInvariant, - s.swapFeePercentage, - ) - if err != nil { - return nil, nil, err - } - - balances[indexOut], err = math.FixedPoint.Sub(balances[indexOut], amountOut) - if err != nil { - return nil, nil, err - } - - postJoinExitSupply, err := math.FixedPoint.Sub(actualSupply, bptAmount) - if err != nil { - return nil, nil, err - } - - return amountOut, postJoinExitSupply, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L520 -/** - * @dev Since this is an exit, we know the tokenIn is BPT. Since it is GivenOut, we know the token amount out, - * and must calculate the BPT amount in. - * We are moving BPT out of circulation and into the Vault, which decreases the virtual supply. - */ -func (s *bptSimulator) _exitSwapExactTokenOutForBptIn( - amountOut *uint256.Int, - balances []*uint256.Int, - indexOut int, - currentAmp *uint256.Int, - actualSupply *uint256.Int, - preJoinExitInvariant *uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - // The StableMath function was created with exits in mind, so it expects a full amounts array. We create an - // empty one and only set the amount for the token involved. - - amountsOut := make([]*uint256.Int, len(balances)) - for i := 0; i < len(balances); i++ { - amountsOut[i] = uint256.NewInt(0) - } - amountsOut[indexOut] = amountOut - - bptAmount, err := math.StableMath.CalcBptInGivenExactTokensOut( - currentAmp, - balances, - amountsOut, - actualSupply, - preJoinExitInvariant, - s.swapFeePercentage, - ) - if err != nil { - return nil, nil, err - } - - balances[indexOut], err = math.FixedPoint.Sub(balances[indexOut], amountOut) - if err != nil { - return nil, nil, err - } - - postJoinExitSupply, err := math.FixedPoint.Sub(actualSupply, bptAmount) - if err != nil { - return nil, nil, err - } - - return bptAmount, postJoinExitSupply, nil -} - -func (s *bptSimulator) _getVirtualSupply(bptBalance *uint256.Int) (*uint256.Int, error) { - cir, err := math.FixedPoint.Sub(s.bptTotalSupply, bptBalance) - if err != nil { - return nil, err - } - return cir, nil -} - -func (s *bptSimulator) _hasRateProvider(tokenIndex int) bool { - if s.rateProviders[tokenIndex] == "" || s.rateProviders[tokenIndex] == valueobject.ZeroAddress { - return false - } - return true -} - -func (s *bptSimulator) _beforeJoinExit( - registeredBalances []*uint256.Int, -) (*uint256.Int, []*uint256.Int, *uint256.Int, *uint256.Int, error) { - preJoinExitSupply, balances, oldAmpPreJoinExitInvariant, err := s._payProtocolFeesBeforeJoinExit(registeredBalances) - if err != nil { - return nil, nil, nil, nil, err - } - - var preJoinExitInvariant *uint256.Int - if s.amp.Eq(s.lastJoinExit.LastJoinExitAmplification) { - preJoinExitInvariant = oldAmpPreJoinExitInvariant - } else { - preJoinExitInvariant, err = math.StableMath.CalculateInvariantV2( - s.amp, - balances, - ) - if err != nil { - return nil, nil, nil, nil, err - } - } - - return preJoinExitSupply, balances, s.amp, preJoinExitInvariant, nil -} - -func (s *bptSimulator) _payProtocolFeesBeforeJoinExit( - registeredBalances []*uint256.Int, -) (*uint256.Int, []*uint256.Int, *uint256.Int, error) { - virtualSupply, balances, err := s._dropBptItemFromBalances(registeredBalances) - if err != nil { - return nil, nil, nil, err - } - - expectedProtocolOwnershipPercentage, currentInvariantWithLastJoinExitAmp, err := s._getProtocolPoolOwnershipPercentage(balances) - if err != nil { - return nil, nil, nil, err - } - - protocolFeeAmount, err := s.protocolFeeAmount(virtualSupply, expectedProtocolOwnershipPercentage) - if err != nil { - return nil, nil, nil, err - } - - return new(uint256.Int).Add(virtualSupply, protocolFeeAmount), - balances, - currentInvariantWithLastJoinExitAmp, - nil -} - -func (s *bptSimulator) _getProtocolPoolOwnershipPercentage( - balances []*uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - if s.poolTypeVer == poolTypeVer5 { - return s._getProtocolPoolOwnershipPercentageV2(balances) - } - return s._getProtocolPoolOwnershipPercentageV1(balances) -} - -func (s *bptSimulator) _getProtocolPoolOwnershipPercentageV2( - balances []*uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, err := s._getGrowthInvariantsV2(balances) - if err != nil { - return nil, nil, err - } - - if totalGrowthInvariant.Cmp(s.lastJoinExit.LastPostJoinExitInvariant) <= 0 { - return uint256.NewInt(0), totalGrowthInvariant, nil - } - - swapFeeGrowthInvariantDelta := new(uint256.Int).Sub( - swapFeeGrowthInvariant, s.lastJoinExit.LastPostJoinExitInvariant, - ) - - nonExemptYieldGrowthInvariantDelta := new(uint256.Int).Sub( - totalNonExemptGrowthInvariant, swapFeeGrowthInvariant, - ) - - var protocolSwapFeePercentage *uint256.Int - { - percentage := s.getProtocolFeePercentageCache(feeTypeSwap) - u, err := math.FixedPoint.DivDown(swapFeeGrowthInvariantDelta, totalGrowthInvariant) - if err != nil { - return nil, nil, err - } - u, err = math.FixedPoint.MulDown(u, percentage) - if err != nil { - return nil, nil, err - } - - protocolSwapFeePercentage = u - } - - var protocolYieldPercentage *uint256.Int - { - percentage := s.getProtocolFeePercentageCache(feeTypeYield) - u, err := math.FixedPoint.DivDown( - nonExemptYieldGrowthInvariantDelta, - totalGrowthInvariant, - ) - if err != nil { - return nil, nil, err - } - - u, err = math.FixedPoint.MulDown(u, percentage) - if err != nil { - return nil, nil, err - } - - protocolYieldPercentage = u - } - - return new(uint256.Int).Add(protocolSwapFeePercentage, protocolYieldPercentage), totalGrowthInvariant, nil -} - -func (s *bptSimulator) _getProtocolPoolOwnershipPercentageV1( - balances []*uint256.Int, -) (*uint256.Int, *uint256.Int, error) { - swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, err := s._getGrowthInvariantsV1(balances) - if err != nil { - return nil, nil, err - } - - swapFeeGrowthInvariantDelta := uint256.NewInt(0) - if swapFeeGrowthInvariant.Gt(s.lastJoinExit.LastPostJoinExitInvariant) { - swapFeeGrowthInvariantDelta = new(uint256.Int).Sub( - swapFeeGrowthInvariant, s.lastJoinExit.LastPostJoinExitInvariant, - ) - } - - nonExemptYieldGrowthInvariantDelta := uint256.NewInt(0) - if totalNonExemptGrowthInvariant.Gt(swapFeeGrowthInvariant) { - nonExemptYieldGrowthInvariantDelta = new(uint256.Int).Sub( - totalNonExemptGrowthInvariant, swapFeeGrowthInvariant, - ) - } - - var protocolSwapFeePercentage *uint256.Int - { - percentage := s.getProtocolFeePercentageCache(feeTypeSwap) - u, err := math.FixedPoint.DivDown(swapFeeGrowthInvariantDelta, totalGrowthInvariant) - if err != nil { - return nil, nil, err - } - u, err = math.FixedPoint.MulDown(u, percentage) - if err != nil { - return nil, nil, err - } - - protocolSwapFeePercentage = u - } - - var protocolYieldPercentage *uint256.Int - { - percentage := s.getProtocolFeePercentageCache(feeTypeYield) - u, err := math.FixedPoint.DivDown( - nonExemptYieldGrowthInvariantDelta, - totalGrowthInvariant, - ) - if err != nil { - return nil, nil, err - } - - u, err = math.FixedPoint.MulDown(u, percentage) - if err != nil { - return nil, nil, err - } - - protocolYieldPercentage = u - } - - return new(uint256.Int).Add(protocolSwapFeePercentage, protocolYieldPercentage), totalGrowthInvariant, nil -} - -func (s *bptSimulator) _isTokenExemptFromYieldProtocolFee(registeredTokenIndex int) bool { - return s.tokenExemptFromYieldProtocolFee[registeredTokenIndex] -} - -func (s *bptSimulator) _getGrowthInvariantsV1( - balances []*uint256.Int, -) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - var ( - swapFeeGrowthInvariant *uint256.Int - totalNonExemptGrowthInvariant *uint256.Int - totalGrowthInvariant *uint256.Int - err error - ) - - adjustedBalances, err := s._getAdjustedBalanceV1(balances, true) - if err != nil { - return nil, nil, nil, err - } - - swapFeeGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, - adjustedBalances, - ) - if err != nil { - return nil, nil, nil, err - } - - if s._areNoTokensExempt() { - totalNonExemptGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, - balances, - ) - if err != nil { - return nil, nil, nil, err - } - - totalGrowthInvariant = totalNonExemptGrowthInvariant - } else if s._areAllTokensExempt() { - totalNonExemptGrowthInvariant = swapFeeGrowthInvariant - totalGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, balances, - ) - if err != nil { - return nil, nil, nil, err - } - } else { - adjustedBalances, err := s._getAdjustedBalanceV1(balances, false) - if err != nil { - return nil, nil, nil, err - } - - totalNonExemptGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, - adjustedBalances, - ) - if err != nil { - return nil, nil, nil, err - } - - totalGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, - balances, - ) - if err != nil { - return nil, nil, nil, err - } - } - - return swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, nil -} - -func (s *bptSimulator) _getAdjustedBalanceV1( - balances []*uint256.Int, - ignoreExemptFlags bool, -) ([]*uint256.Int, error) { - totalTokensWithoutBpt := len(balances) - adjustedBalances := make([]*uint256.Int, totalTokensWithoutBpt) - - for i := 0; i < totalTokensWithoutBpt; i++ { - skipBptIndex := i - if i >= s.bptIndex { - skipBptIndex++ - } - - if s._isTokenExemptFromYieldProtocolFee(skipBptIndex) || - (ignoreExemptFlags && s._hasRateProvider(skipBptIndex)) { - var err error - adjustedBalances[i], err = _adjustedBalance(balances[i], s.tokenRateCaches[skipBptIndex]) - if err != nil { - return nil, err - } - - continue - } - - adjustedBalances[i] = balances[i] - } - - return adjustedBalances, nil -} - -func (s *bptSimulator) _getGrowthInvariantsV2( - balances []*uint256.Int, -) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - var ( - swapFeeGrowthInvariant *uint256.Int - totalNonExemptGrowthInvariant *uint256.Int - totalGrowthInvariant *uint256.Int - err error - ) - - totalGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, - balances, - ) - if err != nil { - return nil, nil, nil, err - } - - if totalGrowthInvariant.Cmp(s.lastJoinExit.LastPostJoinExitInvariant) <= 0 { - return totalGrowthInvariant, totalGrowthInvariant, totalGrowthInvariant, nil - } - - adjustedBalances, err := s._getAdjustedBalanceV2(balances) - if err != nil { - return nil, nil, nil, err - } - swapFeeGrowthInvariant, err = math.StableMath.CalculateInvariantV2( - s.lastJoinExit.LastJoinExitAmplification, - adjustedBalances, - ) - if err != nil { - return nil, nil, nil, err - } - - swapFeeGrowthInvariant = math.Math.Min(totalGrowthInvariant, swapFeeGrowthInvariant) - swapFeeGrowthInvariant = math.Math.Max(s.lastJoinExit.LastPostJoinExitInvariant, swapFeeGrowthInvariant) - - if s.isExemptFromYieldProtocolFee() { - totalNonExemptGrowthInvariant = swapFeeGrowthInvariant - } else { - totalNonExemptGrowthInvariant = totalGrowthInvariant - } - - return swapFeeGrowthInvariant, totalNonExemptGrowthInvariant, totalGrowthInvariant, nil -} - -func (s *bptSimulator) _getAdjustedBalanceV2(balances []*uint256.Int) ([]*uint256.Int, error) { - totalTokensWithoutBpt := len(balances) - adjustedBalances := make([]*uint256.Int, totalTokensWithoutBpt) - - for i := 0; i < totalTokensWithoutBpt; i++ { - skipBptIndex := i - if i >= s.bptIndex { - skipBptIndex++ - } - - if s._hasRateProvider(skipBptIndex) { - var err error - adjustedBalances[i], err = _adjustedBalance(balances[i], s.tokenRateCaches[skipBptIndex]) - if err != nil { - return nil, err - } - continue - } - - adjustedBalances[i] = balances[i] - } - - return adjustedBalances, nil -} - -func (s *bptSimulator) isExemptFromYieldProtocolFee() bool { - return s.exemptFromYieldProtocolFee -} - -func (s *bptSimulator) _areAllTokensExempt() bool { - for _, exempt := range s.tokenExemptFromYieldProtocolFee { - if !exempt { - return false - } - } - return true -} - -func (s *bptSimulator) _areNoTokensExempt() bool { - for _, exempt := range s.tokenExemptFromYieldProtocolFee { - if exempt { - return false - } - } - return true -} - -func (s *bptSimulator) getProtocolFeePercentageCache(feeType int) *uint256.Int { - if s.inRecoveryMode { - return uint256.NewInt(0) - } - - return s.protocolFeePercentageCache[feeType] -} - -func (s *bptSimulator) protocolFeeAmount( - totalSupply *uint256.Int, - poolOwnershipPercentage *uint256.Int, -) (*uint256.Int, error) { - if s.poolTypeVer == poolTypeVer1 { - return s._calculateAdjustedProtocolFeeAmount(totalSupply, poolOwnershipPercentage) - } - - return s.bptForPoolOwnershipPercentage(totalSupply, poolOwnershipPercentage) -} - -func (s *bptSimulator) _calculateAdjustedProtocolFeeAmount( - totalSupply *uint256.Int, - poolOwnershipPercentage *uint256.Int, -) (*uint256.Int, error) { - u, err := math.FixedPoint.MulDown(totalSupply, poolOwnershipPercentage) - if err != nil { - return nil, err - } - - u, err = math.FixedPoint.DivDown(u, math.FixedPoint.Complement(poolOwnershipPercentage)) - if err != nil { - return nil, err - } - - return u, nil -} - -func (s *bptSimulator) bptForPoolOwnershipPercentage( - totalSupply *uint256.Int, - poolOwnershipPercentage *uint256.Int, -) (*uint256.Int, error) { - u, err := math.Math.Mul(totalSupply, poolOwnershipPercentage) - if err != nil { - return nil, err - } - u, err = math.Math.DivDown(u, math.FixedPoint.Complement(poolOwnershipPercentage)) - if err != nil { - return nil, err - } - return u, nil -} - -func (s *bptSimulator) _dropBptItemFromBalances( - registeredBalances []*uint256.Int, -) (*uint256.Int, []*uint256.Int, error) { - virtualSupply, err := s._getVirtualSupply(registeredBalances[s.bptIndex]) - if err != nil { - return nil, nil, err - } - - balances := _dropBptItem(registeredBalances, s.bptIndex) - - return virtualSupply, balances, nil -} - -func (s *bptSimulator) initSwapInfo( - currentAmp *uint256.Int, - balances []*uint256.Int, - preJoinExitInvariant *uint256.Int, - preJoinExitSupply *uint256.Int, - postJoinExitSupply *uint256.Int, -) (*SwapInfo, error) { - postJoinExitInvariant, err := math.StableMath.CalculateInvariantV2(currentAmp, balances) - if err != nil { - return nil, err - } - - swapInfo := &SwapInfo{ - LastJoinExitData: LastJoinExitData{ - LastJoinExitAmplification: currentAmp, - LastPostJoinExitInvariant: postJoinExitInvariant, - }, - } - - return swapInfo, nil -} - -func (s *bptSimulator) updateBalance(params poolpkg.UpdateBalanceParams) { - for idx, token := range s.Info.Tokens { - // update reserves - - if token == params.TokenAmountIn.Token { - s.Info.Reserves[idx] = new(big.Int).Add( - s.Info.Reserves[idx], - params.TokenAmountIn.Amount, - ) - } - - if token == params.TokenAmountOut.Token { - s.Info.Reserves[idx] = new(big.Int).Sub( - s.Info.Reserves[idx], - params.TokenAmountOut.Amount, - ) - } - - // update rates - - if s._hasRateProvider(idx) { - s.tokenRateCaches[idx].OldRate = s.tokenRateCaches[idx].Rate - } - } - - swapInfo, ok := params.SwapInfo.(*SwapInfo) - if !ok { - return - } - s.lastJoinExit = swapInfo.LastJoinExitData -} - -func _adjustedBalance(balance *uint256.Int, cache TokenRateCache) (*uint256.Int, error) { - u, err := math.Math.Mul(balance, cache.OldRate) - if err != nil { - return nil, err - } - return math.Math.DivDown(u, cache.Rate) -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/config.go b/pkg/liquidity-source/balancer-v3/gyro/config.go deleted file mode 100644 index 628dd94f2..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/config.go +++ /dev/null @@ -1,12 +0,0 @@ -package composablestable - -import ( - "net/http" -) - -type Config struct { - DexID string `json:"dexID"` - SubgraphAPI string `json:"subgraphAPI"` - SubgraphHeaders http.Header `json:"subgraphHeaders"` - NewPoolLimit int `json:"newPoolLimit"` -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/constant.go b/pkg/liquidity-source/balancer-v3/gyro/constant.go deleted file mode 100644 index 0263f1063..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/constant.go +++ /dev/null @@ -1,38 +0,0 @@ -package composablestable - -const ( - DexType = "balancer-v2-composable-stable" -) - -var ( - DefaultGas = Gas{Swap: 80000} -) - -const ( - poolTypeComposableStable = "ComposableStable" - - poolTypeVer1 = 1 - poolTypeVer5 = 5 - - poolMethodGetSwapFeePercentage = "getSwapFeePercentage" - poolMethodGetPausedState = "getPausedState" - poolMethodGetAmplificationParameter = "getAmplificationParameter" - poolMethodGetBptIndex = "getBptIndex" - poolMethodTotalSupply = "totalSupply" - poolMethodGetLastJoinExitData = "getLastJoinExitData" - poolMethodGetRateProviders = "getRateProviders" - poolMethodGetTokenRateCache = "getTokenRateCache" - poolMethodGetProtocolFeePercentageCache = "getProtocolFeePercentageCache" - poolMethodIsTokenExemptFromYieldProtocolFee = "isTokenExemptFromYieldProtocolFee" - poolMethodInRecoveryMode = "inRecoveryMode" - poolMethodIsExemptFromYieldProtocolFee = "isExemptFromYieldProtocolFee" - poolMethodGetRate = "getRate" - poolMethodGetVault = "getVault" - - unknownInt = -1 - - feeTypeSwap = 0 - feeTypeYield = 2 - - zeroAddress = "0x0000000000000000000000000000000000000000" -) diff --git a/pkg/liquidity-source/balancer-v3/gyro/embed.go b/pkg/liquidity-source/balancer-v3/gyro/embed.go deleted file mode 100644 index 0dc4c79e5..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/embed.go +++ /dev/null @@ -1,6 +0,0 @@ -package composablestable - -import _ "embed" - -//go:embed abis/ComposableStablePool.json -var poolJson []byte diff --git a/pkg/liquidity-source/balancer-v3/gyro/error.go b/pkg/liquidity-source/balancer-v3/gyro/error.go deleted file mode 100644 index 9cf0531d8..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/error.go +++ /dev/null @@ -1,12 +0,0 @@ -package composablestable - -import "errors" - -var ( - ErrOverflow = errors.New("overflow") - ErrUnknownToken = errors.New("unknown token") - ErrInvalidReserve = errors.New("invalid reserve") - ErrReserveNotFound = errors.New("reserve not found") - ErrPoolPaused = errors.New("pool is paused") - ErrBeforeSwapJoinExit = errors.New("before swap join exit") -) diff --git a/pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go b/pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go deleted file mode 100644 index 6c2d7a107..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/pool_simulator.go +++ /dev/null @@ -1,298 +0,0 @@ -package composablestable - -import ( - "math/big" - - "github.com/goccy/go-json" - "github.com/holiman/uint256" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" - poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" -) - -type PoolSimulator struct { - poolpkg.Pool - - paused bool - canNotUpdateTokenRates bool - - regularSimulator *regularSimulator - bptSimulator *bptSimulator - - vault string - poolID string - poolTypeVer int -} - -func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { - var ( - extra Extra - staticExtra StaticExtra - - tokens = make([]string, len(entityPool.Tokens)) - reserves = make([]*big.Int, len(entityPool.Tokens)) - ) - - if err := json.Unmarshal([]byte(entityPool.Extra), &extra); err != nil { - return nil, err - } - - if err := json.Unmarshal([]byte(entityPool.StaticExtra), &staticExtra); err != nil { - return nil, err - } - - for idx := 0; idx < len(entityPool.Tokens); idx++ { - tokens[idx] = entityPool.Tokens[idx].Address - reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) - } - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: entityPool.Address, - Exchange: entityPool.Exchange, - Type: entityPool.Type, - Tokens: tokens, - Reserves: reserves, - Checked: true, - BlockNumber: entityPool.BlockNumber, - }, - } - - regularSimulator := regularSimulator{ - Pool: pool, - bptIndex: staticExtra.BptIndex, - scalingFactors: extra.ScalingFactors, - amp: extra.Amp, - swapFeePercentage: extra.SwapFeePercentage, - } - - bptSimulator := bptSimulator{ - Pool: pool, - bptIndex: staticExtra.BptIndex, - bptTotalSupply: extra.BptTotalSupply, - amp: extra.Amp, - scalingFactors: extra.ScalingFactors, - lastJoinExit: extra.LastJoinExit, - rateProviders: extra.RateProviders, - tokenRateCaches: extra.TokenRateCaches, - swapFeePercentage: extra.SwapFeePercentage, - protocolFeePercentageCache: extra.ProtocolFeePercentageCache, - tokenExemptFromYieldProtocolFee: extra.IsTokenExemptFromYieldProtocolFee, - exemptFromYieldProtocolFee: extra.IsExemptFromYieldProtocolFee, - inRecoveryMode: extra.InRecoveryMode, - - poolTypeVer: staticExtra.PoolTypeVer, - } - - return &PoolSimulator{ - Pool: pool, - paused: extra.Paused, - canNotUpdateTokenRates: extra.CanNotUpdateTokenRates, - regularSimulator: ®ularSimulator, - bptSimulator: &bptSimulator, - vault: staticExtra.Vault, - poolID: staticExtra.PoolID, - poolTypeVer: staticExtra.PoolTypeVer, - }, nil -} - -func (s *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { - if s.paused { - return nil, ErrPoolPaused - } - - if s.canNotUpdateTokenRates { - return nil, ErrBeforeSwapJoinExit - } - - tokenAmountIn := params.TokenAmountIn - tokenOut := params.TokenOut - - indexIn := s.GetTokenIndex(tokenAmountIn.Token) - indexOut := s.GetTokenIndex(tokenOut) - if indexIn == unknownInt || indexOut == unknownInt { - return nil, ErrUnknownToken - } - - amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) - if overflow { - return nil, ErrOverflow - } - - balances := make([]*uint256.Int, len(s.Info.Reserves)) - for i, reserve := range s.Info.Reserves { - r, overflow := uint256.FromBig(reserve) - if overflow { - return nil, ErrOverflow - } - balances[i] = r - } - - var ( - amountOut *uint256.Int - fee *poolpkg.TokenAmount - swapInfo *SwapInfo - err error - ) - if tokenAmountIn.Token == s.Info.Address || tokenOut == s.Info.Address { - amountOut, fee, swapInfo, err = s.bptSimulator._swapWithBpt(true, amountIn, balances, indexIn, indexOut) - } else { - amountOut, fee, swapInfo, err = s.regularSimulator._swapGivenIn(amountIn, balances, indexIn, indexOut) - } - if err != nil { - return nil, err - } - - return &poolpkg.CalcAmountOutResult{ - TokenAmountOut: &poolpkg.TokenAmount{ - Token: tokenOut, - Amount: amountOut.ToBig(), - }, - Fee: fee, - Gas: DefaultGas.Swap, - SwapInfo: swapInfo, - }, nil -} - -func (s *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { - if s.paused { - return nil, ErrPoolPaused - } - - if s.canNotUpdateTokenRates { - return nil, ErrBeforeSwapJoinExit - } - - tokenAmountOut := params.TokenAmountOut - tokenIn := params.TokenIn - - indexIn := s.GetTokenIndex(tokenIn) - indexOut := s.GetTokenIndex(tokenAmountOut.Token) - if indexIn == unknownInt || indexOut == unknownInt { - return nil, ErrUnknownToken - } - - amountIn, overflow := uint256.FromBig(tokenAmountOut.Amount) - if overflow { - return nil, ErrOverflow - } - - balances := make([]*uint256.Int, len(s.Info.Reserves)) - for i, reserve := range s.Info.Reserves { - r, overflow := uint256.FromBig(reserve) - if overflow { - return nil, ErrOverflow - } - balances[i] = r - } - - var ( - amountOut *uint256.Int - fee *poolpkg.TokenAmount - swapInfo *SwapInfo - err error - ) - if tokenAmountOut.Token == s.Info.Address || tokenIn == s.Info.Address { - amountOut, fee, swapInfo, err = s.bptSimulator._swapWithBpt(false, amountIn, balances, indexIn, indexOut) - } else { - amountOut, fee, swapInfo, err = s.regularSimulator._swapGivenOut(amountIn, balances, indexIn, indexOut) - } - if err != nil { - return nil, err - } - - return &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: tokenIn, - Amount: amountOut.ToBig(), - }, - Fee: fee, - Gas: DefaultGas.Swap, - SwapInfo: swapInfo, - }, nil -} - -func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { - return PoolMetaInfo{ - Vault: s.vault, - PoolID: s.poolID, - TokenOutIndex: s.GetTokenIndex(tokenOut), - BlockNumber: s.Info.BlockNumber, - } -} - -func (s *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { - if params.TokenAmountIn.Token == s.Info.Address || params.TokenAmountOut.Token == s.Info.Address { - s.bptSimulator.updateBalance(params) - return - } - - s.regularSimulator.updateBalance(params) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L696 -/** - * @dev Reverses the `scalingFactor` applied to `amount`, resulting in a smaller or equal value depending on - * whether it needed scaling or not. The result is rounded down. - */ -func _downscaleDown(amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { - return math.FixedPoint.DivDown(amount, scalingFactor) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L717 -/** - * @dev Reverses the `scalingFactor` applied to `amount`, resulting in a smaller or equal value depending on - * whether it needed scaling or not. The result is rounded up. - */ -func _downscaleUp(amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { - return math.FixedPoint.DivUp(amount, scalingFactor) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L683 -/** - * @dev Same as `_upscale`, but for an entire array. This function does not return anything, but instead *mutates* - * the `amounts` array. - */ -func _upscaleArray(balances []*uint256.Int, scalingFactors []*uint256.Int) ([]*uint256.Int, error) { - upscaled := make([]*uint256.Int, len(balances)) - for i, balance := range balances { - upscaledI, err := _upscale(balance, scalingFactors[i]) - if err != nil { - return nil, err - } - upscaled[i] = upscaledI - } - return upscaled, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L671 -/** - * @dev Applies `scalingFactor` to `amount`, resulting in a larger or equal value depending on whether it needed - * scaling or not. - */ -func _upscale(amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { - return math.FixedPoint.MulDown(amount, scalingFactor) -} - -func _dropBptItem(amounts []*uint256.Int, bptIndex int) []*uint256.Int { - amountsWithoutBpt := make([]*uint256.Int, len(amounts)-1) - - for i := 0; i < len(amountsWithoutBpt); i++ { - if i < bptIndex { - amountsWithoutBpt[i] = amounts[i] - continue - } - amountsWithoutBpt[i] = amounts[i+1] - } - - return amountsWithoutBpt -} - -func _skipBptIndex(index int, bptIndex int) int { - if index < bptIndex { - return index - } - return index - 1 -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go deleted file mode 100644 index b40a57e99..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/pool_simulator_test.go +++ /dev/null @@ -1,1041 +0,0 @@ -package composablestable - -import ( - "math/big" - "testing" - - "github.com/goccy/go-json" - "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" - poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" -) - -func TestRegularSwap(t *testing.T) { - t.Run("1. Should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("2596148429267407814265248164610048", 10) - reserve1, _ := new(big.Int).SetString("6999791779383984752", 10) - reserve2, _ := new(big.Int).SetString("3000000000000000000", 10) - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Tokens: []string{ - "0x00C2A4be503869Fa751c2DbcB7156cc970b5a8dA", - "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399", - "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - }, - }, - } - - regularSimulator := ®ularSimulator{ - Pool: pool, - swapFeePercentage: uint256.NewInt(100000000000000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000052057863883934), - uint256.NewInt(1000000000000000000), - }, - - bptIndex: 0, - amp: uint256.NewInt(1500000), - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - regularSimulator: regularSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399", - Amount: big.NewInt(999791779383984752), - } - tokenOut := "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA" - - // expected - expectedAmountOut := "998507669837625986" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("2. Should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("2596148429267407814265248164610048", 10) - reserve1, _ := new(big.Int).SetString("6999791779383984752", 10) - reserve2, _ := new(big.Int).SetString("3000000000000000000", 10) - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Tokens: []string{ - "0x00C2A4be503869Fa751c2DbcB7156cc970b5a8dA", - "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399", - "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - }, - }, - } - - regularSimulator := ®ularSimulator{ - Pool: pool, - swapFeePercentage: uint256.NewInt(100000000000000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(10000000000), - uint256.NewInt(10000520578), - uint256.NewInt(10000000000), - }, - - bptIndex: 0, - amp: uint256.NewInt(1500000), - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - regularSimulator: regularSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xF71d0774B214c4cf51E33Eb3d30Ef98132e4DBaA", - Amount: big.NewInt(23142175917219494), - } - tokenOut := "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399" - - // expected - expectedAmountOut := "23155810259460675" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) -} - -func TestBptSwap(t *testing.T) { - t.Run("1. Join swap pool type ver 1 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("414101427485347", 10) - reserve1, _ := new(big.Int).SetString("2596148429267348622595662702661260", 10) - reserve2, _ := new(big.Int).SetString("1170046233780600", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - Tokens: []string{ - "0x0000000000085d4780B73119b644AE5ecd22b376", - "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer1, - bptIndex: 1, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(600000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - uint256.NewInt(366332019912307), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(600000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - { - Rate: uint256.MustFromDecimal("1003857034775170156"), - OldRate: uint256.MustFromDecimal("1000977462514719154"), - Duration: uint256.NewInt(1000), - Expires: uint256.NewInt(1677904371), - }, - }, - swapFeePercentage: uint256.NewInt(100000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(0), - feeTypeYield: uint256.NewInt(0), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, true, - }, - inRecoveryMode: true, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xA13a9247ea42D743238089903570127DdA72fE44", - Amount: big.NewInt(170046233780600), - } - tokenOut := "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0" - - // expected - expectedAmountOut := "22005850083674" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("2. Join swap pool type ver 1 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("1414101427485347", 10) - reserve1, _ := new(big.Int).SetString("1596148429267348622595662702661260", 10) - reserve2, _ := new(big.Int).SetString("2170046233780600", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - Tokens: []string{ - "0x0000000000085d4780B73119b644AE5ecd22b376", - "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer1, - bptIndex: 1, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(600000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - uint256.NewInt(366332019912307), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(600000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - { - Rate: uint256.MustFromDecimal("1003857034775170156"), - OldRate: uint256.MustFromDecimal("1000977462514719154"), - Duration: uint256.NewInt(1000), - Expires: uint256.NewInt(1677904371), - }, - }, - swapFeePercentage: uint256.NewInt(100000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(0), - feeTypeYield: uint256.NewInt(0), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, true, - }, - inRecoveryMode: true, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x0000000000085d4780B73119b644AE5ecd22b376", - Amount: big.NewInt(214101427485347), - } - tokenOut := "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0" - - // expected - expectedAmountOut := "128189688116719916203223884786015" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("3. Join swap pool type ver 5 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) - reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) - reserve2, _ := new(big.Int).SetString("10406089385", 10) - reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - Tokens: []string{ - "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - reserve3, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer5, - bptIndex: 0, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(200000), - scalingFactors: []*uint256.Int{ - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000000000000000"), - uint256.MustFromDecimal("1008208139884891050"), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(200000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xd8689E8740C23d73136744817347fd6aC464E842", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - {}, - { - Rate: uint256.MustFromDecimal("1008130755672919714"), - OldRate: uint256.MustFromDecimal("1008130755672919714"), - Duration: uint256.NewInt(10800), - Expires: uint256.NewInt(1700764235), - }, - }, - swapFeePercentage: uint256.NewInt(500000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(500000000000000000), - feeTypeYield: uint256.NewInt(500000000000000000), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, false, false, - }, - exemptFromYieldProtocolFee: false, - inRecoveryMode: false, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - Amount: big.NewInt(2040500000000000), - } - tokenOut := "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c" - - // expected - expectedAmountOut := "72153658150470669505066070" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("4. Join swap pool type ver 5 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) - reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) - reserve2, _ := new(big.Int).SetString("10406089385", 10) - reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - Tokens: []string{ - "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - reserve3, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer5, - bptIndex: 0, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(200000), - scalingFactors: []*uint256.Int{ - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000000000000000"), - uint256.MustFromDecimal("1008208139884891050"), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(200000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xd8689E8740C23d73136744817347fd6aC464E842", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - {}, - { - Rate: uint256.MustFromDecimal("1008130755672919714"), - OldRate: uint256.MustFromDecimal("1008130755672919714"), - Duration: uint256.NewInt(10800), - Expires: uint256.NewInt(1700764235), - }, - }, - swapFeePercentage: uint256.NewInt(500000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(500000000000000000), - feeTypeYield: uint256.NewInt(500000000000000000), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, false, false, - }, - exemptFromYieldProtocolFee: false, - inRecoveryMode: false, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", - Amount: big.NewInt(4048384348048588331), - } - tokenOut := "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c" - - // expected - expectedAmountOut := "4071333855617864209" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("1. Exit swap pool type ver 1 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("414101427485347", 10) - reserve1, _ := new(big.Int).SetString("2596148429267348622595662702661260", 10) - reserve2, _ := new(big.Int).SetString("1170046233780600", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - Tokens: []string{ - "0x0000000000085d4780B73119b644AE5ecd22b376", - "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer1, - bptIndex: 1, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(600000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - uint256.NewInt(366332019912307), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(600000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - { - Rate: uint256.MustFromDecimal("1003857034775170156"), - OldRate: uint256.MustFromDecimal("1000977462514719154"), - Duration: uint256.NewInt(1000), - Expires: uint256.NewInt(1677904371), - }, - }, - swapFeePercentage: uint256.NewInt(100000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(0), - feeTypeYield: uint256.NewInt(0), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, true, - }, - inRecoveryMode: true, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - amountIn, _ := new(big.Int).SetString("95662702661260", 10) - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - Amount: amountIn, - } - tokenOut := "0xA13a9247ea42D743238089903570127DdA72fE44" - - // expected - expectedAmountOut := "473156052715491" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("2. Exit swap pool type ver 1 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("414101427485347", 10) - reserve1, _ := new(big.Int).SetString("2596148429267348622595662702661260", 10) - reserve2, _ := new(big.Int).SetString("1170046233780600", 10) - // 414101427485347,2596148429267348622595662702661260,1170046233780600 - - bptTotalSupply := uint256.MustFromDecimal("2596148429267348624180999930418421") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - Tokens: []string{ - "0x0000000000085d4780B73119b644AE5ecd22b376", - "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer1, - bptIndex: 1, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(600000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - uint256.NewInt(366332019912307), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(600000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("114012967613307699384"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xA13a9247ea42D743238089903570127DdA72fE44", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - { - Rate: uint256.MustFromDecimal("1003857034775170156"), - OldRate: uint256.MustFromDecimal("1000977462514719154"), - Duration: uint256.NewInt(1000), - Expires: uint256.NewInt(1677904371), - }, - }, - swapFeePercentage: uint256.NewInt(100000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(0), - feeTypeYield: uint256.NewInt(0), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, true, - }, - inRecoveryMode: true, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x2Ba7Aa2213Fa2C909Cd9E46FeD5A0059542b36B0", - Amount: big.NewInt(59566270266126), - } - tokenOut := "0x0000000000085d4780B73119b644AE5ecd22b376" - - // expected - expectedAmountOut := "17329834826337" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("3. Exit swap pool type ver 5 should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) - reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) - reserve2, _ := new(big.Int).SetString("10406089385", 10) - reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - Tokens: []string{ - "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - reserve3, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer5, - bptIndex: 0, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(200000), - scalingFactors: []*uint256.Int{ - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000000000000000"), - uint256.MustFromDecimal("1008208139884891050"), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(200000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xd8689E8740C23d73136744817347fd6aC464E842", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - {}, - { - Rate: uint256.MustFromDecimal("1008130755672919714"), - OldRate: uint256.MustFromDecimal("1008130755672919714"), - Duration: uint256.NewInt(10800), - }, - }, - swapFeePercentage: uint256.NewInt(500000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(500000000000000000), - feeTypeYield: uint256.NewInt(500000000000000000), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, false, false, - }, - exemptFromYieldProtocolFee: false, - inRecoveryMode: false, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - Amount: big.NewInt(2040500000000000), - } - tokenOut := "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09" - - // expected - expectedAmountOut := "2027780845478092" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("4. Exit swap pool type ver 5 swap should return OK", func(t *testing.T) { - // data - reserve0, _ := new(big.Int).SetString("2596148429267353763156769271943231", 10) - reserve1, _ := new(big.Int).SetString("20405000000000000000000", 10) - reserve2, _ := new(big.Int).SetString("10406089385", 10) - reserve3, _ := new(big.Int).SetString("20404838434804858833196", 10) - - bptTotalSupply := uint256.MustFromDecimal("2596148429318671447367809085209495") - - pool := poolpkg.Pool{ - Info: poolpkg.PoolInfo{ - Address: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - Tokens: []string{ - "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - "0x571f54D23cDf2211C83E9A0CbD92AcA36c48Fa02", - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, - reserve2, - reserve3, - }, - }, - } - - bptSimulator := &bptSimulator{ - poolTypeVer: poolTypeVer5, - bptIndex: 0, - bptTotalSupply: bptTotalSupply, - amp: uint256.NewInt(200000), - scalingFactors: []*uint256.Int{ - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000"), - uint256.MustFromDecimal("1000000000000000000000000000000"), - uint256.MustFromDecimal("1008208139884891050"), - }, - lastJoinExit: LastJoinExitData{ - LastJoinExitAmplification: uint256.NewInt(200000), - LastPostJoinExitInvariant: uint256.MustFromDecimal("51369044740270984486699"), - }, - rateProviders: []string{ - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000", - "0xd8689E8740C23d73136744817347fd6aC464E842", - }, - tokenRateCaches: []TokenRateCache{ - {}, - {}, - {}, - { - Rate: uint256.MustFromDecimal("1008130755672919714"), - OldRate: uint256.MustFromDecimal("1008130755672919714"), - Duration: uint256.NewInt(10800), - Expires: uint256.NewInt(1700764235), - }, - }, - swapFeePercentage: uint256.NewInt(500000000000000), - protocolFeePercentageCache: map[int]*uint256.Int{ - feeTypeSwap: uint256.NewInt(500000000000000000), - feeTypeYield: uint256.NewInt(500000000000000000), - }, - tokenExemptFromYieldProtocolFee: []bool{ - false, false, false, false, - }, - exemptFromYieldProtocolFee: false, - inRecoveryMode: false, - } - - poolSimulator := &PoolSimulator{ - Pool: pool, - bptSimulator: bptSimulator, - } - - // input - tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x01536b22ea06e4a315e3daaf05a12683ed4dc14c", - Amount: big.NewInt(4048384348048588331), - } - tokenOut := "0xaF4ce7CD4F8891ecf1799878c3e9A35b8BE57E09" - - // expected - expectedAmountOut := "4023147984636196801" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return poolSimulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: tokenAmountIn, - TokenOut: tokenOut, - }) - }) - - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) - }) -} - -func TestPoolSimulator_CalcAmountIn(t *testing.T) { - amountOutTest2, _ := new(big.Int).SetString("100000000", 10) - expectedAmountInTest2, _ := new(big.Int).SetString("99981105484344981876", 10) - amountOutTest3, _ := new(big.Int).SetString("100000000000000000000", 10) - expectedAmountInTest3, _ := new(big.Int).SetString("100018917", 10) - - type fields struct { - poolStr string - } - - tests := []struct { - name string - fields fields - params poolpkg.CalcAmountInParams - want *poolpkg.CalcAmountInResult - wantErr error - }{ - { - name: "1. should return error ErrPoolPaused", - fields: fields{ - poolStr: `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-composable-stable", - "type": "balancer-v2-composable-stable", - "timestamp": 1703667290, - "reserves": [ - "9999991000000000000", - "99999910000000000056", - "8897791020011100123456" - ], - "tokens": [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0x1388\",\"swapFeePercentage\":\"0x2D79883D2000\",\"scalingFactors\":[\"100\",\"1\",\"100\"],\"paused\":true}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"Stable\",\"poolTypeVersion\":1,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: big.NewInt(999999100000), - }, - TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - }, - want: nil, - wantErr: ErrPoolPaused, - }, - { - name: "2. should return OK", - fields: fields{ - poolStr: `{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","reserveUsd":1143324.9804121545,"amplifiedTvl":1143324.9804121545,"exchange":"balancer-v2-composable-stable","type":"balancer-v2-composable-stable","timestamp":1712718393,"reserves":["279496786025154287762267","2596148429569910245264763596342291","253647851077","610180343310"],"tokens":[{"address":"0x6b175474e89094c44da98b954eedeac495271d0f","swappable":true},{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","swappable":true},{"address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","swappable":true},{"address":"0xdac17f958d2ee523a2206206994597c13d831ec7","swappable":true}],"extra":"{\"canNotUpdateTokenRates\":false,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"bptTotalSupply\":\"2596148430699200833573624981511145\",\"amp\":\"5000000\",\"lastJoinExit\":{\"lastJoinExitAmplification\":\"5000000\",\"lastPostJoinExitInvariant\":\"1143300320131453789392387\"},\"rateProviders\":[\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\"],\"tokenRateCaches\":[{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null}],\"swapFeePercentage\":\"100000000000000\",\"protocolFeePercentageCache\":{\"0\":\"0\",\"2\":\"0\"},\"isTokenExemptFromYieldProtocolFee\":[false,false,false,false],\"isExemptFromYieldProtocolFee\":false,\"inRecoveryMode\":false,\"paused\":false}","staticExtra":"{\"poolId\":\"0x79c58f70905f734641735bc61e45c19dd9ad60bc0000000000000000000004e7\",\"poolType\":\"ComposableStable\",\"poolTypeVer\":3,\"bptIndex\":1,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}","blockNumber":19622438}`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: amountOutTest2, - }, - TokenIn: "0x6b175474e89094c44da98b954eedeac495271d0f", - }, - want: &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: "0x6b175474e89094c44da98b954eedeac495271d0f", - Amount: expectedAmountInTest2, - }, - }, - wantErr: nil, - }, - { - name: "3. should return OK", - fields: fields{ - poolStr: `{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","reserveUsd":1143324.9804121545,"amplifiedTvl":1143324.9804121545,"exchange":"balancer-v2-composable-stable","type":"balancer-v2-composable-stable","timestamp":1712718393,"reserves":["279496786025154287762267","2596148429569910245264763596342291","253647851077","610180343310"],"tokens":[{"address":"0x6b175474e89094c44da98b954eedeac495271d0f","swappable":true},{"address":"0x79c58f70905f734641735bc61e45c19dd9ad60bc","swappable":true},{"address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","swappable":true},{"address":"0xdac17f958d2ee523a2206206994597c13d831ec7","swappable":true}],"extra":"{\"canNotUpdateTokenRates\":false,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"bptTotalSupply\":\"2596148430699200833573624981511145\",\"amp\":\"5000000\",\"lastJoinExit\":{\"lastJoinExitAmplification\":\"5000000\",\"lastPostJoinExitInvariant\":\"1143300320131453789392387\"},\"rateProviders\":[\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\"],\"tokenRateCaches\":[{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null},{\"rate\":null,\"oldRate\":null,\"duration\":null,\"expires\":null}],\"swapFeePercentage\":\"100000000000000\",\"protocolFeePercentageCache\":{\"0\":\"0\",\"2\":\"0\"},\"isTokenExemptFromYieldProtocolFee\":[false,false,false,false],\"isExemptFromYieldProtocolFee\":false,\"inRecoveryMode\":false,\"paused\":false}","staticExtra":"{\"poolId\":\"0x79c58f70905f734641735bc61e45c19dd9ad60bc0000000000000000000004e7\",\"poolType\":\"ComposableStable\",\"poolTypeVer\":3,\"bptIndex\":1,\"scalingFactors\":[\"1000000000000000000\",\"1000000000000000000\",\"1000000000000000000000000000000\",\"1000000000000000000000000000000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}","blockNumber":19622438}`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0x6b175474e89094c44da98b954eedeac495271d0f", - Amount: amountOutTest3, - }, - TokenIn: "0xdac17f958d2ee523a2206206994597c13d831ec7", - }, - want: &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: expectedAmountInTest3, - }, - }, - wantErr: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var pool entity.Pool - err := json.Unmarshal([]byte(tt.fields.poolStr), &pool) - assert.Nil(t, err) - - simulator, err := NewPoolSimulator(pool) - assert.Nil(t, err) - - got, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { - return simulator.CalcAmountIn(tt.params) - }) - if err != nil { - assert.ErrorIsf(t, err, tt.wantErr, "PoolSimulator.CalcAmountIn() error = %v, wantErr %v", err, tt.wantErr) - return - } - assert.Equalf(t, tt.want.TokenAmountIn.Token, got.TokenAmountIn.Token, "tokenIn = %v, want %v", got.TokenAmountIn.Token, tt.want.TokenAmountIn.Token) - assert.Equalf(t, tt.want.TokenAmountIn.Amount, got.TokenAmountIn.Amount, "amountIn = %v, want %v", got.TokenAmountIn.Amount.String(), tt.want.TokenAmountIn.Amount.String()) - }) - } -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go b/pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go deleted file mode 100644 index 6de4a591e..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/pool_tracker.go +++ /dev/null @@ -1,478 +0,0 @@ -package composablestable - -import ( - "context" - "math/big" - "strings" - "time" - - "github.com/KyberNetwork/blockchain-toolkit/number" - "github.com/KyberNetwork/ethrpc" - "github.com/KyberNetwork/logger" - "github.com/ethereum/go-ethereum/common" - "github.com/goccy/go-json" - "github.com/holiman/uint256" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/shared" - poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" - "github.com/ethereum/go-ethereum/ethclient/gethclient" -) - -type PoolTracker struct { - config *Config - ethrpcClient *ethrpc.Client -} - -func NewPoolTracker( - config *Config, - ethrpcClient *ethrpc.Client, -) (*PoolTracker, error) { - return &PoolTracker{ - config: config, - ethrpcClient: ethrpcClient, - }, nil -} - -func (t *PoolTracker) GetNewPoolState( - ctx context.Context, - p entity.Pool, - params poolpkg.GetNewPoolStateParams, -) (entity.Pool, error) { - return t.getNewPoolState(ctx, p, params, nil) -} - -func (t *PoolTracker) GetNewPoolStateWithOverrides( - ctx context.Context, - p entity.Pool, - params poolpkg.GetNewPoolStateWithOverridesParams, -) (entity.Pool, error) { - return t.getNewPoolState(ctx, p, poolpkg.GetNewPoolStateParams{Logs: params.Logs}, params.Overrides) -} - -func (t *PoolTracker) getNewPoolState( - ctx context.Context, - p entity.Pool, - _ poolpkg.GetNewPoolStateParams, - overrides map[common.Address]gethclient.OverrideAccount, -) (entity.Pool, error) { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Info("Start updating state ...") - - defer func() { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Info("Finish updating state.") - }() - - var staticExtra StaticExtra - if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error(err.Error()) - - return p, err - } - - // call RPC - rpcRes, err := t.queryRPC( - ctx, - p.Address, - common.HexToHash(staticExtra.PoolID), - staticExtra.PoolTypeVer, - p.Tokens, - staticExtra.Vault, - overrides, - ) - if err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error(err.Error()) - - return p, err - } - - // update pool - - reserves, err := t.initReserves(ctx, p.Tokens, rpcRes.PoolTokens) - if err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error(err.Error()) - - return p, err - } - - extra, err := t.initExtra(ctx, rpcRes, staticExtra) - if err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error(err.Error()) - - return p, err - } - extraBytes, err := json.Marshal(extra) - if err != nil { - return p, err - } - - p.BlockNumber = rpcRes.BlockNumber - p.Timestamp = time.Now().Unix() - p.Reserves = reserves - p.Extra = string(extraBytes) - p.BlockNumber = rpcRes.BlockNumber - - return p, nil -} - -func (t *PoolTracker) queryRPC( - ctx context.Context, - poolAddress string, - poolID common.Hash, - poolTypeVer int, - tokens []*entity.PoolToken, - vault string, - overrides map[common.Address]gethclient.OverrideAccount, -) (*rpcRes, error) { - var ( - tokenNbr = len(tokens) - - poolTokens PoolTokensResp - bptTotalSupply *big.Int - ampParams AmplificationParameterResp - lastJoinExit LastJoinExitResp - rateProviders = make([]common.Address, tokenNbr) - tokenRateCaches = make([]TokenRateCacheResp, tokenNbr) - swapFeePercentage *big.Int - protocolFeePercentageCache = make(map[int]*big.Int) - isTokenExemptFromYieldProtocolFee = make([]bool, tokenNbr) - isExemptFromYieldProtocolFee bool - inRecoveryMode bool - pausedState PausedStateResp - - blockNbr *big.Int - - feeTypes = []int{feeTypeSwap, feeTypeYield} - ) - - /* - Call 1 get: - - poolTokens - - bptTotalSupply - - ampParams - - lastJoinExit - - rateProviders - - tokenRateCaches - - swapFeePercentage - - protocolFeePercentageCache - - isTokenExemptFromYieldProtocolFee - - isExemptFromYieldProtocolFee - - inRecoveryMode - - pausedState - */ - - req := t.ethrpcClient.R().SetContext(ctx) - if overrides != nil { - req.SetOverrides(overrides) - } - - req.AddCall(ðrpc.Call{ - ABI: shared.VaultABI, - Target: vault, - Method: shared.VaultMethodGetPoolTokens, - Params: []interface{}{poolID}, - }, []interface{}{&poolTokens}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodTotalSupply, - }, []interface{}{&bptTotalSupply}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetAmplificationParameter, - }, []interface{}{&Params}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetLastJoinExitData, - }, []interface{}{&lastJoinExit}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetRateProviders, - }, []interface{}{&rateProviders}) - - for i, token := range tokens { - tokenAddr := common.HexToAddress(token.Address) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetTokenRateCache, - Params: []interface{}{tokenAddr}, - }, []interface{}{&tokenRateCaches[i]}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodIsTokenExemptFromYieldProtocolFee, - Params: []interface{}{tokenAddr}, - }, []interface{}{&isTokenExemptFromYieldProtocolFee[i]}) - } - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetSwapFeePercentage, - }, []interface{}{&swapFeePercentage}) - - for _, feeType := range feeTypes { - value := big.NewInt(0) - protocolFeePercentageCache[feeType] = value - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetProtocolFeePercentageCache, - Params: []interface{}{big.NewInt(int64(feeType))}, - }, []interface{}{&value}) - } - - if poolTypeVer >= poolTypeVer5 { - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodIsExemptFromYieldProtocolFee, - }, []interface{}{&isExemptFromYieldProtocolFee}) - } - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodInRecoveryMode, - }, []interface{}{&inRecoveryMode}) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: poolAddress, - Method: poolMethodGetPausedState, - }, []interface{}{&pausedState}) - - res, err := req.TryBlockAndAggregate() - if err != nil { - return nil, err - } - - blockNbr = res.BlockNumber - - /* - Update token rate - */ - - canNotUpdateTokenRates := false - req = t.ethrpcClient.R().SetContext(ctx).SetBlockNumber(blockNbr) - if overrides != nil { - req.SetOverrides(overrides) - } - - rateUpdatedTokenIndexes := []int{} - updatedRate := make([]*big.Int, tokenNbr) - for i, token := range tokens { - if token.Address == poolAddress || - rateProviders[i].Hex() == zeroAddress || - time.Now().Unix() < tokenRateCaches[i].Expires.Int64() { - continue - } - - rateUpdatedTokenIndexes = append(rateUpdatedTokenIndexes, i) - - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: rateProviders[i].Hex(), - Method: poolMethodGetRate, - }, []interface{}{&updatedRate[i]}) - } - if len(rateUpdatedTokenIndexes) > 0 { - if _, err := req.Aggregate(); err != nil { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": poolAddress, - }).Warnf("can not update token rates: %s", err.Error()) - - canNotUpdateTokenRates = true - } - - for _, i := range rateUpdatedTokenIndexes { - if updatedRate[i] == nil { - continue - } - tokenRateCaches[i].Rate = updatedRate[i] - tokenRateCaches[i].Expires = big.NewInt(time.Now().Unix() + tokenRateCaches[i].Duration.Int64()) - } - } - - return &rpcRes{ - CanNotUpdateTokenRates: canNotUpdateTokenRates, - PoolTokens: poolTokens, - BptTotalSupply: bptTotalSupply, - Amp: ampParams.Value, - LastJoinExit: lastJoinExit, - RateProviders: rateProviders, - TokenRateCaches: tokenRateCaches, - SwapFeePercentage: swapFeePercentage, - ProtocolFeePercentageCache: protocolFeePercentageCache, - IsTokenExemptFromYieldProtocolFee: isTokenExemptFromYieldProtocolFee, - IsExemptFromYieldProtocolFee: isExemptFromYieldProtocolFee, - InRecoveryMode: inRecoveryMode, - PausedState: pausedState, - BlockNumber: res.BlockNumber.Uint64(), - }, nil -} - -func (t *PoolTracker) initExtra( - ctx context.Context, - rpcRes *rpcRes, - staticExtra StaticExtra, -) (*Extra, error) { - scalingFactors := make([]*uint256.Int, len(staticExtra.ScalingFactors)) - for i, scalingFactor := range staticExtra.ScalingFactors { - var rate *uint256.Int - if i == staticExtra.BptIndex || rpcRes.RateProviders[i].Hex() == zeroAddress { - rate = number.Number_1e18 - } else { - rate, _ = uint256.FromBig(rpcRes.TokenRateCaches[i].Rate) - } - - var err error - scalingFactors[i], err = math.FixedPoint.MulDown(scalingFactor, rate) - if err != nil { - return nil, err - } - } - - bptTotalSupply, overflow := uint256.FromBig(rpcRes.BptTotalSupply) - if overflow { - return nil, ErrOverflow - } - - amp, overflow := uint256.FromBig(rpcRes.Amp) - if overflow { - return nil, ErrOverflow - } - - var lastJoinExit LastJoinExitData - lastJoinExit.LastJoinExitAmplification, _ = uint256.FromBig( - rpcRes.LastJoinExit.LastJoinExitAmplification, - ) - lastJoinExit.LastPostJoinExitInvariant, _ = uint256.FromBig( - rpcRes.LastJoinExit.LastPostJoinExitInvariant, - ) - - rateProviders := make([]string, len(rpcRes.RateProviders)) - for i, rateProvider := range rpcRes.RateProviders { - rateProviders[i] = strings.ToLower(rateProvider.Hex()) - } - - tokenRateCaches := make([]TokenRateCache, len(rpcRes.TokenRateCaches)) - for i, tokenRateCache := range rpcRes.TokenRateCaches { - rate, _ := uint256.FromBig(tokenRateCache.Rate) - oldRate, _ := uint256.FromBig(tokenRateCache.OldRate) - duration, _ := uint256.FromBig(tokenRateCache.Duration) - expires, _ := uint256.FromBig(tokenRateCache.Expires) - tokenRateCaches[i] = TokenRateCache{ - Rate: rate, - OldRate: oldRate, - Duration: duration, - Expires: expires, - } - } - - swapFeePercentage, _ := uint256.FromBig(rpcRes.SwapFeePercentage) - - protocolFeePercentageCache := make(map[int]*uint256.Int) - for feeType, value := range rpcRes.ProtocolFeePercentageCache { - protocolFeePercentageCache[feeType], _ = uint256.FromBig(value) - } - - isTokenExemptFromYieldProtocolFee := rpcRes.IsTokenExemptFromYieldProtocolFee - - isExemptFromYieldProtocolFee := rpcRes.IsExemptFromYieldProtocolFee - - inRecoveryMode := rpcRes.InRecoveryMode - - paused := !isNotPaused(rpcRes.PausedState) - - canNotUpdateTokenRates := rpcRes.CanNotUpdateTokenRates - - extra := Extra{ - CanNotUpdateTokenRates: canNotUpdateTokenRates, - ScalingFactors: scalingFactors, - BptTotalSupply: bptTotalSupply, - Amp: amp, - LastJoinExit: lastJoinExit, - RateProviders: rateProviders, - TokenRateCaches: tokenRateCaches, - SwapFeePercentage: swapFeePercentage, - ProtocolFeePercentageCache: protocolFeePercentageCache, - IsTokenExemptFromYieldProtocolFee: isTokenExemptFromYieldProtocolFee, - IsExemptFromYieldProtocolFee: isExemptFromYieldProtocolFee, - InRecoveryMode: inRecoveryMode, - Paused: paused, - } - - return &extra, nil -} - -func (t *PoolTracker) initReserves( - ctx context.Context, - tokens []*entity.PoolToken, - poolTokens PoolTokensResp, -) ([]string, error) { - reserveByToken := make(map[string]*big.Int) - for idx, token := range poolTokens.Tokens { - addr := strings.ToLower(token.Hex()) - reserveByToken[addr] = poolTokens.Balances[idx] - } - - reserves := make([]string, len(tokens)) - for idx, token := range tokens { - r, ok := reserveByToken[token.Address] - if !ok { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": token.Address, - }).Error("can not get reserve") - - return nil, ErrReserveNotFound - } - - reserves[idx] = r.String() - } - - return reserves, nil -} - -func isNotPaused(pausedState PausedStateResp) bool { - return time.Now().Unix() > pausedState.BufferPeriodEndTime.Int64() || !pausedState.Paused -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go deleted file mode 100644 index 95d5fad21..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/pools_list_updater.go +++ /dev/null @@ -1,197 +0,0 @@ -package composablestable - -import ( - "context" - "math/big" - "strings" - "time" - - "github.com/KyberNetwork/blockchain-toolkit/number" - "github.com/KyberNetwork/ethrpc" - "github.com/KyberNetwork/logger" - "github.com/ethereum/go-ethereum/common" - "github.com/goccy/go-json" - "github.com/holiman/uint256" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/shared" -) - -type PoolsListUpdater struct { - config *Config - ethrpcClient *ethrpc.Client - sharedUpdater *shared.PoolsListUpdater -} - -func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsListUpdater { - sharedUpdater := shared.NewPoolsListUpdater(&shared.Config{ - DexID: config.DexID, - SubgraphAPI: config.SubgraphAPI, - SubgraphHeaders: config.SubgraphHeaders, - NewPoolLimit: config.NewPoolLimit, - PoolTypes: []string{poolTypeComposableStable}, - }) - - return &PoolsListUpdater{ - config: config, - ethrpcClient: ethrpcClient, - sharedUpdater: sharedUpdater, - } -} - -func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte) ([]entity.Pool, []byte, error) { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Infof("Start updating pools list ...") - defer func() { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Infof("Finish updating pools list.") - }() - - subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) - if err != nil { - return nil, nil, err - } - - vaults, err := u.getVaults(ctx, subgraphPools) - if err != nil { - return nil, nil, err - } - - bptIndexes, err := u.getBptIndex(ctx, subgraphPools) - if err != nil { - return nil, nil, err - } - - pools, err := u.initPools(ctx, subgraphPools, bptIndexes, vaults) - if err != nil { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Error(err.Error()) - - return nil, nil, err - } - - return pools, newMetadataBytes, nil -} - -func (u *PoolsListUpdater) getVaults(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]string, error) { - vaultAddresses := make([]common.Address, len(subgraphPools)) - vaults := make([]string, len(subgraphPools)) - - req := u.ethrpcClient.R() - for idx, subgraphPool := range subgraphPools { - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: subgraphPool.Address, - Method: poolMethodGetVault, - }, []interface{}{&vaultAddresses[idx]}) - } - if _, err := req.Aggregate(); err != nil { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Error(err.Error()) - return nil, err - } - - for idx, addr := range vaultAddresses { - vaults[idx] = strings.ToLower(addr.Hex()) - } - - return vaults, nil -} - -func (u *PoolsListUpdater) getBptIndex(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]*big.Int, error) { - bptIndexes := make([]*big.Int, len(subgraphPools)) - - req := u.ethrpcClient.R().SetContext(ctx) - for i, p := range subgraphPools { - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: p.Address, - Method: poolMethodGetBptIndex, - }, []interface{}{&bptIndexes[i]}) - } - - if _, err := req.Aggregate(); err != nil { - return nil, err - } - - return bptIndexes, nil -} - -func (u *PoolsListUpdater) initPools( - ctx context.Context, - subgraphPools []*shared.SubgraphPool, - bptIndexes []*big.Int, - vaults []string, -) ([]entity.Pool, error) { - pools := make([]entity.Pool, 0, len(subgraphPools)) - for idx := range subgraphPools { - pool, err := u.initPool(ctx, subgraphPools[idx], bptIndexes[idx], vaults[idx]) - if err != nil { - return nil, err - } - - pools = append(pools, pool) - } - - return pools, nil -} - -func (u *PoolsListUpdater) initPool( - ctx context.Context, - subgraphPool *shared.SubgraphPool, - bptIndex *big.Int, - vault string, -) (entity.Pool, error) { - var ( - poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) - reserves = make([]string, len(subgraphPool.Tokens)) - scalingFactors = make([]*uint256.Int, len(subgraphPool.Tokens)) - - err error - ) - - for j, token := range subgraphPool.Tokens { - poolTokens[j] = &entity.PoolToken{ - Address: strings.ToLower(token.Address), - Swappable: true, - } - - reserves[j] = "0" - - scalingFactors[j] = new(uint256.Int).Mul( - number.TenPow(18-uint8(token.Decimals)), - number.Number_1e18, - ) - } - - staticExtra := StaticExtra{ - PoolID: subgraphPool.ID, - PoolType: subgraphPool.PoolType, - PoolTypeVer: int(subgraphPool.PoolTypeVersion.Int64()), - BptIndex: int(bptIndex.Int64()), - ScalingFactors: scalingFactors, - Vault: vault, - } - staticExtraBytes, err := json.Marshal(staticExtra) - if err != nil { - return entity.Pool{}, err - } - - return entity.Pool{ - Address: strings.ToLower(subgraphPool.Address), - Exchange: u.config.DexID, - Type: DexType, - Timestamp: time.Now().Unix(), - Tokens: poolTokens, - Reserves: reserves, - StaticExtra: string(staticExtraBytes), - }, nil -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/regular_swap.go b/pkg/liquidity-source/balancer-v3/gyro/regular_swap.go deleted file mode 100644 index 7e154d96e..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/regular_swap.go +++ /dev/null @@ -1,206 +0,0 @@ -package composablestable - -import ( - "math/big" - - "github.com/holiman/uint256" - - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" - poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" -) - -type regularSimulator struct { - poolpkg.Pool - - bptIndex int - scalingFactors []*uint256.Int - amp *uint256.Int - swapFeePercentage *uint256.Int -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L184 -// It calls `super._swapGivenIn`, which is the code below: -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F11#L49 -func (s *regularSimulator) _swapGivenIn( - amountIn *uint256.Int, - balances []*uint256.Int, - indexIn int, - indexOut int, -) (*uint256.Int, *poolpkg.TokenAmount, *SwapInfo, error) { - feeAmount, err := math.FixedPoint.MulUp(amountIn, s.swapFeePercentage) - if err != nil { - return nil, nil, nil, err - } - amountInAfterFee, err := math.FixedPoint.Sub(amountIn, feeAmount) - if err != nil { - return nil, nil, nil, err - } - - balances, err = _upscaleArray(balances, s.scalingFactors) - if err != nil { - return nil, nil, nil, err - } - - upScaledAmountInAfterFee, err := _upscale(amountInAfterFee, s.scalingFactors[indexIn]) - if err != nil { - return nil, nil, nil, err - } - - upscaledAmountOut, err := s._onSwapGivenIn(upScaledAmountInAfterFee, balances, indexIn, indexOut) - if err != nil { - return nil, nil, nil, err - } - - amountOut, err := _downscaleDown(upscaledAmountOut, s.scalingFactors[indexOut]) - if err != nil { - return nil, nil, nil, err - } - - fee := poolpkg.TokenAmount{ - Token: s.Info.Tokens[indexIn], - Amount: feeAmount.ToBig(), - } - - return amountOut, &fee, &SwapInfo{}, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L215 -// It calls `super._swapGivenOut`, which is the code below: -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F11#L68 -func (s *regularSimulator) _swapGivenOut( - amountOut *uint256.Int, - balances []*uint256.Int, - indexIn int, - indexOut int, -) (*uint256.Int, *poolpkg.TokenAmount, *SwapInfo, error) { - balances, err := _upscaleArray(balances, s.scalingFactors) - if err != nil { - return nil, nil, nil, err - } - - upScaledAmountOut, err := _upscale(amountOut, s.scalingFactors[indexOut]) - if err != nil { - return nil, nil, nil, err - } - - upscaledAmountIn, err := s._onSwapGivenOut(upScaledAmountOut, balances, indexIn, indexOut) - if err != nil { - return nil, nil, nil, err - } - - amountIn, err := _downscaleUp(upscaledAmountIn, s.scalingFactors[indexIn]) - if err != nil { - return nil, nil, nil, err - } - - // Fees are added after scaling happens, to reduce the complexity of the rounding direction analysis. - amountInAfterFee, err := s._addSwapFeeAmount(amountIn) - if err != nil { - return nil, nil, nil, err - } - - feeAmount, err := math.FixedPoint.Sub(amountInAfterFee, amountIn) - if err != nil { - return nil, nil, nil, err - } - - fee := poolpkg.TokenAmount{ - Token: s.Info.Tokens[indexIn], - Amount: feeAmount.ToBig(), - } - - return amountIn, &fee, &SwapInfo{}, nil -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L229 -func (s *regularSimulator) _onSwapGivenIn( - amountIn *uint256.Int, - balances []*uint256.Int, - indexIn int, - indexOut int, -) (*uint256.Int, error) { - return s._onRegularSwap( - true, // given in - amountIn, - balances, - indexIn, - indexOut, - ) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L250 -func (s *regularSimulator) _onSwapGivenOut( - amountIn *uint256.Int, - balances []*uint256.Int, - indexIn int, - indexOut int, -) (*uint256.Int, error) { - return s._onRegularSwap( - false, // given out - amountIn, - balances, - indexIn, - indexOut, - ) -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F1#L270 -func (s *regularSimulator) _onRegularSwap( - isGivenIn bool, - amountGiven *uint256.Int, - registeredBalances []*uint256.Int, - registeredIndexIn int, - registeredIndexOut int, -) (*uint256.Int, error) { - balances := _dropBptItem(registeredBalances, s.bptIndex) - indexIn, indexOut := _skipBptIndex(registeredIndexIn, s.bptIndex), _skipBptIndex(registeredIndexOut, s.bptIndex) - - invariant, err := math.StableMath.CalculateInvariantV2(s.amp, balances) - if err != nil { - return nil, err - } - - if isGivenIn { - return math.StableMath.CalcOutGivenIn( - invariant, - s.amp, - amountGiven, - balances, - indexIn, - indexOut, - ) - } - - return math.StableMath.CalcInGivenOut( - invariant, - s.amp, - amountGiven, - balances, - indexIn, - indexOut, - ) -} - -func (s *regularSimulator) updateBalance(params poolpkg.UpdateBalanceParams) { - for idx, token := range s.Info.Tokens { - if token == params.TokenAmountIn.Token { - s.Info.Reserves[idx] = new(big.Int).Add( - s.Info.Reserves[idx], - params.TokenAmountIn.Amount, - ) - } - - if token == params.TokenAmountOut.Token { - s.Info.Reserves[idx] = new(big.Int).Sub( - s.Info.Reserves[idx], - params.TokenAmountOut.Amount, - ) - } - } -} - -// https://etherscan.io/address/0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b0#code#F22#L609 -func (s *regularSimulator) _addSwapFeeAmount(amount *uint256.Int) (*uint256.Int, error) { - // This returns amount + fee amount, so we round up (favoring a higher fee amount). - return math.FixedPoint.DivUp(amount, math.FixedPoint.Complement(s.swapFeePercentage)) -} diff --git a/pkg/liquidity-source/balancer-v3/gyro/type.go b/pkg/liquidity-source/balancer-v3/gyro/type.go deleted file mode 100644 index aaf1fb930..000000000 --- a/pkg/liquidity-source/balancer-v3/gyro/type.go +++ /dev/null @@ -1,107 +0,0 @@ -package composablestable - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/holiman/uint256" -) - -type PoolMetaInfo struct { - Vault string `json:"vault"` - PoolID string `json:"poolId"` - TokenOutIndex int `json:"tokenOutIndex"` - BlockNumber uint64 `json:"blockNumber"` -} - -type SwapInfo struct { - LastJoinExitData LastJoinExitData `json:"-"` -} - -type LastJoinExitData struct { - LastJoinExitAmplification *uint256.Int `json:"lastJoinExitAmplification"` - LastPostJoinExitInvariant *uint256.Int `json:"lastPostJoinExitInvariant"` -} - -type TokenRateCache struct { - Rate *uint256.Int `json:"rate"` - OldRate *uint256.Int `json:"oldRate"` - Duration *uint256.Int `json:"duration"` - Expires *uint256.Int `json:"expires"` -} - -type Gas struct { - Swap int64 -} - -type Extra struct { - CanNotUpdateTokenRates bool `json:"canNotUpdateTokenRates"` - ScalingFactors []*uint256.Int `json:"scalingFactors"` - BptTotalSupply *uint256.Int `json:"bptTotalSupply"` - Amp *uint256.Int `json:"amp"` - LastJoinExit LastJoinExitData `json:"lastJoinExit"` - RateProviders []string `json:"rateProviders"` - TokenRateCaches []TokenRateCache `json:"tokenRateCaches"` - SwapFeePercentage *uint256.Int `json:"swapFeePercentage"` - ProtocolFeePercentageCache map[int]*uint256.Int `json:"protocolFeePercentageCache"` - IsTokenExemptFromYieldProtocolFee []bool `json:"isTokenExemptFromYieldProtocolFee"` - IsExemptFromYieldProtocolFee bool `json:"isExemptFromYieldProtocolFee"` - InRecoveryMode bool `json:"inRecoveryMode"` - Paused bool `json:"paused"` -} - -type StaticExtra struct { - PoolID string `json:"poolId"` - PoolType string `json:"poolType"` - PoolTypeVer int `json:"poolTypeVer"` - BptIndex int `json:"bptIndex"` - ScalingFactors []*uint256.Int `json:"scalingFactors"` - Vault string `json:"vault"` -} - -type AmplificationParameterResp struct { - Value *big.Int - IsUpdating bool - Precision *big.Int -} - -type LastJoinExitResp struct { - LastJoinExitAmplification *big.Int - LastPostJoinExitInvariant *big.Int -} - -type TokenRateCacheResp struct { - Rate *big.Int - OldRate *big.Int - Duration *big.Int - Expires *big.Int -} - -type PoolTokensResp struct { - Tokens []common.Address - Balances []*big.Int - LastChangeBlock *big.Int -} - -type PausedStateResp struct { - Paused bool - PauseWindowEndTime *big.Int - BufferPeriodEndTime *big.Int -} - -type rpcRes struct { - CanNotUpdateTokenRates bool - PoolTokens PoolTokensResp - BptTotalSupply *big.Int - Amp *big.Int - LastJoinExit LastJoinExitResp - RateProviders []common.Address - TokenRateCaches []TokenRateCacheResp - SwapFeePercentage *big.Int - ProtocolFeePercentageCache map[int]*big.Int - IsTokenExemptFromYieldProtocolFee []bool - IsExemptFromYieldProtocolFee bool - InRecoveryMode bool - PausedState PausedStateResp - BlockNumber uint64 -} diff --git a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go index 69bc83dd1..7371ef00f 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go +++ b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go @@ -33,28 +33,28 @@ func (h *directionalFeeHook) OnComputeDynamicSwapFeePercentage(param shared.Pool } func (h *directionalFeeHook) calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, swapAmount *uint256.Int) (*uint256.Int, error) { - finalBalanceTokenIn, err := math.Add(balanceIn, swapAmount) + finalBalanceTokenIn, err := math.FixPoint.Add(balanceIn, swapAmount) if err != nil { return nil, err } - finalBalanceTokenOut, err := math.Sub(balanceOut, swapAmount) + finalBalanceTokenOut, err := math.FixPoint.Sub(balanceOut, swapAmount) if err != nil { return nil, err } if finalBalanceTokenIn.Gt(finalBalanceTokenOut) { - diff, err := math.Sub(finalBalanceTokenIn, finalBalanceTokenOut) + diff, err := math.FixPoint.Sub(finalBalanceTokenIn, finalBalanceTokenOut) if err != nil { return nil, err } - totalLiquidity, err := math.Add(finalBalanceTokenIn, finalBalanceTokenOut) + totalLiquidity, err := math.FixPoint.Add(finalBalanceTokenIn, finalBalanceTokenOut) if err != nil { return nil, err } - diff, err = math.DivDown(diff, totalLiquidity) + diff, err = math.FixPoint.DivDown(diff, totalLiquidity) return diff, err } diff --git a/pkg/liquidity-source/balancer-v3/hooks/ihook.go b/pkg/liquidity-source/balancer-v3/hooks/ihook.go index 21d7819a4..39e027e05 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/ihook.go +++ b/pkg/liquidity-source/balancer-v3/hooks/ihook.go @@ -10,3 +10,16 @@ type IHook interface { OnAfterSwap(param shared.AfterSwapParams) (bool, *uint256.Int, error) OnComputeDynamicSwapFeePercentage(param shared.PoolSwapParams) (bool, *uint256.Int, error) } + +// Define a map of supported hooks +var hooksMap = map[string]bool{ + "DirectionalFee": true, + "FeeTaking": true, + "StableSurge": true, + "VeBALFeeDiscount": true, +} + +func IsHookSupported(hook string) bool { + _, exists := hooksMap[hook] + return exists +} diff --git a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go index 3ae6e95a0..66a72fe3d 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go +++ b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go @@ -1,10 +1,5 @@ package hooks -import ( - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" - "github.com/holiman/uint256" -) - type StableSurgeHook struct { BaseHook } @@ -12,16 +7,3 @@ type StableSurgeHook struct { func NewStableSurgeHook() *StableSurgeHook { return &StableSurgeHook{} } - -func (h *StableSurgeHook) OnComputeDynamicSwapFeePercentage( - staticSwapFeePercentage, - amountGivenScaled18, - balanceIn, - balanceOut *uint256.Int, -) (bool, *uint256.Int, error) { - return false, staticSwapFeePercentage, nil -} - -func (h *StableSurgeHook) getSurgeFeePercentage(params shared.VaultSwapParams) (*uint256.Int, error) { - return nil, nil -} diff --git a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go index 8adcbe5f3..e491a3a2a 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go +++ b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go @@ -1,10 +1,5 @@ package hooks -import ( - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" - "github.com/holiman/uint256" -) - type VeBALFeeDiscountHook struct { BaseHook } @@ -12,50 +7,3 @@ type VeBALFeeDiscountHook struct { func NewVeBALFeeDiscountHook() *VeBALFeeDiscountHook { return &VeBALFeeDiscountHook{} } - -func (h *VeBALFeeDiscountHook) OnComputeDynamicSwapFeePercentage( - staticSwapFeePercentage, - amountGivenScaled18, - balanceIn, - balanceOut *uint256.Int, -) (bool, *uint256.Int, error) { - calculatedSwapFeePercentage, err := h.calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, amountGivenScaled18) - if err != nil { - return false, nil, err - } - - if calculatedSwapFeePercentage.Gt(staticSwapFeePercentage) { - return true, calculatedSwapFeePercentage, nil - } - - return false, staticSwapFeePercentage, nil -} - -func (h *VeBALFeeDiscountHook) calculatedExpectedSwapFeePercentage(balanceIn, balanceOut, swapAmount *uint256.Int) (*uint256.Int, error) { - finalBalanceTokenIn, err := math.Add(balanceIn, swapAmount) - if err != nil { - return nil, err - } - - finalBalanceTokenOut, err := math.Sub(balanceOut, swapAmount) - if err != nil { - return nil, err - } - - if finalBalanceTokenIn.Gt(finalBalanceTokenOut) { - diff, err := math.Sub(finalBalanceTokenIn, finalBalanceTokenOut) - if err != nil { - return nil, err - } - - totalLiquidity, err := math.Add(finalBalanceTokenIn, finalBalanceTokenOut) - if err != nil { - return nil, err - } - - diff, err = math.DivDown(diff, totalLiquidity) - return diff, err - } - - return math.ZERO, nil -} diff --git a/pkg/liquidity-source/balancer-v3/math/constant.go b/pkg/liquidity-source/balancer-v3/math/const.go similarity index 80% rename from pkg/liquidity-source/balancer-v3/math/constant.go rename to pkg/liquidity-source/balancer-v3/math/const.go index 229780742..f507bd42c 100644 --- a/pkg/liquidity-source/balancer-v3/math/constant.go +++ b/pkg/liquidity-source/balancer-v3/math/const.go @@ -6,6 +6,4 @@ var ( ZERO = uint256.NewInt(0) ONE = uint256.NewInt(1) TWO = uint256.NewInt(2) - - ONE_E18 = uint256.NewInt(1e18) ) diff --git a/pkg/liquidity-source/balancer-v3/math/error.go b/pkg/liquidity-source/balancer-v3/math/error.go deleted file mode 100644 index c58c3d8ad..000000000 --- a/pkg/liquidity-source/balancer-v3/math/error.go +++ /dev/null @@ -1,11 +0,0 @@ -package math - -import "errors" - -var ( - ErrAddOverflow = errors.New("ADD_OVERFLOW") - ErrSubOverflow = errors.New("SUB_OVERFLOW") - ErrZeroDivision = errors.New("ZERO_DIVISION") - ErrDivInternal = errors.New("DIV_INTERNAL") - ErrMulOverflow = errors.New("MUL_OVERFLOW") -) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go new file mode 100644 index 000000000..35cc5d7c6 --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go @@ -0,0 +1,165 @@ +package math + +import ( + "errors" + + "github.com/holiman/uint256" +) + +var ( + ErrAddOverflow = errors.New("ADD_OVERFLOW") + ErrSubOverflow = errors.New("SUB_OVERFLOW") + ErrZeroDivision = errors.New("ZERO_DIVISION") + ErrDivInternal = errors.New("DIV_INTERNAL") + ErrMulOverflow = errors.New("MUL_OVERFLOW") + + ONE_E18 = uint256.NewInt(1e18) // 18 decimal places + TWO_E18 = new(uint256.Int).Mul(ONE_E18, TWO) + FOUR_E18 = new(uint256.Int).Mul(TWO_E18, TWO) + MAX_POW_RELATIVE_ERROR = uint256.NewInt(10000) // 10^(-14) +) + +var FixPoint *fixPoint + +type fixPoint struct{} + +func init() { + FixPoint = &fixPoint{} +} + +func (f *fixPoint) MulDivUp(a, b, c *uint256.Int) (*uint256.Int, error) { + if c.IsZero() { + return nil, ErrZeroDivision + } + + product, err := f.Mul(a, b) + if err != nil { + return nil, err + } + + // result = a == 0 ? 0 : (a * b - 1) / c + 1 + if product.IsZero() { + return ZERO, nil + } + + product.Sub(product, ONE) + product.Div(product, c) + product.Add(product, ONE) + + return product, nil +} + +func (f *fixPoint) MulUp(a, b *uint256.Int) (*uint256.Int, error) { + product, err := f.Mul(a, b) + if err != nil { + return nil, ErrMulOverflow + } + + // result = product == 0 ? 0 : ((product - 1) / FixedPoint.ONE) + 1 + if product.IsZero() { + return ZERO, nil + } + + product.Sub(product, ONE) + product.Div(product, ONE_E18) + product.Add(product, ONE) + + return product, nil +} + +func (f *fixPoint) MulDown(a, b *uint256.Int) (*uint256.Int, error) { + product, err := f.Mul(a, b) + if err != nil { + return nil, ErrMulOverflow + } + + return product.Div(product, ONE_E18), nil +} + +func (f *fixPoint) DivUp(a, b *uint256.Int) (*uint256.Int, error) { + return f.MulDivUp(a, ONE_E18, b) +} + +func (f *fixPoint) DivDown(a, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + + aInflated, err := f.Mul(a, ONE_E18) + if err != nil { + return nil, err + } + + return aInflated.Div(aInflated, b), nil +} + +func (f *fixPoint) PowUp(x, y *uint256.Int) (*uint256.Int, error) { + if y.Eq(ONE_E18) { + return x, nil + } + + if y.Eq(TWO_E18) { + return f.MulUp(x, x) + } + + if y.Eq(FOUR_E18) { + square, err := f.MulUp(x, x) + if err != nil { + return nil, err + } + + return f.MulUp(square, square) + } + + raw, err := Pow(x, y) + if err != nil { + return nil, err + } + + var maxError *uint256.Int + maxError, err = f.MulUp(raw, MAX_POW_RELATIVE_ERROR) + if err != nil { + return nil, err + } + + maxError, err = f.Add(maxError, ONE) + if err != nil { + return nil, err + } + + return f.Add(raw, maxError) +} + +func (f *fixPoint) Complement(x *uint256.Int) *uint256.Int { + // result = (x < ONE) ? (ONE - x) : 0 + result := new(uint256.Int).Set(ZERO) + if x.Lt(ONE_E18) { + result.Sub(ONE_E18, x) + } + + return result +} + +func (f *fixPoint) Add(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c, overflow := new(uint256.Int).AddOverflow(a, b) + if overflow { + return nil, ErrAddOverflow + } + return c, nil +} + +func (f *fixPoint) Sub(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c, overflow := new(uint256.Int).SubOverflow(a, b) + if overflow { + return nil, ErrSubOverflow + } + return c, nil +} + +func (f *fixPoint) Mul(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { + c, overflow := new(uint256.Int).MulOverflow(a, b) + if overflow { + return nil, ErrMulOverflow + } + return c, nil +} diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go new file mode 100644 index 000000000..82e86dd6a --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go @@ -0,0 +1,120 @@ +package math + +import ( + "errors" + + "github.com/KyberNetwork/blockchain-toolkit/i256" + "github.com/KyberNetwork/int256" + "github.com/holiman/uint256" +) + +var ( + ONE_E20, _ = uint256.FromDecimal("100000000000000000000") // 1e20 + + iONE_E17 = int256.NewInt(1e17) + iONE_E18 = int256.NewInt(1e18) + + TWO_254 = new(uint256.Int).Lsh(ONE, 254) + MILD_EXPONENT_BOUND = new(uint256.Int).Div(TWO_254, ONE_E20) + + LN_36_LOWER_BOUND = new(int256.Int).Sub(iONE_E18, iONE_E17) + LN_36_UPPER_BOUND = new(int256.Int).Add(iONE_E18, iONE_E17) + + MAX_NATURAL_EXPONENT = new(int256.Int).Mul(int256.NewInt(130), iONE_E18) // 130e18 + MIN_NATURAL_EXPONENT = new(int256.Int).Mul(int256.NewInt(-41), iONE_E18) // -41e18 + + ErrBaseOutOfBounds = errors.New("Base_OutOfBounds") + ErrExponentOutOfBounds = errors.New("Exponent_OutOfBounds") + ErrProductOutOfBounds = errors.New("Product_OutOfBounds") +) + +func Pow(x, y *uint256.Int) (*uint256.Int, error) { + if y.IsZero() { + return ONE_E18, nil + } + + if x.IsZero() { + return ZERO, nil + } + + xRight255 := new(uint256.Int).Rsh(x, 255) + if !xRight255.IsZero() { + return nil, ErrBaseOutOfBounds + } + + if y.Cmp(MILD_EXPONENT_BOUND) >= 0 { + return nil, ErrExponentOutOfBounds + } + + x_int256 := i256.SafeToInt256(x) + y_int256 := i256.SafeToInt256(y) + + var ( + logx_times_y *int256.Int + overflow bool + ) + + if LN_36_LOWER_BOUND.Lt(x_int256) && x_int256.Lt(LN_36_UPPER_BOUND) { + ln_36_x, err := Ln36(x_int256) + if err != nil { + return nil, err + } + + // logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18) + quotient := new(int256.Int).Quo(ln_36_x, iONE_E18) + remainder := new(int256.Int).Rem(ln_36_x, iONE_E18) + + // (ln36X / ONE_18) * y_int256 + term1, overflow := new(int256.Int).MulOverflow(quotient, y_int256) + if overflow { + return nil, ErrMulOverflow + } + + // ((ln36X % ONE_18) * y_int256) / ONE_18 + term2, overflow := new(int256.Int).MulOverflow(remainder, y_int256) + if overflow { + return nil, ErrMulOverflow + } + term2 = term2.Quo(term2, iONE_E18) + + logx_times_y, overflow = logx_times_y.AddOverflow(term1, term2) + if overflow { + return nil, ErrAddOverflow + } + } else { + ln_x, err := Ln(x_int256) + if err != nil { + return nil, err + } + + logx_times_y, overflow = logx_times_y.MulOverflow(ln_x, y_int256) + if overflow { + return nil, ErrMulOverflow + } + } + + logx_times_y = logx_times_y.Quo(logx_times_y, iONE_E18) + + if !(MIN_NATURAL_EXPONENT.Cmp(logx_times_y) <= 0 && logx_times_y.Cmp(MAX_NATURAL_EXPONENT) <= 0) { + return nil, ErrProductOutOfBounds + } + + exp_logx_times_y, err := Exp(logx_times_y) + if err != nil { + return nil, err + } + + return i256.SafeConvertToUInt256(exp_logx_times_y), nil +} + +func Ln36(x *int256.Int) (*int256.Int, error) { + return nil, nil +} + +func Ln(x *int256.Int) (*int256.Int, error) { + return nil, nil +} + +func Exp(x *int256.Int) (*int256.Int, error) { + return nil, nil +} diff --git a/pkg/liquidity-source/balancer-v3/math/math.go b/pkg/liquidity-source/balancer-v3/math/math.go deleted file mode 100644 index 6e4f56ebc..000000000 --- a/pkg/liquidity-source/balancer-v3/math/math.go +++ /dev/null @@ -1,108 +0,0 @@ -package math - -import ( - "github.com/holiman/uint256" -) - -func MulDivUp(a, b, c *uint256.Int) (*uint256.Int, error) { - if c.IsZero() { - return nil, ErrZeroDivision - } - - product, err := Mul(a, b) - if err != nil { - return nil, err - } - - // Equivalent to: - // result = a == 0 ? 0 : (a * b - 1) / c + 1 - if product.IsZero() { - return ZERO, nil - } - - product.Sub(product, ONE) - product.Div(product, c) - product.Add(product, ONE) - - return product, nil -} - -func MulUp(a, b *uint256.Int) (*uint256.Int, error) { - product, err := Mul(a, b) - if err != nil { - return nil, ErrMulOverflow - } - - // Equivalent to: - // result = product == 0 ? 0 : ((product - 1) / FixedPoint.ONE) + 1 - if product.IsZero() { - return ZERO, nil - } - - product.Sub(product, ONE) - product.Div(product, ONE_E18) - product.Add(product, ONE) - - return product, nil -} - -func MulDown(a, b *uint256.Int) (*uint256.Int, error) { - product, err := Mul(a, b) - if err != nil { - return nil, ErrMulOverflow - } - - return product.Div(product, ONE_E18), nil -} - -func DivUp(a, b *uint256.Int) (*uint256.Int, error) { - return MulDivUp(a, ONE_E18, b) -} - -func DivDown(a, b *uint256.Int) (*uint256.Int, error) { - if b.IsZero() { - return nil, ErrZeroDivision - } - - aInflated, err := Mul(a, ONE_E18) - if err != nil { - return nil, err - } - - return aInflated.Div(aInflated, b), nil -} - -func Complement(x *uint256.Int) *uint256.Int { - // Equivalent to: - // result = (x < ONE) ? (ONE - x) : 0 - result := new(uint256.Int).Set(ZERO) - if x.Lt(ONE_E18) { - result.Sub(ONE_E18, x) - } - - return result -} - -func Add(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - c, overflow := new(uint256.Int).AddOverflow(a, b) - if overflow { - return nil, ErrAddOverflow - } - return c, nil -} - -func Sub(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - c, overflow := new(uint256.Int).SubOverflow(a, b) - if overflow { - return nil, ErrSubOverflow - } - return c, nil -} - -func Mul(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) { - c, overflow := new(uint256.Int).MulOverflow(a, b) - if overflow { - return nil, ErrMulOverflow - } - return c, nil -} diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go index f663a5eca..a356ccbcf 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -48,7 +48,7 @@ func (s *stableMath) ComputeOutGivenExactIn( balances[tokenIndexIn].Sub(balances[tokenIndexIn], tokenAmountIn) - amountOut, err := Sub(balances[tokenIndexOut], finalBalanceOut) + amountOut, err := FixPoint.Sub(balances[tokenIndexOut], finalBalanceOut) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (s *stableMath) ComputeInGivenExactOut( balances[tokenIndexOut].Add(balances[tokenIndexOut], tokenAmountOut) - amountOut, err := Sub(finalBalanceIn, balances[tokenIndexIn]) + amountOut, err := FixPoint.Sub(finalBalanceIn, balances[tokenIndexIn]) if err != nil { return nil, err } diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math.go b/pkg/liquidity-source/balancer-v3/math/weighted_math.go index 5eaca1375..c6a15aae2 100644 --- a/pkg/liquidity-source/balancer-v3/math/weighted_math.go +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math.go @@ -10,191 +10,117 @@ var ( ErrMaxInRatio = errors.New("MAX_IN_RATIO") ErrMaxOutRatio = errors.New("MAX_OUT_RATIO") - _MAX_IN_RATIO = uint256.NewInt(0.3e18) - _MAX_OUT_RATIO = uint256.NewInt(0.3e18) + MAX_IN_RATIO = uint256.NewInt(30e16) + MAX_OUT_RATIO = uint256.NewInt(30e16) ) var WeightedMath *weightedMath -type weightedMath struct { -} +type weightedMath struct{} func init() { WeightedMath = &weightedMath{} } -// // https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L78 -// func (l *weightedMath) CalcOutGivenIn( -// balanceIn *uint256.Int, -// weightIn *uint256.Int, -// balanceOut *uint256.Int, -// weightOut *uint256.Int, -// amountIn *uint256.Int, -// ) (*uint256.Int, error) { -// maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) -// if err != nil { -// return nil, err -// } - -// if amountIn.Gt(maxIn) { -// return nil, ErrMaxInRatio -// } - -// denominator, err := FixedPoint.Add(balanceIn, amountIn) -// if err != nil { -// return nil, err -// } - -// base, err := FixedPoint.DivUp(balanceIn, denominator) -// if err != nil { -// return nil, err -// } - -// exponent, err := FixedPoint.DivDown(weightIn, weightOut) -// if err != nil { -// return nil, err -// } - -// power, err := FixedPoint.PowUp(base, exponent) -// if err != nil { -// return nil, err -// } - -// return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) -// } - -// // https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L69 -// func (l *weightedMath) CalcOutGivenInV1( -// balanceIn *uint256.Int, -// weightIn *uint256.Int, -// balanceOut *uint256.Int, -// weightOut *uint256.Int, -// amountIn *uint256.Int, -// ) (*uint256.Int, error) { -// maxIn, err := FixedPoint.MulDown(balanceIn, _MAX_IN_RATIO) -// if err != nil { -// return nil, err -// } - -// if amountIn.Gt(maxIn) { -// return nil, ErrMaxInRatio -// } - -// denominator, err := FixedPoint.Add(balanceIn, amountIn) -// if err != nil { -// return nil, err -// } - -// base, err := FixedPoint.DivUp(balanceIn, denominator) -// if err != nil { -// return nil, err -// } - -// exponent, err := FixedPoint.DivDown(weightIn, weightOut) -// if err != nil { -// return nil, err -// } - -// power, err := FixedPoint.PowUpV1(base, exponent) -// if err != nil { -// return nil, err -// } - -// return FixedPoint.MulDown(balanceOut, FixedPoint.Complement(power)) -// } - -// // https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F9#L113 -// func (l *weightedMath) CalcInGivenOut( -// balanceIn *uint256.Int, -// weightIn *uint256.Int, -// balanceOut *uint256.Int, -// weightOut *uint256.Int, -// amountOut *uint256.Int, -// ) (*uint256.Int, error) { -// maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) -// if err != nil { -// return nil, err -// } - -// // Cannot exceed maximum out ratio -// if amountOut.Gt(maxOut) { -// return nil, ErrMaxOutRatio -// } - -// remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) -// if err != nil { -// return nil, err -// } - -// base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) -// if err != nil { -// return nil, err -// } - -// exponent, err := FixedPoint.DivUp(weightOut, weightIn) -// if err != nil { -// return nil, err -// } - -// power, err := FixedPoint.PowUp(base, exponent) -// if err != nil { -// return nil, err -// } - -// // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so -// // the following subtraction should never revert. -// ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) -// if err != nil { -// return nil, err -// } - -// return FixedPoint.MulUp(balanceIn, ratio) -// } - -// // https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F25#L104 -// func (l *weightedMath) CalcInGivenOutV1( -// balanceIn *uint256.Int, -// weightIn *uint256.Int, -// balanceOut *uint256.Int, -// weightOut *uint256.Int, -// amountOut *uint256.Int, -// ) (*uint256.Int, error) { -// maxOut, err := FixedPoint.MulDown(balanceOut, _MAX_OUT_RATIO) -// if err != nil { -// return nil, err -// } - -// // Cannot exceed maximum out ratio -// if amountOut.Gt(maxOut) { -// return nil, ErrMaxOutRatio -// } - -// remainingBalanceOut, err := FixedPoint.Sub(balanceOut, amountOut) -// if err != nil { -// return nil, err -// } - -// base, err := FixedPoint.DivUp(balanceOut, remainingBalanceOut) -// if err != nil { -// return nil, err -// } - -// exponent, err := FixedPoint.DivUp(weightOut, weightIn) -// if err != nil { -// return nil, err -// } - -// power, err := FixedPoint.PowUpV1(base, exponent) -// if err != nil { -// return nil, err -// } - -// // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so -// // the following subtraction should never revert. -// ratio, err := FixedPoint.Sub(power, FixedPoint.ONE) -// if err != nil { -// return nil, err -// } - -// return FixedPoint.MulUp(balanceIn, ratio) -// } +func (s *weightedMath) ComputeOutGivenExactIn( + balanceIn, + weightIn, + balanceOut, + weightOut, + amountIn *uint256.Int, +) (*uint256.Int, error) { + /********************************************************************************************** + // inGivenExactOut // + // aO = amountOut // + // bO = balanceOut // + // bI = balanceIn / / bO \ (wO / wI) \ // + // aI = amountIn aI = bI * | | -------------------------- | ^ - 1 | // + // wI = weightIn \ \ ( bO - aO ) / / // + // wO = weightOut // + **********************************************************************************************/ + + balanceInApplyRate, err := FixPoint.MulDown(amountIn, MAX_IN_RATIO) + if err != nil { + return nil, err + } + + if amountIn.Gt(balanceInApplyRate) { + return nil, ErrMaxInRatio + } + + denominator, err := FixPoint.Add(balanceIn, amountIn) + if err != nil { + return nil, err + } + + base, err := FixPoint.DivUp(balanceIn, denominator) + if err != nil { + return nil, err + } + + exponent, err := FixPoint.DivDown(weightIn, weightOut) + if err != nil { + return nil, err + } + + power, err := FixPoint.PowUp(base, exponent) + if err != nil { + return nil, err + } + + return FixPoint.MulDown(balanceOut, FixPoint.Complement(power)) +} + +func (s *weightedMath) ComputeInGivenExactOut( + balanceIn, + weightIn, + balanceOut, + weightOut, + amountOut *uint256.Int, +) (*uint256.Int, error) { + /********************************************************************************************** + // outGivenExactIn // + // aO = amountOut // + // bO = balanceOut // + // bI = balanceIn / / bI \ (wI / wO) \ // + // aI = amountIn aO = bO * | 1 - | -------------------------- | ^ | // + // wI = weightIn \ \ ( bI + aI ) / / // + // wO = weightOut // + **********************************************************************************************/ + + balanceOutApplyRate, err := FixPoint.MulDown(amountOut, MAX_OUT_RATIO) + if err != nil { + return nil, err + } + + if amountOut.Gt(balanceOutApplyRate) { + return nil, ErrMaxOutRatio + } + + delta, err := FixPoint.Sub(balanceOut, amountOut) + if err != nil { + return nil, err + } + + base, err := FixPoint.DivUp(balanceOut, delta) + if err != nil { + return nil, err + } + + exponent, err := FixPoint.DivUp(weightOut, weightIn) + if err != nil { + return nil, err + } + + power, err := FixPoint.PowUp(base, exponent) + if err != nil { + return nil, err + } + + ratio, err := FixPoint.Sub(power, ONE_E18) + if err != nil { + return nil, err + } + + return FixPoint.MulUp(balanceIn, ratio) +} diff --git a/pkg/liquidity-source/balancer-v3/shared/error.go b/pkg/liquidity-source/balancer-v3/shared/error.go index a423933e2..3c62411a0 100644 --- a/pkg/liquidity-source/balancer-v3/shared/error.go +++ b/pkg/liquidity-source/balancer-v3/shared/error.go @@ -3,6 +3,13 @@ package shared import "errors" var ( + ErrTokenNotRegistered = errors.New("TOKEN_NOT_REGISTERED") + ErrInvalidReserve = errors.New("invalid reserve") + ErrInvalidAmountIn = errors.New("invalid amount in") + ErrInvalidAmountOut = errors.New("invalid amount out") + ErrInvalidPoolType = errors.New("invalid pool type") + ErrInvalidPoolID = errors.New("invalid pool id") + ErrTradeAmountTooSmall = errors.New("trade amount is too small") ErrProtocolFeesExceedTotalCollected = errors.New("protocolFees exceed totalCollected") ErrVaultIsPaused = errors.New("vault is paused") diff --git a/pkg/liquidity-source/balancer-v3/shared/type.go b/pkg/liquidity-source/balancer-v3/shared/type.go index 8d98c744d..15edf5df4 100644 --- a/pkg/liquidity-source/balancer-v3/shared/type.go +++ b/pkg/liquidity-source/balancer-v3/shared/type.go @@ -7,6 +7,10 @@ import ( "github.com/holiman/uint256" ) +type Gas struct { + Swap int64 +} + type AggregateFeePercentage struct { AggregateSwapFeePercentage *big.Int AggregateYieldFeePercentage *big.Int diff --git a/pkg/liquidity-source/balancer-v3/stable/constant.go b/pkg/liquidity-source/balancer-v3/stable/constant.go index 90ad454ba..12c3e2590 100644 --- a/pkg/liquidity-source/balancer-v3/stable/constant.go +++ b/pkg/liquidity-source/balancer-v3/stable/constant.go @@ -1,5 +1,7 @@ package stable +import "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + const ( DexType = "balancer-v3-stable" @@ -9,5 +11,5 @@ const ( ) var ( - defaultGas = Gas{Swap: 80000} + defaultGas = shared.Gas{Swap: 80000} ) diff --git a/pkg/liquidity-source/balancer-v3/stable/error.go b/pkg/liquidity-source/balancer-v3/stable/error.go deleted file mode 100644 index f41960d1b..000000000 --- a/pkg/liquidity-source/balancer-v3/stable/error.go +++ /dev/null @@ -1,12 +0,0 @@ -package stable - -import "errors" - -var ( - ErrTokenNotRegistered = errors.New("TOKEN_NOT_REGISTERED") - ErrInvalidReserve = errors.New("invalid reserve") - ErrInvalidAmountIn = errors.New("invalid amount in") - ErrInvalidAmountOut = errors.New("invalid amount out") - ErrInvalidPoolType = errors.New("invalid pool type") - ErrInvalidPoolID = errors.New("invalid pool id") -) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index a1f5dba62..f69707c47 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -37,9 +37,6 @@ type PoolSimulator struct { isPoolInRecoveryMode bool vaultAddress string - - poolType string - poolVersion int } func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { @@ -64,7 +61,7 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) } - // Need to detect the current hook of pool + // Need to detect the current hook type of pool hook := hooks.NewBaseHook() vault := vault.New(hook, extra.HooksConfig, extra.IsPoolInRecoveryMode, extra.DecimalScalingFactors, extra.TokenRates, @@ -89,7 +86,6 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { hooksConfig: extra.HooksConfig, currentAmp: extra.AmplificationParameter, vaultAddress: staticExtra.Vault, - poolType: staticExtra.PoolType, }, nil } @@ -106,12 +102,12 @@ func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool indexIn, indexOut := p.GetTokenIndex(tokenAmountIn.Token), p.GetTokenIndex(tokenOut) if indexIn < 0 || indexOut < 0 { - return nil, ErrTokenNotRegistered + return nil, shared.ErrTokenNotRegistered } amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) if overflow { - return nil, ErrInvalidAmountIn + return nil, shared.ErrInvalidAmountIn } amountOut, totalSwapFee, aggregateFee, err := p.vault.Swap(shared.VaultSwapParams{ @@ -153,12 +149,12 @@ func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk indexIn, indexOut := p.GetTokenIndex(tokenIn), p.GetTokenIndex(tokenAmountOut.Token) if indexIn < 0 || indexOut < 0 { - return nil, ErrTokenNotRegistered + return nil, shared.ErrTokenNotRegistered } amountOut, overflow := uint256.FromBig(tokenAmountOut.Amount) if overflow { - return nil, ErrInvalidAmountOut + return nil, shared.ErrInvalidAmountOut } amountIn, totalSwapFee, aggregateSwapFee, err := p.vault.Swap(shared.VaultSwapParams{ @@ -225,8 +221,6 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { return PoolMetaInfo{ Vault: s.vaultAddress, - PoolType: s.poolType, - PoolVersion: s.poolVersion, TokenOutIndex: s.GetTokenIndex(tokenOut), BlockNumber: s.Info.BlockNumber, } @@ -280,18 +274,7 @@ func (p *PoolSimulator) computeInvariant(balancesLiveScaled18 []*uint256.Int, ro func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { cloned := *p - // cloned.swapFeePercentage = p.swapFeePercentage.Clone() - // cloned.aggregateSwapFeePercentage = p.aggregateSwapFeePercentage.Clone() - // cloned.amplificationParameter = p.amplificationParameter.Clone() - // cloned.balancesLiveScaled18 = lo.Map(p.balancesLiveScaled18, func(v *uint256.Int, _ int) *uint256.Int { - // return new(uint256.Int).Set(v) - // }) - // cloned.decimalScalingFactors = lo.Map(p.decimalScalingFactors, func(v *uint256.Int, _ int) *uint256.Int { - // return new(uint256.Int).Set(v) - // }) - // cloned.tokenRates = lo.Map(p.tokenRates, func(v *uint256.Int, _ int) *uint256.Int { - // return new(uint256.Int).Set(v) - // }) + cloned.vault = p.vault.CloneState() return &cloned } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index 9500c844d..cf0614084 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -46,7 +46,6 @@ func TestCalcAmountOut(t *testing.T) { uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), currentAmp: uint256.NewInt(1000000), - poolType: PoolType, } tokenAmountIn := poolpkg.TokenAmount{ @@ -90,7 +89,6 @@ func TestCalcAmountOut(t *testing.T) { uint256.NewInt(500000000000000000), ), currentAmp: uint256.NewInt(1000000), - poolType: PoolType, } tokenAmountIn := poolpkg.TokenAmount{ @@ -143,9 +141,6 @@ func TestCalcAmountOut(t *testing.T) { uint256.NewInt(500000000000000000), ), currentAmp: uint256.NewInt(1000000), - - poolType: PoolType, - poolVersion: 1, } tokenAmountIn := poolpkg.TokenAmount{ diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index f26f0a174..24fbb4189 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -220,6 +220,14 @@ func (t *PoolTracker) queryRPC( return nil, err } + if hooksConfig.Data.HooksContract != (common.Address{}) { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": poolAddress, + }).Warnf("this pool has a contract hook implemented at %s => should check it", hooksConfig.Data.HooksContract) + } + return &RpcResult{ HooksConfig: shared.HooksConfig{ EnableHookAdjustedAmounts: hooksConfig.Data.EnableHookAdjustedAmounts, diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index dc7802229..ec2bd0dc3 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -88,8 +88,7 @@ func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]en if strings.EqualFold(PoolType, poolType) { staticExtraBytes, err := json.Marshal(&StaticExtra{ - PoolType: PoolType, - Vault: subgraphPool.Vault.ID, + Vault: subgraphPool.Vault.ID, }) if err != nil { return nil, err diff --git a/pkg/liquidity-source/balancer-v3/stable/type.go b/pkg/liquidity-source/balancer-v3/stable/type.go index bbd187087..738736731 100644 --- a/pkg/liquidity-source/balancer-v3/stable/type.go +++ b/pkg/liquidity-source/balancer-v3/stable/type.go @@ -4,19 +4,14 @@ import ( "math/big" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" - "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" ) -type Gas struct { - Swap int64 -} - type Extra struct { HooksConfig shared.HooksConfig `json:"hooksConfig"` - AmplificationParameter *uint256.Int `json:"amplificationParameter"` StaticSwapFeePercentage *uint256.Int `json:"staticSwapFeePercentage"` AggregateSwapFeePercentage *uint256.Int `json:"aggregateSwapFeePercentage"` + AmplificationParameter *uint256.Int `json:"amplificationParameter"` BalancesLiveScaled18 []*uint256.Int `json:"balancesLiveScaled18"` DecimalScalingFactors []*uint256.Int `json:"decimalScalingFactors"` TokenRates []*uint256.Int `json:"tokenRates"` @@ -26,20 +21,7 @@ type Extra struct { } type StaticExtra struct { - PoolType string `json:"poolType"` - Vault string `json:"vault"` -} - -type PoolTokens struct { - Tokens []common.Address - Balances []*big.Int - LastChangeBlock *big.Int -} - -type PausedState struct { - Paused bool - PauseWindowEndTime *big.Int - BufferPeriodEndTime *big.Int + Vault string `json:"vault"` } type AmplificationParameter struct { @@ -67,23 +49,8 @@ type StablePoolDynamicData struct { } } -type PoolTokenInfo struct { - Tokens []common.Address - TokenInfo []TokenInfo - BalancesRaw []*big.Int - LastBalancesLiveScaled18 []*big.Int -} - -type TokenInfo struct { - TokenType uint8 - IRateProvider common.Address - PaysYieldFees bool -} - type PoolMetaInfo struct { Vault string `json:"vault"` - PoolType string `json:"poolType"` - PoolVersion int `json:"poolVersion"` TokenOutIndex int `json:"tokenOutIndex"` BlockNumber uint64 `json:"blockNumber"` } @@ -94,9 +61,9 @@ type RpcResult struct { BalancesLiveScaled18 []*big.Int TokenRates []*big.Int DecimalScalingFactors []*big.Int + AmplificationParameter *big.Int StaticSwapFeePercentage *big.Int AggregateSwapFeePercentage *big.Int - AmplificationParameter *big.Int IsVaultPaused bool IsPoolPaused bool IsPoolInRecoveryMode bool diff --git a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go index 44e610a8f..ff06f8a9d 100644 --- a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go +++ b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go @@ -7,12 +7,12 @@ import ( func toScaled18ApplyRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { scaledAmount := new(uint256.Int).Mul(amount, scalingFactor) - return math.MulUp(scaledAmount, tokenRate) + return math.FixPoint.MulUp(scaledAmount, tokenRate) } func toScaled18ApplyRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { scaledAmount := new(uint256.Int).Mul(amount, scalingFactor) - return math.MulDown(scaledAmount, tokenRate) + return math.FixPoint.MulDown(scaledAmount, tokenRate) } func computeRateRoundUp(rate *uint256.Int) *uint256.Int { @@ -27,19 +27,19 @@ func computeRateRoundUp(rate *uint256.Int) *uint256.Int { } func toRawUndoRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - divisor, err := math.Mul(scalingFactor, tokenRate) + divisor, err := math.FixPoint.Mul(scalingFactor, tokenRate) if err != nil { return nil, err } - return math.DivDown(amount, divisor) + return math.FixPoint.DivDown(amount, divisor) } func toRawUndoRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - divisor, err := math.Mul(scalingFactor, tokenRate) + divisor, err := math.FixPoint.Mul(scalingFactor, tokenRate) if err != nil { return nil, err } - return math.DivUp(amount, divisor) + return math.FixPoint.DivUp(amount, divisor) } diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index 53d52eca1..857b1d52b 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -5,6 +5,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/holiman/uint256" + "github.com/samber/lo" ) type Vault struct { @@ -35,6 +36,16 @@ func New(hook hooks.IHook, hooksConfig shared.HooksConfig, isPoolInRecoveryMode } } +func (v *Vault) CloneState() *Vault { + cloned := *v + + v.balancesLiveScaled18 = lo.Map(v.balancesLiveScaled18, func(v *uint256.Int, _ int) *uint256.Int { + return new(uint256.Int).Set(v) + }) + + return &cloned +} + func (v *Vault) Swap( vaultSwapParams shared.VaultSwapParams, onSwap func(param shared.PoolSwapParams) (*uint256.Int, error), @@ -73,12 +84,12 @@ func (v *Vault) Swap( var totalSwapFeeAmountScaled18 *uint256.Int if vaultSwapParams.Kind == shared.EXACT_IN { - totalSwapFeeAmountScaled18, err = math.MulUp(poolSwapParams.AmountGivenScaled18, poolSwapParams.SwapFeePercentage) + totalSwapFeeAmountScaled18, err = math.FixPoint.MulUp(poolSwapParams.AmountGivenScaled18, poolSwapParams.SwapFeePercentage) if err != nil { return nil, nil, nil, err } - poolSwapParams.AmountGivenScaled18, err = math.Sub(poolSwapParams.AmountGivenScaled18, totalSwapFeeAmountScaled18) + poolSwapParams.AmountGivenScaled18, err = math.FixPoint.Sub(poolSwapParams.AmountGivenScaled18, totalSwapFeeAmountScaled18) if err != nil { return nil, nil, nil, err } @@ -109,12 +120,12 @@ func (v *Vault) Swap( return nil, nil, nil, err } } else { - totalSwapFeeAmountScaled18, err := math.MulDivUp(amountCalculatedScaled18, v.swapFeePercentage, math.Complement(v.swapFeePercentage)) + totalSwapFeeAmountScaled18, err := math.FixPoint.MulDivUp(amountCalculatedScaled18, v.swapFeePercentage, math.FixPoint.Complement(v.swapFeePercentage)) if err != nil { return nil, nil, nil, err } - amountCalculatedScaled18, err = math.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) + amountCalculatedScaled18, err = math.FixPoint.Add(amountCalculatedScaled18, totalSwapFeeAmountScaled18) if err != nil { return nil, nil, nil, err } @@ -164,7 +175,7 @@ func (v *Vault) ComputeAggregateSwapFees( } if !v.isPoolInRecoveryMode { - aggregateSwapFeeAmountRaw, err = math.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) + aggregateSwapFeeAmountRaw, err = math.FixPoint.MulDown(totalSwapFeeAmountRaw, aggregateSwapFeePercentage) if err != nil { return nil, nil, err } diff --git a/pkg/liquidity-source/balancer-v3/weighted/config.go b/pkg/liquidity-source/balancer-v3/weighted/config.go index 5c81d3d36..f6661f901 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/config.go +++ b/pkg/liquidity-source/balancer-v3/weighted/config.go @@ -3,8 +3,10 @@ package weighted import "net/http" type Config struct { - DexID string `json:"dexID"` - SubgraphAPI string `json:"subgraphAPI"` - SubgraphHeaders http.Header `json:"subgraphHeaders"` - NewPoolLimit int `json:"newPoolLimit"` + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` + VaultExplorer string `json:"vaultExplorer"` + Factories map[string]string `json:"factories"` } diff --git a/pkg/liquidity-source/balancer-v3/weighted/constant.go b/pkg/liquidity-source/balancer-v3/weighted/constant.go index cabd0adcc..1eab7e1de 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/constant.go +++ b/pkg/liquidity-source/balancer-v3/weighted/constant.go @@ -1,7 +1,15 @@ package weighted +import "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + const ( DexType = "balancer-v3-weighted" PoolType = "WeightedPool" + + poolMethodGetNormalizedWeights = "getNormalizedWeights" +) + +var ( + defaultGas = shared.Gas{Swap: 80000} ) diff --git a/pkg/liquidity-source/balancer-v3/weighted/error.go b/pkg/liquidity-source/balancer-v3/weighted/error.go new file mode 100644 index 000000000..4829245ce --- /dev/null +++ b/pkg/liquidity-source/balancer-v3/weighted/error.go @@ -0,0 +1,7 @@ +package weighted + +import "errors" + +var ( + ErrInvalidToken = errors.New("invalid token") +) diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index 47c042445..4af0325d1 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -4,60 +4,40 @@ import ( "errors" "math/big" - "github.com/KyberNetwork/blockchain-toolkit/number" "github.com/goccy/go-json" "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" + poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" + "github.com/KyberNetwork/logger" ) var ( - ErrTokenNotRegistered = errors.New("TOKEN_NOT_REGISTERED") - ErrInvalidReserve = errors.New("invalid reserve") - ErrInvalidAmountIn = errors.New("invalid amount in") ErrInvalidSwapFeePercentage = errors.New("invalid swap fee percentage") - ErrPoolPaused = errors.New("pool is paused") - ErrMaxTotalInRatio = errors.New("MAX_TOTAL_IN_RATIO") - ErrMaxTotalOutRatio = errors.New("MAX_TOTAL_OUT_RATIO") - ErrOverflow = errors.New("OVERFLOW") -) - -var ( - defaultGas = Gas{Swap: 80000} - - _MAX_IN_RATIO = uint256.NewInt(0.3e18) - _MAX_OUT_RATIO = uint256.NewInt(0.3e18) + ErrInvalidAmp = errors.New("invalid amp") + ErrNotTwoTokens = errors.New("not two tokens") ) -type ( - PoolSimulator struct { - poolpkg.Pool - - paused bool +type PoolSimulator struct { + poolpkg.Pool - swapFeePercentage *uint256.Int - scalingFactors []*uint256.Int - normalizedWeights []*uint256.Int + vault *vault.Vault + normalizedWeights []*uint256.Int - vault string - poolID string - poolVerion int + hooksConfig shared.HooksConfig - totalAmountsIn []*uint256.Int - scaledMaxTotalAmountsIn []*uint256.Int - - totalAmountsOut []*uint256.Int - scaledMaxTotalAmountsOut []*uint256.Int - } + isVaultPaused bool + isPoolPaused bool + isPoolInRecoveryMode bool - Gas struct { - Swap int64 - } -) + vaultAddress string +} func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { var ( @@ -66,12 +46,6 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { tokens = make([]string, len(entityPool.Tokens)) reserves = make([]*big.Int, len(entityPool.Tokens)) - - totalAmountsIn = make([]*uint256.Int, len(entityPool.Tokens)) - scaledMaxTotalAmountsIn = make([]*uint256.Int, len(entityPool.Tokens)) - - totalAmountsOut = make([]*uint256.Int, len(entityPool.Tokens)) - scaledMaxTotalAmountsOut = make([]*uint256.Int, len(entityPool.Tokens)) ) if err := json.Unmarshal([]byte(entityPool.Extra), &extra); err != nil { @@ -87,26 +61,11 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { reserves[idx] = bignumber.NewBig10(entityPool.Reserves[idx]) } - scaledInitialBalances, err := _upscaleArray(staticExtra.PoolVersion, reserves, staticExtra.ScalingFactors) - if err != nil { - return nil, err - } - for idx := 0; idx < len(entityPool.Tokens); idx++ { - totalAmountsIn[idx] = number.Zero - totalAmountsOut[idx] = number.Zero - - maxIn, err := math.FixedPoint.MulDown(scaledInitialBalances[idx], _MAX_IN_RATIO) - if err != nil { - return nil, err - } - scaledMaxTotalAmountsIn[idx] = maxIn - - maxOut, err := math.FixedPoint.MulDown(scaledInitialBalances[idx], _MAX_OUT_RATIO) - if err != nil { - return nil, err - } - scaledMaxTotalAmountsOut[idx] = maxOut - } + // Need to detect the current hook type of pool + hook := hooks.NewBaseHook() + + vault := vault.New(hook, extra.HooksConfig, extra.IsPoolInRecoveryMode, extra.DecimalScalingFactors, extra.TokenRates, + extra.BalancesLiveScaled18, extra.StaticSwapFeePercentage, extra.AggregateSwapFeePercentage) poolInfo := poolpkg.PoolInfo{ Address: entityPool.Address, @@ -115,99 +74,48 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { Tokens: tokens, Reserves: reserves, Checked: true, - BlockNumber: uint64(entityPool.BlockNumber), + BlockNumber: entityPool.BlockNumber, } return &PoolSimulator{ - Pool: poolpkg.Pool{Info: poolInfo}, - paused: extra.Paused, - swapFeePercentage: extra.SwapFeePercentage, - scalingFactors: staticExtra.ScalingFactors, - vault: staticExtra.Vault, - poolID: staticExtra.PoolID, - poolVerion: staticExtra.PoolVersion, - totalAmountsIn: totalAmountsIn, - scaledMaxTotalAmountsIn: scaledMaxTotalAmountsIn, - totalAmountsOut: totalAmountsOut, - scaledMaxTotalAmountsOut: scaledMaxTotalAmountsOut, + Pool: poolpkg.Pool{Info: poolInfo}, + isVaultPaused: extra.IsVaultPaused, + isPoolPaused: extra.IsPoolPaused, + isPoolInRecoveryMode: extra.IsPoolInRecoveryMode, + vault: vault, + hooksConfig: extra.HooksConfig, + normalizedWeights: extra.NormalizedWeights, + vaultAddress: staticExtra.Vault, }, nil } -// https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F7#L32 -func (s *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { - if s.paused { - return nil, ErrPoolPaused +func (p *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*poolpkg.CalcAmountOutResult, error) { + if p.isVaultPaused { + return nil, shared.ErrVaultIsPaused } - tokenAmountIn := params.TokenAmountIn - tokenOut := params.TokenOut - - indexIn, indexOut := s.GetTokenIndex(tokenAmountIn.Token), s.GetTokenIndex(tokenOut) - - if indexIn == -1 || indexOut == -1 { - return nil, ErrTokenNotRegistered + if p.isPoolPaused { + return nil, shared.ErrPoolIsPaused } - reserveIn, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexIn]) - if overflow { - return nil, ErrInvalidReserve - } + tokenAmountIn, tokenOut := params.TokenAmountIn, params.TokenOut - reserveOut, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexOut]) - if overflow { - return nil, ErrInvalidReserve + indexIn, indexOut := p.GetTokenIndex(tokenAmountIn.Token), p.GetTokenIndex(tokenOut) + if indexIn < 0 || indexOut < 0 { + return nil, shared.ErrTokenNotRegistered } amountIn, overflow := uint256.FromBig(tokenAmountIn.Amount) if overflow { - return nil, ErrInvalidAmountIn + return nil, shared.ErrInvalidAmountIn } - scalingFactorTokenIn := s.scalingFactors[indexIn] - scalingFactorTokenOut := s.scalingFactors[indexOut] - normalizedWeightIn := s.normalizedWeights[indexIn] - normalizedWeightOut := s.normalizedWeights[indexOut] - - balanceTokenIn, err := _upscale(s.poolVerion, reserveIn, scalingFactorTokenIn) - if err != nil { - return nil, err - } - balanceTokenOut, err := _upscale(s.poolVerion, reserveOut, scalingFactorTokenOut) - if err != nil { - return nil, err - } - - feeAmount, err := math.FixedPoint.MulUp(amountIn, s.swapFeePercentage) - if err != nil { - return nil, err - } - - amountInAfterFee, err := math.FixedPoint.Sub(amountIn, feeAmount) - if err != nil { - return nil, err - } - - if err := s.validateMaxInRatio(indexIn, amountInAfterFee); err != nil { - return nil, err - } - - upScaledAmountIn, err := _upscale(s.poolVerion, amountInAfterFee, scalingFactorTokenIn) - if err != nil { - return nil, err - } - - upScaledAmountOut, err := s._onSwapGivenIn( - balanceTokenIn, - normalizedWeightIn, - balanceTokenOut, - normalizedWeightOut, - upScaledAmountIn, - ) - if err != nil { - return nil, err - } - - amountOut, err := _downscaleDown(s.poolVerion, upScaledAmountOut, scalingFactorTokenOut) + amountOut, totalSwapFee, aggregateFee, err := p.vault.Swap(shared.VaultSwapParams{ + Kind: shared.EXACT_IN, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGivenRaw: amountIn, + }, p.OnSwap) if err != nil { return nil, err } @@ -219,86 +127,42 @@ func (s *PoolSimulator) CalcAmountOut(params poolpkg.CalcAmountOutParams) (*pool }, Fee: &poolpkg.TokenAmount{ Token: tokenAmountIn.Token, - Amount: feeAmount.ToBig(), + Amount: totalSwapFee.ToBig(), + }, + SwapInfo: SwapInfo{ + AggregateFee: aggregateFee.ToBig(), }, Gas: defaultGas.Swap, }, nil } -func (s *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { - if s.paused { - return nil, ErrPoolPaused +func (p *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpkg.CalcAmountInResult, error) { + if p.isVaultPaused { + return nil, shared.ErrVaultIsPaused } - tokenAmountOut := params.TokenAmountOut - tokenIn := params.TokenIn - - indexIn, indexOut := s.GetTokenIndex(tokenIn), s.GetTokenIndex(tokenAmountOut.Token) - - if indexIn == -1 || indexOut == -1 { - return nil, ErrTokenNotRegistered + if p.isPoolPaused { + return nil, shared.ErrPoolIsPaused } - reserveIn, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexIn]) - if overflow { - return nil, ErrInvalidReserve - } + tokenAmountOut, tokenIn := params.TokenAmountOut, params.TokenIn - reserveOut, overflow := uint256.FromBig(s.Pool.Info.Reserves[indexOut]) - if overflow { - return nil, ErrInvalidReserve + indexIn, indexOut := p.GetTokenIndex(tokenIn), p.GetTokenIndex(tokenAmountOut.Token) + if indexIn < 0 || indexOut < 0 { + return nil, shared.ErrTokenNotRegistered } amountOut, overflow := uint256.FromBig(tokenAmountOut.Amount) if overflow { - return nil, ErrInvalidAmountIn - } - - if err := s.validateMaxOutRatio(indexOut, amountOut); err != nil { - return nil, err - } - - scalingFactorTokenIn := s.scalingFactors[indexIn] - scalingFactorTokenOut := s.scalingFactors[indexOut] - normalizedWeightIn := s.normalizedWeights[indexIn] - normalizedWeightOut := s.normalizedWeights[indexOut] - - balanceTokenIn, err := _upscale(s.poolVerion, reserveIn, scalingFactorTokenIn) - if err != nil { - return nil, err - } - balanceTokenOut, err := _upscale(s.poolVerion, reserveOut, scalingFactorTokenOut) - if err != nil { - return nil, err - } - - upScaledAmountOut, err := _upscale(s.poolVerion, amountOut, scalingFactorTokenOut) - if err != nil { - return nil, err - } - - upScaledAmountIn, err := s._onSwapGivenOut( - balanceTokenIn, - normalizedWeightIn, - balanceTokenOut, - normalizedWeightOut, - upScaledAmountOut, - ) - if err != nil { - return nil, err - } - - amountIn, err := _downscaleUp(s.poolVerion, upScaledAmountIn, scalingFactorTokenIn) - if err != nil { - return nil, err + return nil, shared.ErrInvalidAmountOut } - amountInAfterFee, err := s._addSwapFeeAmount(amountIn) - if err != nil { - return nil, err - } - - feeAmount, err := math.FixedPoint.Sub(amountInAfterFee, amountIn) + amountIn, totalSwapFee, aggregateSwapFee, err := p.vault.Swap(shared.VaultSwapParams{ + Kind: shared.EXACT_OUT, + IndexIn: indexIn, + IndexOut: indexOut, + AmountGivenRaw: amountOut, + }, p.OnSwap) if err != nil { return nil, err } @@ -310,184 +174,107 @@ func (s *PoolSimulator) CalcAmountIn(params poolpkg.CalcAmountInParams) (*poolpk }, Fee: &poolpkg.TokenAmount{ Token: tokenIn, - Amount: feeAmount.ToBig(), + Amount: totalSwapFee.ToBig(), + }, + SwapInfo: SwapInfo{ + AggregateFee: aggregateSwapFee.ToBig(), }, Gas: defaultGas.Swap, }, nil } -// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F1#L165 -// -// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F3#L117 -func (s *PoolSimulator) _onSwapGivenIn( - balanceTokenIn *uint256.Int, - normalizedWeightIn *uint256.Int, - balanceTokenOut *uint256.Int, - normalizedWeightOut *uint256.Int, - upScaledAmountIn *uint256.Int, -) (*uint256.Int, error) { - if s.poolVerion == shared.PoolVersion1 { - return math.WeightedMath.CalcOutGivenInV1( - balanceTokenIn, - normalizedWeightIn, - balanceTokenOut, - normalizedWeightOut, - upScaledAmountIn, - ) - } +func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { + tokenIndexIn := p.GetTokenIndex(params.TokenAmountIn.Token) + tokenIndexOut := p.GetTokenIndex(params.TokenAmountOut.Token) - return math.WeightedMath.CalcOutGivenIn( - balanceTokenIn, - normalizedWeightIn, - balanceTokenOut, - normalizedWeightOut, - upScaledAmountIn, - ) -} - -// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F1#L182 -// -// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F3#L132 -func (s *PoolSimulator) _onSwapGivenOut( - balanceTokenIn *uint256.Int, - normalizedWeightIn *uint256.Int, - balanceTokenOut *uint256.Int, - normalizedWeightOut *uint256.Int, - upScaledAmountOut *uint256.Int, -) (*uint256.Int, error) { - if s.poolVerion == shared.PoolVersion1 { - return math.WeightedMath.CalcInGivenOutV1( - balanceTokenIn, - normalizedWeightIn, - balanceTokenOut, - normalizedWeightOut, - upScaledAmountOut, - ) + swapInfo, ok := params.SwapInfo.(SwapInfo) + if !ok { + return } - return math.WeightedMath.CalcInGivenOut( - balanceTokenIn, - normalizedWeightIn, - balanceTokenOut, - normalizedWeightOut, - upScaledAmountOut, - ) -} + updatedRawBalanceIn := new(big.Int) + updatedRawBalanceIn.Add(p.Info.Reserves[tokenIndexIn], params.TokenAmountIn.Amount) + updatedRawBalanceIn.Sub(updatedRawBalanceIn, swapInfo.AggregateFee) + p.Info.Reserves[tokenIndexIn] = updatedRawBalanceIn -// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F28#L454 -// -// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F14#L619 -func (s *PoolSimulator) _addSwapFeeAmount(amount *uint256.Int) (*uint256.Int, error) { - // This returns amount + fee amount, so we round up (favoring a higher fee amount). - return math.FixedPoint.DivUp(amount, math.FixedPoint.Complement(s.swapFeePercentage)) -} + amountGivenRaw := uint256.MustFromBig(updatedRawBalanceIn) -func (s *PoolSimulator) validateMaxInRatio(tokenIndex int, amountIn *uint256.Int) error { - sum := new(uint256.Int).Add(s.totalAmountsIn[tokenIndex], amountIn) - upscaledSum, err := _upscale(s.poolVerion, sum, s.scalingFactors[tokenIndex]) + _, err := p.vault.UpdateLiveBalance(tokenIndexIn, amountGivenRaw, shared.ROUND_DOWN) if err != nil { - return err + logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + return } - if upscaledSum.Gt(s.scaledMaxTotalAmountsIn[tokenIndex]) { - return ErrMaxTotalInRatio - } + updatedRawBalanceOut := new(big.Int) + updatedRawBalanceOut.Sub(p.Info.Reserves[tokenIndexOut], params.TokenAmountOut.Amount) + p.Info.Reserves[tokenIndexOut] = updatedRawBalanceOut - return nil -} + amountGivenRaw.SetFromBig(updatedRawBalanceOut) -func (s *PoolSimulator) validateMaxOutRatio(tokenIndex int, amountOut *uint256.Int) error { - sum := new(uint256.Int).Add(s.totalAmountsOut[tokenIndex], amountOut) - upscaledSum, err := _upscale(s.poolVerion, sum, s.scalingFactors[tokenIndex]) + _, err = p.vault.UpdateLiveBalance(tokenIndexOut, amountGivenRaw, shared.ROUND_DOWN) if err != nil { - return err - } - - if upscaledSum.Gt(s.scaledMaxTotalAmountsOut[tokenIndex]) { - return ErrMaxTotalOutRatio - } - - return nil -} - -func (s *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { - for idx, token := range s.Info.Tokens { - if token == params.TokenAmountIn.Token { - s.Info.Reserves[idx] = new(big.Int).Add( - s.Info.Reserves[idx], - params.TokenAmountIn.Amount, - ) - - s.totalAmountsIn[idx] = new(uint256.Int).Add( - s.totalAmountsIn[idx], - uint256.MustFromBig(params.TokenAmountIn.Amount), - ) - } - - if token == params.TokenAmountOut.Token { - s.Info.Reserves[idx] = new(big.Int).Sub( - s.Info.Reserves[idx], - params.TokenAmountOut.Amount, - ) - } + logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + return } } func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} { return PoolMetaInfo{ - Vault: s.vault, - PoolID: s.poolID, + Vault: s.vaultAddress, TokenOutIndex: s.GetTokenIndex(tokenOut), BlockNumber: s.Info.BlockNumber, } } -// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F27#L529 -// -// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F13#L681 -func _upscale(poolVerion int, amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { - if poolVerion == shared.PoolVersion1 { - return math.Math.Mul(amount, scalingFactor) +func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (amountOutScaled18 *uint256.Int, err error) { + balanceTokenInScaled18 := param.BalancesLiveScaled18[param.IndexIn] + balanceTokenOutScaled18 := param.BalancesLiveScaled18[param.IndexOut] + + weightIn, err := p.getNormalizedWeight(param.IndexIn) + if err != nil { + return nil, err } - return math.FixedPoint.MulDown(amount, scalingFactor) -} + weightOut, err := p.getNormalizedWeight(param.IndexOut) + if err != nil { + return nil, err + } -// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F28#L547 -// -// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F14#L706 -func _downscaleDown(poolVerion int, amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { - if poolVerion == shared.PoolVersion1 { - return math.Math.DivDown(amount, scalingFactor) + if param.Kind == shared.EXACT_IN { + amountOutScaled18, err = math.WeightedMath.ComputeOutGivenExactIn( + balanceTokenInScaled18, + weightIn, + balanceTokenOutScaled18, + weightOut, + param.AmountGivenScaled18, + ) + } else { + amountOutScaled18, err = math.WeightedMath.ComputeInGivenExactOut( + balanceTokenInScaled18, + weightIn, + balanceTokenOutScaled18, + weightOut, + param.AmountGivenScaled18, + ) + } + if err != nil { + return nil, err } - return math.FixedPoint.DivDown(amount, scalingFactor) + return } -// Version = 1: https://etherscan.io/address/0x6df50e37a6aefb9024a7284ef1c9e1e8e7c4f7b8#code#F28#L565 -// -// Version > 1: https://etherscan.io/address/0x065f5b35d4077334379847fe26f58b1029e51161#code#F14#L727 -func _downscaleUp(poolVerion int, amount *uint256.Int, scalingFactor *uint256.Int) (*uint256.Int, error) { - if poolVerion == shared.PoolVersion1 { - return math.Math.DivUp(amount, scalingFactor) +func (p *PoolSimulator) getNormalizedWeight(tokenIndex int) (*uint256.Int, error) { + if tokenIndex > len(p.normalizedWeights) { + return nil, ErrInvalidToken } - return math.FixedPoint.DivUp(amount, scalingFactor) + return p.normalizedWeights[tokenIndex], nil } -func _upscaleArray(poolVerion int, balances []*big.Int, scalingFactors []*uint256.Int) ([]*uint256.Int, error) { - upscaled := make([]*uint256.Int, len(balances)) - for i, balance := range balances { - b, overflow := uint256.FromBig(balance) - if overflow { - return nil, ErrOverflow - } - - upscaledI, err := _upscale(poolVerion, b, scalingFactors[i]) - if err != nil { - return nil, err - } - upscaled[i] = upscaledI - } - return upscaled, nil +func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { + cloned := *p + cloned.vault = p.vault.CloneState() + + return &cloned } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go index 9f781f19d..e8933e50f 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go @@ -9,376 +9,230 @@ import ( "github.com/stretchr/testify/assert" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" ) -func Test_CalcAmountOut(t *testing.T) { - t.Run("1. should return OK", func(t *testing.T) { - // input +func TestCalcAmountOut(t *testing.T) { + t.Run("1. should return error balance didnt converge", func(t *testing.T) { + reserves := make([]*big.Int, 3) + reserves[0], _ = new(big.Int).SetString("9999991000000000000", 10) + reserves[1], _ = new(big.Int).SetString("99999910000000000056", 10) + reserves[2], _ = new(big.Int).SetString("8897791020011100123456", 10) + s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ + Reserves: reserves, Tokens: []string{ - "0xac3E018457B222d93114458476f3E3416Abbe38F", - "0xae78736Cd615f374D3085123A210448E74Fc6393", - "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", - }, - Reserves: []*big.Int{ - big.NewInt(331125), - big.NewInt(320633), - big.NewInt(348846), + "0xdac17f958d2ee523a2206206994597c13d831ec7", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0x6b175474e89094c44da98b954eedeac495271d0f", }, }, }, - - swapFeePercentage: uint256.NewInt(3000000000000000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - }, - normalizedWeights: []*uint256.Int{ - uint256.NewInt(333300000000000000), - uint256.NewInt(333300000000000000), - uint256.NewInt(333400000000000000), - }, - totalAmountsIn: []*uint256.Int{uint256.NewInt(0), uint256.NewInt(0), uint256.NewInt(0)}, - scaledMaxTotalAmountsIn: []*uint256.Int{ - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - }, - poolVerion: 3, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 + uint256.NewInt(30000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + // currentAmp: uint256.NewInt(1000000), } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xac3E018457B222d93114458476f3E3416Abbe38F", - Amount: big.NewInt(3311), + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: new(big.Int).SetUint64(99999910000000), } - tokenOut := "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" - - // expected - amountOut := "3442" - - // calculation - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - - // assert - assert.Nil(t, err) - assert.Equal(t, amountOut, result.TokenAmountOut.Amount.String()) + assert.ErrorIs(t, err, math.ErrStableComputeBalanceDidNotConverge) }) t.Run("2. should return OK", func(t *testing.T) { // input - reserve0, _ := new(big.Int).SetString("3360160080014532471350474", 10) - reserve1, _ := new(big.Int).SetString("1112301324508754708737", 10) + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("151090057727415359409", 10) + reserves[1], _ = new(big.Int).SetString("249356634133584290044", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ + Reserves: reserves, Tokens: []string{ - "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a", - "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, + "0x5F9D59db355b4A60501544637b00e94082cA575b", + "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8", }, }, }, - - swapFeePercentage: uint256.NewInt(1000000000000000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - }, - normalizedWeights: []*uint256.Int{ - uint256.NewInt(800000000000000000), - uint256.NewInt(200000000000000000), - }, - totalAmountsIn: []*uint256.Int{uint256.NewInt(0), uint256.NewInt(0)}, - scaledMaxTotalAmountsIn: []*uint256.Int{ - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - }, - poolVerion: 2, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 + uint256.NewInt(30000000000000), + uint256.NewInt(500000000000000000), + ), + // currentAmp: uint256.NewInt(1000000), } - amountIn, _ := new(big.Int).SetString("60160080014532471350474", 10) tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a", - Amount: amountIn, + Token: "0x5F9D59db355b4A60501544637b00e94082cA575b", + Amount: new(big.Int).SetUint64(10000000), } - tokenOut := "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223" + tokenOut := "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8" - // expected - amountOut := "76143667376405160244" + expectedAmountOut := "9999700" + expectedSwapFee := "300" - // calculation result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - - // assert assert.Nil(t, err) - assert.Equal(t, amountOut, result.TokenAmountOut.Amount.String()) + + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) t.Run("3. should return OK", func(t *testing.T) { // input - reserve0, _ := new(big.Int).SetString("3360160080014532471350474", 10) - reserve1, _ := new(big.Int).SetString("1112301324508754708737", 10) + reserves := make([]*big.Int, 3) + reserves[0], _ = new(big.Int).SetString("9999991000000000013314124321", 10) + reserves[1], _ = new(big.Int).SetString("9999991000000123120010005613", 10) + reserves[2], _ = new(big.Int).SetString("1328897131447911102200123456", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ + Reserves: reserves, Tokens: []string{ - "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a", - "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223", - }, - Reserves: []*big.Int{ - reserve0, - reserve1, + "0xdac17f958d2ee523a2206206994597c13d831ec7", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0x6b175474e89094c44da98b954eedeac495271d0f", }, }, }, - - swapFeePercentage: uint256.NewInt(1000000000000000), - scalingFactors: []*uint256.Int{ - uint256.NewInt(1000000000000000000), - uint256.NewInt(1000000000000000000), - }, - normalizedWeights: []*uint256.Int{ - uint256.NewInt(800000000000000000), - uint256.NewInt(200000000000000000), - }, - totalAmountsIn: []*uint256.Int{uint256.NewInt(0), uint256.NewInt(0)}, - scaledMaxTotalAmountsIn: []*uint256.Int{ - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935"), - }, - poolVerion: 4, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 + uint256.NewInt(30000000000000), + uint256.NewInt(500000000000000000), + ), + // currentAmp: uint256.NewInt(1000000), } - amountIn, _ := new(big.Int).SetString("6016008001453247", 10) tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xbE666bB32a8e4B6b2f2D0fb053d965bdfA277223", - Amount: amountIn, + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: new(big.Int).SetUint64(12111222333444555666), } - tokenOut := "0x5a8F45b943A7E6a4BEA463A98de68940A153c78a" + tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" // expected - amountOut := "4538893010907736440" + expected := "590000000000000000" - // calculation + // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - - // assert - assert.Nil(t, err) - assert.Equal(t, amountOut, result.TokenAmountOut.Amount.String()) - }) - - t.Run("4. should return OK", func(t *testing.T) { - // input - // block 18783187 - p := `{ - "address": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", - "reserveUsd": 153314467.24136648, - "amplifiedTvl": 153314467.24136648, - "exchange": "balancer-v2-weighted", - "type": "balancer-v2-weighted", - "timestamp": 1702542461, - "reserves": [ - "31686717298564222587034828", - "14236767788701850247952" - ], - "tokens": [ - { - "address": "0xba100000625a3754423978a60c9317c58a424e3d", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - } - ], - "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0xb1a2bc2ec500000\",\"0x2c68af0bb140000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" -}` - var pool entity.Pool - err := json.Unmarshal([]byte(p), &pool) assert.Nil(t, err) - // expected - expectedAmountOut := "1014934149732776116160723" - - // calculation - simulator, err := NewPoolSimulator(pool) - assert.Nil(t, err) - amountIn, _ := new(big.Int).SetString("2000000000000000000000", 10) - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return simulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: poolpkg.TokenAmount{ - Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - Amount: amountIn, - }, - TokenOut: "0xba100000625a3754423978a60c9317c58a424e3d", - }) - }) - // assert - assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) }) - t.Run("5. should return OK", func(t *testing.T) { - // input - // polygon, block 51339771 - p := `{ - "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", - "reserveUsd": 3454.483888331181, - "amplifiedTvl": 3454.483888331181, - "exchange": "balancer-v2-weighted", - "type": "balancer-v2-weighted", - "timestamp": 1703033832, + t.Run("4. should return OK", func(t *testing.T) { + poolStr := `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-stable", + "type": "balancer-v2-stable", + "timestamp": 1703667290, "reserves": [ - "382259350067562080018", - "563895201975090444069", - "432276836", - "415858931425966091248020", - "198780894165507591", - "9187067339281421763", - "111172932376992452571", - "1835599921140802978251" + "1152882153159026494", + "873225053252443292" ], "tokens": [ - { - "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - } + { + "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + } ], - "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }` + "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }` var pool entity.Pool - err := json.Unmarshal([]byte(p), &pool) + err := json.Unmarshal([]byte(poolStr), &pool) + assert.Nil(t, err) + + s, err := NewPoolSimulator(pool) assert.Nil(t, err) + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Amount: big.NewInt(73183418984294781), + } + tokenOut := "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0" + // expected - expectedAmountOut := "49523009318781117474536" + expected := "63551050657042642" - // calculation - simulator, err := NewPoolSimulator(pool) - assert.Nil(t, err) - amountIn, _ := new(big.Int).SetString("77000000000000000000", 10) + // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { - return simulator.CalcAmountOut(poolpkg.CalcAmountOutParams{ - TokenAmountIn: poolpkg.TokenAmount{ - Token: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - Amount: amountIn, - }, - TokenOut: "0x580a84c73811e1839f75d86d75d88cca0c241ff4", + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, }) }) - // assert assert.Nil(t, err) - assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + + // assert + assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) + }) } func TestPoolSimulator_CalcAmountIn(t *testing.T) { - amountOutTest1, _ := new(big.Int).SetString("1014934149732776116160723", 10) - expectedAmountInTest1, _ := new(big.Int).SetString("1979999999999513367997", 10) - - amountOutTest2, _ := new(big.Int).SetString("49523009318781117474536", 10) - expectedAmountInTest2, _ := new(big.Int).SetString("76229999999999997187", 10) - type fields struct { - p string + poolStr string } + tests := []struct { name string fields fields @@ -387,367 +241,113 @@ func TestPoolSimulator_CalcAmountIn(t *testing.T) { wantErr error }{ { - name: "1. should return OK", + name: "1. should return error ErrStableGetBalanceDidntConverge", fields: fields{ - p: `{ - "address": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", - "reserveUsd": 153314467.24136648, - "amplifiedTvl": 153314467.24136648, - "exchange": "balancer-v2-weighted", - "type": "balancer-v2-weighted", - "timestamp": 1702542461, + poolStr: `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-stable", + "type": "balancer-v2-stable", + "timestamp": 1703667290, "reserves": [ - "31686717298564222587034828", - "14236767788701850247952" + "9999991000000000000", + "99999910000000000056", + "8897791020011100123456" ], "tokens": [ { - "address": "0xba100000625a3754423978a60c9317c58a424e3d", + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", "name": "", "symbol": "", "decimals": 0, - "weight": 0, + "weight": 1, "swappable": true }, { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "name": "", "symbol": "", "decimals": 0, - "weight": 0, - "swappable": true - } - ], - "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0xb1a2bc2ec500000\",\"0x2c68af0bb140000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0xba100000625a3754423978a60c9317c58a424e3d", - Amount: amountOutTest1, - }, - TokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - }, - want: &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - Amount: expectedAmountInTest1, - }, - }, - wantErr: nil, - }, - { - name: "2. should return OK", - fields: fields{ - p: `{ - "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", - "reserveUsd": 3454.483888331181, - "amplifiedTvl": 3454.483888331181, - "exchange": "balancer-v2-weighted", - "type": "balancer-v2-weighted", - "timestamp": 1703033832, - "reserves": [ - "382259350067562080018", - "563895201975090444069", - "432276836", - "415858931425966091248020", - "198780894165507591", - "9187067339281421763", - "111172932376992452571", - "1835599921140802978251" - ], - "tokens": [ - { - "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, + "weight": 1, "swappable": true }, { - "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", "name": "", "symbol": "", "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, + "weight": 1, "swappable": true } ], - "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, + "extra": "{\"amp\":\"0x1388\",\"swapFeePercentage\":\"0x2D79883D2000\",\"scalingFactors\":[\"100\",\"1\",\"100\"],\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"Stable\",\"poolVersionsion\":1,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, }, params: poolpkg.CalcAmountInParams{ TokenAmountOut: poolpkg.TokenAmount{ - Token: "0x580a84c73811e1839f75d86d75d88cca0c241ff4", - Amount: amountOutTest2, - }, - TokenIn: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - }, - want: &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - Amount: expectedAmountInTest2, + Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", + Amount: big.NewInt(999999100000), }, + TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", }, - wantErr: nil, + want: nil, + wantErr: math.ErrStableComputeBalanceDidNotConverge, }, { - name: "3. should return error ErrPoolPaused", + name: "2. should return OK", fields: fields{ - p: `{ - "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", - "reserveUsd": 3454.483888331181, - "amplifiedTvl": 3454.483888331181, - "exchange": "balancer-v2-weighted", - "type": "balancer-v2-weighted", - "timestamp": 1703033832, + poolStr: `{ + "address": "0x851523a36690bf267bbfec389c823072d82921a9", + "exchange": "balancer-v2-stable", + "type": "balancer-v2-stable", + "timestamp": 1703667290, "reserves": [ - "382259350067562080018", - "563895201975090444069", - "432276836", - "415858931425966091248020", - "198780894165507591", - "9187067339281421763", - "111172932376992452571", - "1835599921140802978251" + "1152882153159026494", + "873225053252443292" ], "tokens": [ - { - "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - } + { + "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "name": "", + "symbol": "", + "decimals": 0, + "weight": 1, + "swappable": true + } ], - "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":true}", - "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, + "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", + "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" + }`, }, params: poolpkg.CalcAmountInParams{ TokenAmountOut: poolpkg.TokenAmount{ - Token: "0x580a84c73811e1839f75d86d75d88cca0c241ff5", // not registered token, last character should be 4 - Amount: amountOutTest2, + Token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + Amount: big.NewInt(63551050657042642), }, - TokenIn: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - }, - want: nil, - wantErr: ErrPoolPaused, - }, - { - name: "4. should return error ErrTokenNotRegistered", - fields: fields{ - p: `{ - "address": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a", - "reserveUsd": 3454.483888331181, - "amplifiedTvl": 3454.483888331181, - "exchange": "balancer-v2-weighted", - "type": "balancer-v2-weighted", - "timestamp": 1703033832, - "reserves": [ - "382259350067562080018", - "563895201975090444069", - "432276836", - "415858931425966091248020", - "198780894165507591", - "9187067339281421763", - "111172932376992452571", - "1835599921140802978251" - ], - "tokens": [ - { - "address": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x580a84c73811e1839f75d86d75d88cca0c241ff4", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x831753dd7087cac61ab5644b308642cc1c33dc13", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - }, - { - "address": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 0, - "swappable": true - } - ], - "extra": "{\"swapFeePercentage\":\"0x2386f26fc10000\",\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005\",\"poolType\":\"Weighted\",\"poolTypeVer\":1,\"scalingFactors\":[\"0x1\",\"0x1\",\"0xe8d4a51000\",\"0x1\",\"0x1\",\"0x1\",\"0x1\",\"0x1\"],\"normalizedWeights\":[\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\",\"0x1bc16d674ec8000\"],\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, + TokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0x580a84c73811e1839f75d86d75d88cca0c241ff5", // not registered token, last character should be 4 - Amount: amountOutTest2, + want: &poolpkg.CalcAmountInResult{ + TokenAmountIn: &poolpkg.TokenAmount{ + Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Amount: big.NewInt(73154145616700748), }, - TokenIn: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270", }, - want: nil, - wantErr: ErrTokenNotRegistered, + wantErr: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var pool entity.Pool - err := json.Unmarshal([]byte(tt.fields.p), &pool) + err := json.Unmarshal([]byte(tt.fields.poolStr), &pool) assert.Nil(t, err) simulator, err := NewPoolSimulator(pool) diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go index 39cc39dfe..75d4d1aa4 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go @@ -4,15 +4,19 @@ import ( "context" "errors" "math/big" - "strings" "time" "github.com/KyberNetwork/ethrpc" "github.com/KyberNetwork/logger" + "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" + "github.com/holiman/uint256" + "github.com/samber/lo" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/ethereum/go-ethereum/ethclient/gethclient" ) var ErrReserveNotFound = errors.New("reserve not found") @@ -33,9 +37,26 @@ func NewPoolTracker( } func (t *PoolTracker) GetNewPoolState( + ctx context.Context, + p entity.Pool, + params poolpkg.GetNewPoolStateParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, params, nil) +} + +func (t *PoolTracker) GetNewPoolStateWithOverrides( + ctx context.Context, + p entity.Pool, + params poolpkg.GetNewPoolStateWithOverridesParams, +) (entity.Pool, error) { + return t.getNewPoolState(ctx, p, poolpkg.GetNewPoolStateParams{Logs: params.Logs}, params.Overrides) +} + +func (t *PoolTracker) getNewPoolState( ctx context.Context, p entity.Pool, _ poolpkg.GetNewPoolStateParams, + overrides map[common.Address]gethclient.OverrideAccount, ) (entity.Pool, error) { logger.WithFields(logger.Fields{ "dexId": t.config.DexID, @@ -51,37 +72,49 @@ func (t *PoolTracker) GetNewPoolState( }).Info("Finish updating state.") }() - var staticExtra StaticExtra - if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { + res, err := t.queryRPC(ctx, p.Address, overrides) + if err != nil { + return p, err + } + + if len(p.Reserves) != len(res.BalancesRaw) { logger.WithFields(logger.Fields{ "dexId": t.config.DexID, "dexType": DexType, "poolAddress": p.Address, - }).Error(err.Error()) - - return p, err - } - - // call RPC - rpcRes, err := t.queryRPC(ctx, p.Address, staticExtra.PoolID, staticExtra.Vault) - if err != nil { + }).Error("can not fetch reserves") return p, err } var ( - poolTokens = rpcRes.PoolTokens - swapFeePercentage = rpcRes.SwapFeePercentage - pausedState = rpcRes.PausedState - blockNumber = rpcRes.BlockNumber + staticSwapFeePercentage, _ = uint256.FromBig(res.StaticSwapFeePercentage) + aggregateSwapFeePercentage, _ = uint256.FromBig(res.AggregateSwapFeePercentage) + + balancesLiveScaled18 = lo.Map(res.BalancesLiveScaled18, func(v *big.Int, _ int) *uint256.Int { + return uint256.MustFromBig(v) + }) + tokenRates = lo.Map(res.TokenRates, func(v *big.Int, _ int) *uint256.Int { + return uint256.MustFromBig(v) + }) + decimalScalingFactors = lo.Map(res.DecimalScalingFactors, func(v *big.Int, _ int) *uint256.Int { + return uint256.MustFromBig(v) + }) + normalizedWeights = lo.Map(res.NormalizedWeights, func(v *big.Int, _ int) *uint256.Int { + return uint256.MustFromBig(v) + }) ) - // update pool - - extra := Extra{ - SwapFeePercentage: swapFeePercentage, - Paused: !isNotPaused(pausedState), - } - extraBytes, err := json.Marshal(extra) + extraBytes, err := json.Marshal(&Extra{ + NormalizedWeights: normalizedWeights, + StaticSwapFeePercentage: staticSwapFeePercentage, + AggregateSwapFeePercentage: aggregateSwapFeePercentage, + BalancesLiveScaled18: balancesLiveScaled18, + DecimalScalingFactors: decimalScalingFactors, + TokenRates: tokenRates, + IsVaultPaused: res.IsVaultPaused, + IsPoolPaused: res.IsPoolPaused, + IsPoolInRecoveryMode: res.IsPoolInRecoveryMode, + }) if err != nil { logger.WithFields(logger.Fields{ "dexId": t.config.DexID, @@ -92,107 +125,127 @@ func (t *PoolTracker) GetNewPoolState( return p, err } - reserves, err := t.initReserves(ctx, p, poolTokens) - if err != nil { - return p, err - } - - p.BlockNumber = blockNumber + p.BlockNumber = res.BlockNumber p.Extra = string(extraBytes) p.Timestamp = time.Now().Unix() - p.Reserves = reserves + p.Reserves = lo.Map(res.TokenRates, func(v *big.Int, _ int) string { + return v.String() + }) return p, nil } -func (t *PoolTracker) initReserves( +func (t *PoolTracker) queryRPC( ctx context.Context, - p entity.Pool, - poolTokens PoolTokens, -) ([]string, error) { - reserveByToken := make(map[string]*big.Int) - for idx, token := range poolTokens.Tokens { - addr := strings.ToLower(token.Hex()) - reserveByToken[addr] = poolTokens.Balances[idx] - } + poolAddress string, + overrides map[common.Address]gethclient.OverrideAccount, +) (*RpcResult, error) { + var ( + aggregateFeePercentages shared.AggregateFeePercentage + hooksConfig shared.HooksConfigRPC + poolData shared.PoolDataRPC - reserves := make([]string, len(p.Tokens)) - for idx, token := range p.Tokens { - r, ok := reserveByToken[token.Address] - if !ok { - logger.WithFields(logger.Fields{ - "dexId": t.config.DexID, - "dexType": DexType, - "poolAddress": p.Address, - }).Error("can not get reserve") + normalizedWeights []*big.Int + staticSwapFeePercentage *big.Int - return nil, ErrReserveNotFound - } + isVaultPaused bool + isPoolPaused bool + isPoolInRecoveryMode bool + ) - reserves[idx] = r.String() + req := t.ethrpcClient.R().SetContext(ctx).SetRequireSuccess(true) + if overrides != nil { + req.SetOverrides(overrides) } - return reserves, nil -} + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetAggregateFeePercentages, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&aggregateFeePercentages}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetStaticSwapFeePercentage, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&staticSwapFeePercentage}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetPoolData, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&poolData}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodGetHooksConfig, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&hooksConfig}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodIsVaultPaused, + }, []interface{}{&isVaultPaused}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodIsPoolPaused, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&isPoolPaused}) + + req.AddCall(ðrpc.Call{ + ABI: shared.VaultExplorerABI, + Target: t.config.VaultExplorer, + Method: shared.VaultMethodIsPoolInRecoveryMode, + Params: []interface{}{common.HexToAddress(poolAddress)}, + }, []interface{}{&isPoolInRecoveryMode}) + + req.AddCall(ðrpc.Call{ + ABI: poolABI, + Target: poolAddress, + Method: poolMethodGetNormalizedWeights, + }, []interface{}{&normalizedWeights}) + + res, err := req.TryBlockAndAggregate() + if err != nil { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": poolAddress, + }).Error(err.Error()) + return nil, err + } -func (t *PoolTracker) queryRPC( - ctx context.Context, - poolAddress string, - poolID string, - vault string, -) (*rpcRes, error) { - // var ( - // poolTokens PoolTokens - // swapFeePercentage *big.Int - // pausedState PausedState - // ) - - // req := t.ethrpcClient.R(). - // SetContext(ctx). - // SetRequireSuccess(true) - - // req.AddCall(ðrpc.Call{ - // ABI: shared.VaultABI, - // Target: vault, - // Method: shared.VaultMethodGetPoolTokens, - // Params: []interface{}{common.HexToHash(poolID)}, - // }, []interface{}{&poolTokens}) - - // req.AddCall(ðrpc.Call{ - // ABI: poolABI, - // Target: poolAddress, - // Method: shared.PoolMethodGetSwapFeePercentage, - // }, []interface{}{&swapFeePercentage}) - - // req.AddCall(ðrpc.Call{ - // ABI: poolABI, - // Target: poolAddress, - // Method: shared.PoolMethodGetPausedState, - // }, []interface{}{&pausedState}) - - // res, err := req.TryBlockAndAggregate() - // if err != nil { - // logger.WithFields(logger.Fields{ - // "dexId": t.config.DexID, - // "dexType": DexType, - // "poolAddress": poolAddress, - // }).Error(err.Error()) - - // return nil, err - // } - - // swapFeePercentageU256, _ := uint256.FromBig(swapFeePercentage) - - // return &rpcRes{ - // PoolTokens: poolTokens, - // SwapFeePercentage: swapFeePercentageU256, - // PausedState: pausedState, - // BlockNumber: res.BlockNumber.Uint64(), - // }, nil - - return nil, nil -} + if hooksConfig.Data.HooksContract != (common.Address{}) { + logger.WithFields(logger.Fields{ + "dexId": t.config.DexID, + "dexType": DexType, + "poolAddress": poolAddress, + }).Warnf("this pool has a contract hook implemented at %s => should check it", hooksConfig.Data.HooksContract) + } -func isNotPaused(pausedState PausedState) bool { - return time.Now().Unix() > pausedState.BufferPeriodEndTime.Int64() || !pausedState.Paused + return &RpcResult{ + HooksConfig: shared.HooksConfig{ + EnableHookAdjustedAmounts: hooksConfig.Data.EnableHookAdjustedAmounts, + ShouldCallComputeDynamicSwapFee: hooksConfig.Data.ShouldCallComputeDynamicSwapFee, + ShouldCallBeforeSwap: hooksConfig.Data.ShouldCallBeforeSwap, + ShouldCallAfterSwap: hooksConfig.Data.ShouldCallAfterSwap, + }, + BalancesRaw: poolData.Data.BalancesRaw, + BalancesLiveScaled18: poolData.Data.BalancesLiveScaled18, + TokenRates: poolData.Data.TokenRates, + StaticSwapFeePercentage: staticSwapFeePercentage, + AggregateSwapFeePercentage: aggregateFeePercentages.AggregateSwapFeePercentage, + NormalizedWeights: normalizedWeights, + IsVaultPaused: isVaultPaused, + IsPoolPaused: isPoolPaused, + IsPoolInRecoveryMode: isPoolInRecoveryMode, + BlockNumber: res.BlockNumber.Uint64(), + }, nil } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go index 3bd697a05..354da0702 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go @@ -8,18 +8,14 @@ import ( "github.com/KyberNetwork/ethrpc" "github.com/KyberNetwork/logger" - "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/holiman/uint256" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" ) -var ErrInvalidWeight = errors.New("invalid weight") - type PoolsListUpdater struct { - config Config + config *Config ethrpcClient *ethrpc.Client sharedUpdater *shared.PoolsListUpdater } @@ -33,7 +29,7 @@ func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsList }) return &PoolsListUpdater{ - config: *config, + config: config, ethrpcClient: ethrpcClient, sharedUpdater: sharedUpdater, } @@ -51,17 +47,21 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte }).Infof("Finish updating pools list.") }() - subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) - if err != nil { - return nil, nil, err + if u.config.Factories == nil { + logger.WithFields(logger.Fields{ + "dexId": u.config.DexID, + "dexType": DexType, + }).Error("factories config is empty") + + return nil, nil, errors.New("PoolTypeByFactory config is empty") } - vaults, poolVerions, err := u.getPoolInfos(ctx, subgraphPools) + subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) if err != nil { return nil, nil, err } - pools, err := u.initPools(subgraphPools, vaults, poolVerions) + pools, err := u.initPools(subgraphPools) if err != nil { logger.WithFields(logger.Fields{ "dexId": u.config.DexID, @@ -74,120 +74,50 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte return pools, newMetadataBytes, nil } -func (u *PoolsListUpdater) getPoolInfos(ctx context.Context, subgraphPools []*shared.SubgraphPool) ([]string, []int, error) { - var ( - vaultAddresses = make([]common.Address, len(subgraphPools)) - vaults = make([]string, len(subgraphPools)) - poolInfos = make([]string, len(subgraphPools)) - poolVersions = make([]int, len(subgraphPools)) - ) - - req := u.ethrpcClient.R().SetContext(ctx) - for idx, subgraphPool := range subgraphPools { - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: subgraphPool.Address, - Method: shared.PoolMethodGetVault, - }, []interface{}{&vaultAddresses[idx]}) - req.AddCall(ðrpc.Call{ - ABI: poolABI, - Target: subgraphPool.Address, - Method: shared.PoolMethodVersion, - }, []interface{}{&poolInfos[idx]}) - } - if _, err := req.Aggregate(); err != nil { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Errorf("failed to getPoolInfos: %v", err) - return nil, nil, err - } - - for idx, addr := range vaultAddresses { - var poolInfo shared.PoolInfo - err := json.Unmarshal([]byte(poolInfos[idx]), &poolInfo) - if err != nil { +func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]entity.Pool, error) { + pools := make([]entity.Pool, 0, len(subgraphPools)) + for _, subgraphPool := range subgraphPools { + poolType, found := u.config.Factories[subgraphPool.Factory] + if !found && subgraphPool.Factory != "" { logger.WithFields(logger.Fields{ "dexId": u.config.DexID, "dexType": DexType, - }).Warnf("invalid pool version data, fallback to %v", err) - - poolInfo.Version = shared.PoolVersion1 // temporary + }).Warnf("detected a new factory that hasn't been configured : %s", subgraphPool.Factory) + continue } - poolVersions[idx] = poolInfo.Version - vaults[idx] = strings.ToLower(addr.Hex()) - } - - return vaults, poolVersions, nil -} - -func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool, vaults []string, poolVersions []int) ([]entity.Pool, error) { - pools := make([]entity.Pool, 0, len(subgraphPools)) - - for idx := range subgraphPools { - pool, err := u.initPool(subgraphPools[idx], vaults[idx], poolVersions[idx]) - if err != nil { - return nil, err - } - - pools = append(pools, pool) - } - - for idx := range subgraphPools { - pool, err := u.initPool(subgraphPools[idx], vaults[idx], poolVersions[idx]) - if err != nil { - return nil, err + if strings.EqualFold(PoolType, poolType) { + staticExtraBytes, err := json.Marshal(&StaticExtra{ + Vault: subgraphPool.Vault.ID, + }) + if err != nil { + return nil, err + } + + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + ) + for i, token := range subgraphPool.Tokens { + poolTokens[i] = &entity.PoolToken{ + Address: token.Address, + Weight: 1, + Swappable: true, + } + reserves[i] = "0" + } + + pools = append(pools, entity.Pool{ + Address: subgraphPool.Address, + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + }) } - - pools = append(pools, pool) } return pools, nil } - -func (u *PoolsListUpdater) initPool(subgraphPool *shared.SubgraphPool, vault string, poolVersion int) (entity.Pool, error) { - var ( - poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) - reserves = make([]string, len(subgraphPool.Tokens)) - scalingFactors = make([]*uint256.Int, len(subgraphPool.Tokens)) - err error - ) - - for j, token := range subgraphPool.Tokens { - scalingFactors[j], err = uint256.FromHex(token.ScalingFactor) - if err != nil { - return entity.Pool{}, err - } - - poolTokens[j] = &entity.PoolToken{ - Address: token.Address, - Decimals: uint8(token.Decimals), - Weight: 1, - Swappable: true, - } - - reserves[j] = "0" - } - - staticExtra := StaticExtra{ - PoolID: subgraphPool.ID, - PoolType: PoolType, - PoolVersion: poolVersion, - Vault: vault, - } - staticExtraBytes, err := json.Marshal(staticExtra) - if err != nil { - return entity.Pool{}, err - } - - return entity.Pool{ - Address: strings.ToLower(subgraphPool.Address), - Exchange: u.config.DexID, - Type: DexType, - Timestamp: time.Now().Unix(), - Tokens: poolTokens, - Reserves: reserves, - StaticExtra: string(staticExtraBytes), - }, nil -} diff --git a/pkg/liquidity-source/balancer-v3/weighted/type.go b/pkg/liquidity-source/balancer-v3/weighted/type.go index 571ab0c1c..ebfe95620 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/type.go +++ b/pkg/liquidity-source/balancer-v3/weighted/type.go @@ -3,45 +3,73 @@ package weighted import ( "math/big" - "github.com/ethereum/go-ethereum/common" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/holiman/uint256" ) type Extra struct { - SwapFeePercentage *uint256.Int `json:"swapFeePercentage"` - Paused bool `json:"paused"` + HooksConfig shared.HooksConfig `json:"hooksConfig"` + StaticSwapFeePercentage *uint256.Int `json:"staticSwapFeePercentage"` + AggregateSwapFeePercentage *uint256.Int `json:"aggregateSwapFeePercentage"` + NormalizedWeights []*uint256.Int `json:"normalizedWeights"` + BalancesLiveScaled18 []*uint256.Int `json:"balancesLiveScaled18"` + DecimalScalingFactors []*uint256.Int `json:"decimalScalingFactors"` + TokenRates []*uint256.Int `json:"tokenRates"` + IsVaultPaused bool `json:"isVaultPaused"` + IsPoolPaused bool `json:"isPoolPaused"` + IsPoolInRecoveryMode bool `json:"isPoolInRecoveryMode"` } type StaticExtra struct { - PoolID string `json:"poolId"` - PoolType string `json:"poolType"` - PoolVersion int `json:"poolVersion"` - ScalingFactors []*uint256.Int `json:"scalingFactors"` - Vault string `json:"vault"` + Vault string `json:"vault"` } -type PoolTokens struct { - Tokens []common.Address - Balances []*big.Int - LastChangeBlock *big.Int +type AmplificationParameter struct { + Value *big.Int + IsUpdating bool + Precision *big.Int } -type PausedState struct { - Paused bool - PauseWindowEndTime *big.Int - BufferPeriodEndTime *big.Int +type StablePoolDynamicData struct { + Data struct { + BalancesLiveScaled18 []*big.Int + TokenRates []*big.Int + StaticSwapFeePercentage *big.Int + TotalSupply *big.Int + BptRate *big.Int + AmplificationParameter *big.Int + StartValue *big.Int + EndValue *big.Int + StartTime uint32 + EndTime uint32 + IsAmpUpdating bool + IsPoolInitialized bool + IsPoolPaused bool + IsPoolInRecoveryMode bool + } } type PoolMetaInfo struct { Vault string `json:"vault"` - PoolID string `json:"poolId"` TokenOutIndex int `json:"tokenOutIndex"` BlockNumber uint64 `json:"blockNumber"` } -type rpcRes struct { - PoolTokens PoolTokens - SwapFeePercentage *uint256.Int - PausedState PausedState - BlockNumber uint64 +type RpcResult struct { + HooksConfig shared.HooksConfig + BalancesRaw []*big.Int + BalancesLiveScaled18 []*big.Int + TokenRates []*big.Int + DecimalScalingFactors []*big.Int + NormalizedWeights []*big.Int + StaticSwapFeePercentage *big.Int + AggregateSwapFeePercentage *big.Int + IsVaultPaused bool + IsPoolPaused bool + IsPoolInRecoveryMode bool + BlockNumber uint64 +} + +type SwapInfo struct { + AggregateFee *big.Int `json:"aggregateFee"` } From 71775878b607643d8792bdcf2cd76b83e53def5b Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 1 Jan 2025 20:37:23 +0700 Subject: [PATCH 15/39] tmp --- .../balancer-v3/math/log_exp_math.go | 79 ++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go index 82e86dd6a..1f15721b5 100644 --- a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go +++ b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go @@ -9,10 +9,20 @@ import ( ) var ( + iTWO = int256.NewInt(2) + iTHREE = int256.NewInt(3) + iFIVE = int256.NewInt(5) + iSEVEN = int256.NewInt(7) + iNINE = int256.NewInt(9) + iELEVEN = int256.NewInt(11) + iTHIRTEEN = int256.NewInt(13) + iFIFTEEN = int256.NewInt(15) + ONE_E20, _ = uint256.FromDecimal("100000000000000000000") // 1e20 - iONE_E17 = int256.NewInt(1e17) - iONE_E18 = int256.NewInt(1e18) + iONE_E36, _ = int256.FromDec("1000000000000000000000000000000000000") // 1e36 + iONE_E17 = int256.NewInt(1e17) + iONE_E18 = int256.NewInt(1e18) TWO_254 = new(uint256.Int).Lsh(ONE, 254) MILD_EXPONENT_BOUND = new(uint256.Int).Div(TWO_254, ONE_E20) @@ -108,7 +118,70 @@ func Pow(x, y *uint256.Int) (*uint256.Int, error) { } func Ln36(x *int256.Int) (*int256.Int, error) { - return nil, nil + x18, overflow := new(int256.Int).MulOverflow(x, iONE_E18) + if overflow { + return nil, ErrMulOverflow + } + + // z = (x - ONE_36) * ONE_36 / (x + ONE_36) + numerator := new(int256.Int).Sub(x18, iONE_E36) + numerator, overflow = numerator.MulOverflow(numerator, iONE_E36) + if overflow { + return nil, ErrMulOverflow + } + + denominator, overflow := new(int256.Int).AddOverflow(x18, iONE_E36) + if overflow { + return nil, ErrMulOverflow + } + + z := new(int256.Int).Quo(numerator, denominator) + + // z_squared = (z * z) / ONE_36 + zSquared, overflow := new(int256.Int).MulOverflow(z, z) + if overflow { + return nil, ErrMulOverflow + } + zSquared.Quo(zSquared, iONE_E36) + + // Initial term + num := new(int256.Int).Set(z) + seriesSum := new(int256.Int).Set(z) + + temp := new(int256.Int) + + // Helper function for term calculation + calculateTerm := func(divisor *int256.Int) error { + num, overflow = num.MulOverflow(num, zSquared) + if overflow { + return ErrMulOverflow + } + num.Quo(num, iONE_E36) + + temp.Set(num) + temp.Quo(temp, divisor) + + seriesSum, overflow = seriesSum.AddOverflow(seriesSum, temp) + if overflow { + return ErrAddOverflow + } + + return nil + } + + // Calculate all terms + for _, divisor := range []*int256.Int{iTHREE, iFIVE, iSEVEN, iNINE, iELEVEN, iTHIRTEEN, iFIFTEEN} { + if err := calculateTerm(divisor); err != nil { + return nil, err + } + } + + result, overflow := seriesSum.MulOverflow(seriesSum, iTWO) + if overflow { + return nil, ErrMulOverflow + } + + return result, nil } func Ln(x *int256.Int) (*int256.Int, error) { From 02f0c64701f070fe101ff1163b9b7538569066a5 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 1 Jan 2025 20:40:22 +0700 Subject: [PATCH 16/39] tmp --- .../weighted/abis/WeightedPool.json | 940 ++++++++---------- 1 file changed, 409 insertions(+), 531 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json b/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json index f2f8141e8..d561bedb7 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json +++ b/pkg/liquidity-source/balancer-v3/weighted/abis/WeightedPool.json @@ -14,9 +14,9 @@ "type": "string" }, { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" + "internalType": "uint256", + "name": "numTokens", + "type": "uint256" }, { "internalType": "uint256[]", @@ -24,19 +24,9 @@ "type": "uint256[]" }, { - "internalType": "contract IRateProvider[]", - "name": "rateProviders", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "assetManagers", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" + "internalType": "string", + "name": "version", + "type": "string" } ], "internalType": "struct WeightedPool.NewPoolParams", @@ -47,117 +37,202 @@ "internalType": "contract IVault", "name": "vault", "type": "address" - }, - { - "internalType": "contract IProtocolFeePercentagesProvider", - "name": "protocolFeeProvider", - "type": "address" - }, + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "BaseOutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "ECDSAInvalidSignature", + "type": "error" + }, + { + "inputs": [ { "internalType": "uint256", - "name": "pauseWindowDuration", + "name": "length", "type": "uint256" - }, + } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "ECDSAInvalidSignatureS", + "type": "error" + }, + { + "inputs": [ { "internalType": "uint256", - "name": "bufferPeriodDuration", + "name": "deadline", "type": "uint256" - }, + } + ], + "name": "ERC2612ExpiredSignature", + "type": "error" + }, + { + "inputs": [ { "internalType": "address", - "name": "owner", + "name": "signer", "type": "address" }, { - "internalType": "string", - "name": "version", - "type": "string" + "internalType": "address", + "name": "owner", + "type": "address" } ], - "stateMutability": "nonpayable", - "type": "constructor" + "name": "ERC2612InvalidSigner", + "type": "error" + }, + { + "inputs": [], + "name": "ExponentOutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMismatch", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, "internalType": "address", - "name": "spender", + "name": "account", "type": "address" }, { - "indexed": false, "internalType": "uint256", - "name": "value", + "name": "currentNonce", "type": "uint256" } ], - "name": "Approval", - "type": "event" + "name": "InvalidAccountNonce", + "type": "error" }, { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PausedStateChanged", - "type": "event" + "inputs": [], + "name": "InvalidExponent", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidShortString", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidToken", + "type": "error" + }, + { + "inputs": [], + "name": "MaxInRatio", + "type": "error" + }, + { + "inputs": [], + "name": "MaxOutRatio", + "type": "error" + }, + { + "inputs": [], + "name": "MinWeight", + "type": "error" + }, + { + "inputs": [], + "name": "NormalizedWeightInvariant", + "type": "error" + }, + { + "inputs": [], + "name": "ProductOutOfBounds", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "uint256", - "name": "feeType", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "protocolFeePercentage", - "type": "uint256" + "internalType": "address", + "name": "sender", + "type": "address" } ], - "name": "ProtocolFeePercentageCacheUpdated", - "type": "event" + "name": "SenderIsNotVault", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": false, - "internalType": "bool", - "name": "enabled", - "type": "bool" + "internalType": "string", + "name": "str", + "type": "string" } ], - "name": "RecoveryModeStateChanged", - "type": "event" + "name": "StringTooLong", + "type": "error" + }, + { + "inputs": [], + "name": "WeightedPoolBptRateUnsupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDivision", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroInvariant", + "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": "swapFeePercentage", + "name": "value", "type": "uint256" } ], - "name": "SwapFeePercentageChanged", + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", "type": "event" }, { @@ -187,12 +262,12 @@ }, { "inputs": [], - "name": "DELEGATE_PROTOCOL_SWAP_FEES_SENTINEL", + "name": "DOMAIN_SEPARATOR", "outputs": [ { - "internalType": "uint256", + "internalType": "bytes32", "name": "", - "type": "uint256" + "type": "bytes32" } ], "stateMutability": "view", @@ -200,7 +275,7 @@ }, { "inputs": [], - "name": "DOMAIN_SEPARATOR", + "name": "PERMIT_TYPEHASH", "outputs": [ { "internalType": "bytes32", @@ -278,64 +353,29 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { - "internalType": "address", - "name": "spender", - "type": "address" + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" }, { "internalType": "uint256", - "name": "amount", + "name": "tokenInIndex", "type": "uint256" - } - ], - "name": "decreaseAllowance", - "outputs": [ + }, { - "internalType": "bool", - "name": "", - "type": "bool" + "internalType": "uint256", + "name": "invariantRatio", + "type": "uint256" } ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "disableRecoveryMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "enableRecoveryMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getATHRateProduct", + "name": "computeBalance", "outputs": [ { "internalType": "uint256", - "name": "", + "name": "newBalance", "type": "uint256" } ], @@ -345,17 +385,22 @@ { "inputs": [ { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "enum Rounding", + "name": "rounding", + "type": "uint8" } ], - "name": "getActionId", + "name": "computeInvariant", "outputs": [ { - "internalType": "bytes32", + "internalType": "uint256", "name": "", - "type": "bytes32" + "type": "uint256" } ], "stateMutability": "view", @@ -363,82 +408,118 @@ }, { "inputs": [], - "name": "getActualSupply", + "name": "decimals", "outputs": [ { - "internalType": "uint256", + "internalType": "uint8", "name": "", - "type": "uint256" + "type": "uint8" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "getAuthorizer", + "name": "eip712Domain", "outputs": [ { - "internalType": "contract IAuthorizer", - "name": "", + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" } ], "stateMutability": "view", "type": "function" }, { - "inputs": [], - "name": "getDomainSeparator", - "outputs": [ + "inputs": [ { - "internalType": "bytes32", - "name": "", - "type": "bytes32" + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" } ], - "stateMutability": "view", + "name": "emitApproval", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [], - "name": "getInvariant", - "outputs": [ + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, { "internalType": "uint256", - "name": "", + "name": "amount", "type": "uint256" } ], - "stateMutability": "view", + "name": "emitTransfer", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], - "name": "getLastPostJoinExitInvariant", + "name": "getAggregateFeePercentages", "outputs": [ { "internalType": "uint256", - "name": "", + "name": "aggregateSwapFeePercentage", "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "getNextNonce", - "outputs": [ + }, { "internalType": "uint256", - "name": "", + "name": "aggregateYieldFeePercentage", "type": "uint256" } ], @@ -447,11 +528,11 @@ }, { "inputs": [], - "name": "getNormalizedWeights", + "name": "getCurrentLiveBalances", "outputs": [ { "internalType": "uint256[]", - "name": "", + "name": "balancesLiveScaled18", "type": "uint256[]" } ], @@ -460,62 +541,46 @@ }, { "inputs": [], - "name": "getOwner", + "name": "getMaximumInvariantRatio", "outputs": [ { - "internalType": "address", + "internalType": "uint256", "name": "", - "type": "address" + "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "getPausedState", + "name": "getMaximumSwapFeePercentage", "outputs": [ - { - "internalType": "bool", - "name": "paused", - "type": "bool" - }, { "internalType": "uint256", - "name": "pauseWindowEndTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "bufferPeriodEndTime", + "name": "", "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "getPoolId", + "name": "getMinimumInvariantRatio", "outputs": [ { - "internalType": "bytes32", + "internalType": "uint256", "name": "", - "type": "bytes32" + "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { - "inputs": [ - { - "internalType": "uint256", - "name": "feeType", - "type": "uint256" - } - ], - "name": "getProtocolFeePercentageCache", + "inputs": [], + "name": "getMinimumSwapFeePercentage", "outputs": [ { "internalType": "uint256", @@ -523,17 +588,17 @@ "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "getProtocolFeesCollector", + "name": "getNormalizedWeights", "outputs": [ { - "internalType": "contract IProtocolFeesCollector", + "internalType": "uint256[]", "name": "", - "type": "address" + "type": "uint256[]" } ], "stateMutability": "view", @@ -541,25 +606,25 @@ }, { "inputs": [], - "name": "getProtocolSwapFeeDelegation", + "name": "getRate", "outputs": [ { - "internalType": "bool", + "internalType": "uint256", "name": "", - "type": "bool" + "type": "uint256" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "getRateProviders", + "name": "getStaticSwapFeePercentage", "outputs": [ { - "internalType": "contract IRateProvider[]", + "internalType": "uint256", "name": "", - "type": "address[]" + "type": "uint256" } ], "stateMutability": "view", @@ -567,11 +632,43 @@ }, { "inputs": [], - "name": "getScalingFactors", + "name": "getTokenInfo", "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "paysYieldFees", + "type": "bool" + } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, { "internalType": "uint256[]", - "name": "", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "lastBalancesLiveScaled18", "type": "uint256[]" } ], @@ -580,12 +677,12 @@ }, { "inputs": [], - "name": "getSwapFeePercentage", + "name": "getTokens", "outputs": [ { - "internalType": "uint256", - "name": "", - "type": "uint256" + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" } ], "stateMutability": "view", @@ -606,38 +703,88 @@ }, { "inputs": [], - "name": "inRecoveryMode", + "name": "getWeightedPoolDynamicData", "outputs": [ { - "internalType": "bool", - "name": "", - "type": "bool" + "components": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "staticSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolPaused", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInRecoveryMode", + "type": "bool" + } + ], + "internalType": "struct WeightedPoolDynamicData", + "name": "data", + "type": "tuple" } ], "stateMutability": "view", "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "addedValue", - "type": "uint256" - } - ], - "name": "increaseAllowance", + "inputs": [], + "name": "getWeightedPoolImmutableData", "outputs": [ { - "internalType": "bool", - "name": "", - "type": "bool" + "components": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "normalizedWeights", + "type": "uint256[]" + } + ], + "internalType": "struct WeightedPoolImmutableData", + "name": "data", + "type": "tuple" } ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "incrementNonce", + "outputs": [], "stateMutability": "nonpayable", "type": "function" }, @@ -673,156 +820,38 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "onExitPool", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "onJoinPool", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { "components": [ { - "internalType": "enum IVault.SwapKind", + "internalType": "enum SwapKind", "name": "kind", "type": "uint8" }, - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, { "internalType": "uint256", - "name": "amount", + "name": "amountGivenScaled18", "type": "uint256" }, { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" + "internalType": "uint256[]", + "name": "balancesScaled18", + "type": "uint256[]" }, { "internalType": "uint256", - "name": "lastChangeBlock", + "name": "indexIn", "type": "uint256" }, { - "internalType": "address", - "name": "from", - "type": "address" + "internalType": "uint256", + "name": "indexOut", + "type": "uint256" }, { "internalType": "address", - "name": "to", + "name": "router", "type": "address" }, { @@ -831,19 +860,9 @@ "type": "bytes" } ], - "internalType": "struct IPoolSwapStructs.SwapRequest", + "internalType": "struct PoolSwapParams", "name": "request", "type": "tuple" - }, - { - "internalType": "uint256", - "name": "balanceTokenIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "balanceTokenOut", - "type": "uint256" } ], "name": "onSwap", @@ -854,14 +873,7 @@ "type": "uint256" } ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -878,7 +890,7 @@ }, { "internalType": "uint256", - "name": "value", + "name": "amount", "type": "uint256" }, { @@ -910,140 +922,20 @@ { "inputs": [ { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "queryExit", - "outputs": [ - { - "internalType": "uint256", - "name": "bptIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "amountsOut", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "balances", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "lastChangeBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "protocolSwapFeePercentage", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" } ], - "name": "queryJoin", + "name": "supportsInterface", "outputs": [ { - "internalType": "uint256", - "name": "bptOut", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "amountsIn", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "bytes", - "name": "poolConfig", - "type": "bytes" - } - ], - "name": "setAssetManagerPoolConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" + "internalType": "bool", + "name": "", + "type": "bool" } ], - "name": "setSwapFeePercentage", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -1076,7 +968,7 @@ "inputs": [ { "internalType": "address", - "name": "recipient", + "name": "to", "type": "address" }, { @@ -1100,12 +992,12 @@ "inputs": [ { "internalType": "address", - "name": "sender", + "name": "from", "type": "address" }, { "internalType": "address", - "name": "recipient", + "name": "to", "type": "address" }, { @@ -1125,20 +1017,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "updateProtocolFeePercentageCache", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "version", From 98e437ccc7237fbb0c01f7bd931e71c74a290d3d Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 1 Jan 2025 20:41:57 +0700 Subject: [PATCH 17/39] tmp --- pkg/liquidity-source/balancer-v3/stable/pool_simulator.go | 4 ++++ pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index f69707c47..e8236d8c3 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -6,6 +6,7 @@ import ( "github.com/goccy/go-json" "github.com/holiman/uint256" + "github.com/samber/lo" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" @@ -275,6 +276,9 @@ func (p *PoolSimulator) computeInvariant(balancesLiveScaled18 []*uint256.Int, ro func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { cloned := *p cloned.vault = p.vault.CloneState() + cloned.Info.Reserves = lo.Map(p.Info.Reserves, func(v *big.Int, i int) *big.Int { + return new(big.Int).Set(v) + }) return &cloned } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index 4af0325d1..4eb8a1ddd 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -6,6 +6,7 @@ import ( "github.com/goccy/go-json" "github.com/holiman/uint256" + "github.com/samber/lo" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" @@ -275,6 +276,9 @@ func (p *PoolSimulator) getNormalizedWeight(tokenIndex int) (*uint256.Int, error func (p *PoolSimulator) CloneState() poolpkg.IPoolSimulator { cloned := *p cloned.vault = p.vault.CloneState() + cloned.Info.Reserves = lo.Map(p.Info.Reserves, func(v *big.Int, i int) *big.Int { + return new(big.Int).Set(v) + }) return &cloned } From 5870a3fe2dee955d54947b66cabc6f6a66e0d947 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 1 Jan 2025 20:56:53 +0700 Subject: [PATCH 18/39] tmp --- pkg/liquidity-source/balancer-v3/stable/pool_simulator.go | 1 + pkg/liquidity-source/balancer-v3/vault/const.go | 1 - pkg/liquidity-source/balancer-v3/vault/vault.go | 1 + pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index e8236d8c3..1f685b6f6 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -227,6 +227,7 @@ func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} } } +// https://etherscan.io/address/0xc1d48bb722a22cc6abf19facbe27470f08b3db8c#code#F1#L169 func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (*uint256.Int, error) { invariant, err := p.computeInvariant(param.BalancesLiveScaled18, shared.ROUND_DOWN) if err != nil { diff --git a/pkg/liquidity-source/balancer-v3/vault/const.go b/pkg/liquidity-source/balancer-v3/vault/const.go index f83073a47..f3ea9fb8b 100644 --- a/pkg/liquidity-source/balancer-v3/vault/const.go +++ b/pkg/liquidity-source/balancer-v3/vault/const.go @@ -9,5 +9,4 @@ var ( MINIMUM_TRADE_AMOUNT = uint256.NewInt(1000000) MAX_FEE_PERCENTAGE, _ = uint256.FromDecimal("999999000000000000") // 99.9999e16; // 99.9999% - ) diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index 857b1d52b..b4df0c909 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -46,6 +46,7 @@ func (v *Vault) CloneState() *Vault { return &cloned } +// https://etherscan.io/address/0xbA1333333333a1BA1108E8412f11850A5C319bA9#code#F1#L197 func (v *Vault) Swap( vaultSwapParams shared.VaultSwapParams, onSwap func(param shared.PoolSwapParams) (*uint256.Int, error), diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index 4eb8a1ddd..a34197438 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -227,6 +227,7 @@ func (s *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} } } +// https://etherscan.io/address/0xb9b144b5678ff6527136b2c12a86c9ee5dd12a85#code#F1#L150 func (p *PoolSimulator) OnSwap(param shared.PoolSwapParams) (amountOutScaled18 *uint256.Int, err error) { balanceTokenInScaled18 := param.BalancesLiveScaled18[param.IndexIn] balanceTokenOutScaled18 := param.BalancesLiveScaled18[param.IndexOut] From 10ae01b1ea41989857bbed574843f3d4e76a32a9 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 10:14:31 +0700 Subject: [PATCH 19/39] tmp --- .../balancer-v3/hooks/directional_fee.go | 2 ++ .../balancer-v3/hooks/fee_taking.go | 2 ++ .../balancer-v3/hooks/stable_surge.go | 2 ++ .../balancer-v3/hooks/ve_BAL_fee_discount.go | 2 ++ .../balancer-v3/stable/config.go | 1 + .../balancer-v3/stable/pool_simulator.go | 14 ++++++++-- .../balancer-v3/stable/pool_tracker.go | 28 +++++++++++++------ .../balancer-v3/stable/pools_list_updater.go | 3 +- .../balancer-v3/stable/type.go | 3 +- .../balancer-v3/weighted/config.go | 1 + .../balancer-v3/weighted/pool_simulator.go | 10 ++++++- .../balancer-v3/weighted/pool_tracker.go | 24 +++++++++++----- .../weighted/pools_list_updater.go | 3 +- .../balancer-v3/weighted/type.go | 3 +- 14 files changed, 75 insertions(+), 23 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go index 7371ef00f..1d8e370ef 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go +++ b/pkg/liquidity-source/balancer-v3/hooks/directional_fee.go @@ -6,6 +6,8 @@ import ( "github.com/holiman/uint256" ) +const DirectionalFeeHookType = "DirectionalFeeHook" + type directionalFeeHook struct { BaseHook diff --git a/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go b/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go index 6de678e59..3e6f33b22 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go +++ b/pkg/liquidity-source/balancer-v3/hooks/fee_taking.go @@ -1 +1,3 @@ package hooks + +const FeeTakingHookType = "FeeTakingHook" diff --git a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go index 66a72fe3d..4efd9773e 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go +++ b/pkg/liquidity-source/balancer-v3/hooks/stable_surge.go @@ -1,5 +1,7 @@ package hooks +const StableSurgeHookType = "StableSurgeHook" + type StableSurgeHook struct { BaseHook } diff --git a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go index e491a3a2a..6c0860917 100644 --- a/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go +++ b/pkg/liquidity-source/balancer-v3/hooks/ve_BAL_fee_discount.go @@ -1,5 +1,7 @@ package hooks +const VeBALFeeDiscountHookType = "VeBALFeeDiscountHook" + type VeBALFeeDiscountHook struct { BaseHook } diff --git a/pkg/liquidity-source/balancer-v3/stable/config.go b/pkg/liquidity-source/balancer-v3/stable/config.go index 1ce80543c..1fdb71b2f 100644 --- a/pkg/liquidity-source/balancer-v3/stable/config.go +++ b/pkg/liquidity-source/balancer-v3/stable/config.go @@ -9,4 +9,5 @@ type Config struct { NewPoolLimit int `json:"newPoolLimit"` VaultExplorer string `json:"vaultExplorer"` Factories map[string]string `json:"factories"` + DefaultHook string `json:"defaultHook"` } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index 1f685b6f6..dc57d147d 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -63,7 +63,15 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { } // Need to detect the current hook type of pool - hook := hooks.NewBaseHook() + if staticExtra.DefaultHook != "" && !hooks.IsHookSupported(staticExtra.DefaultHook) { + logger.Warnf("[%s] defaultHook is not supported now for pool %s => fallback to BaseHook", DexType, entityPool.Address) + } + + var hook hooks.IHook + switch staticExtra.DefaultHook { + default: + hook = hooks.NewBaseHook() + } vault := vault.New(hook, extra.HooksConfig, extra.IsPoolInRecoveryMode, extra.DecimalScalingFactors, extra.TokenRates, extra.BalancesLiveScaled18, extra.StaticSwapFeePercentage, extra.AggregateSwapFeePercentage) @@ -202,7 +210,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { _, err := p.vault.UpdateLiveBalance(tokenIndexIn, amountGivenRaw, shared.ROUND_DOWN) if err != nil { - logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + logger.Warnf("[%s] failed to UpdateBalance for pool %s", DexType, p.Info.Address) return } @@ -214,7 +222,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { _, err = p.vault.UpdateLiveBalance(tokenIndexOut, amountGivenRaw, shared.ROUND_DOWN) if err != nil { - logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + logger.Warnf("[%s] failed to UpdateBalance for pool %s", DexType, p.Info.Address) return } } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index 24fbb4189..482bcae63 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -72,7 +72,7 @@ func (t *PoolTracker) getNewPoolState( }).Info("Finish updating state.") }() - res, err := t.queryRPC(ctx, p.Address, overrides) + res, shouldDisablePool, err := t.queryRPC(ctx, p.Address, overrides) if err != nil { return p, err } @@ -82,7 +82,8 @@ func (t *PoolTracker) getNewPoolState( "dexId": t.config.DexID, "dexType": DexType, "poolAddress": p.Address, - }).Error("can not fetch reserves") + }).Error("can not fetch new reserves") + return p, err } @@ -126,9 +127,17 @@ func (t *PoolTracker) getNewPoolState( p.BlockNumber = res.BlockNumber p.Extra = string(extraBytes) p.Timestamp = time.Now().Unix() - p.Reserves = lo.Map(res.TokenRates, func(v *big.Int, _ int) string { - return v.String() - }) + + // Set all reserves to 0 to disable pool temporarily + if shouldDisablePool { + p.Reserves = lo.Map(p.Reserves, func(_ string, _ int) string { + return "0" + }) + } else { + p.Reserves = lo.Map(res.BalancesRaw, func(v *big.Int, _ int) string { + return v.String() + }) + } return p, nil } @@ -137,7 +146,7 @@ func (t *PoolTracker) queryRPC( ctx context.Context, poolAddress string, overrides map[common.Address]gethclient.OverrideAccount, -) (*RpcResult, error) { +) (*RpcResult, bool, error) { var ( aggregateFeePercentages shared.AggregateFeePercentage hooksConfig shared.HooksConfigRPC @@ -217,10 +226,13 @@ func (t *PoolTracker) queryRPC( "dexType": DexType, "poolAddress": poolAddress, }).Error(err.Error()) - return nil, err + return nil, false, err } + var shouldDisablePool bool if hooksConfig.Data.HooksContract != (common.Address{}) { + shouldDisablePool = true + logger.WithFields(logger.Fields{ "dexId": t.config.DexID, "dexType": DexType, @@ -245,5 +257,5 @@ func (t *PoolTracker) queryRPC( IsPoolPaused: isPoolPaused, IsPoolInRecoveryMode: isPoolInRecoveryMode, BlockNumber: res.BlockNumber.Uint64(), - }, nil + }, shouldDisablePool, nil } diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index ec2bd0dc3..be4251f86 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -88,7 +88,8 @@ func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]en if strings.EqualFold(PoolType, poolType) { staticExtraBytes, err := json.Marshal(&StaticExtra{ - Vault: subgraphPool.Vault.ID, + Vault: subgraphPool.Vault.ID, + DefaultHook: u.config.DefaultHook, }) if err != nil { return nil, err diff --git a/pkg/liquidity-source/balancer-v3/stable/type.go b/pkg/liquidity-source/balancer-v3/stable/type.go index 738736731..84f292488 100644 --- a/pkg/liquidity-source/balancer-v3/stable/type.go +++ b/pkg/liquidity-source/balancer-v3/stable/type.go @@ -21,7 +21,8 @@ type Extra struct { } type StaticExtra struct { - Vault string `json:"vault"` + Vault string `json:"vault"` + DefaultHook string `json:"defaultHook"` } type AmplificationParameter struct { diff --git a/pkg/liquidity-source/balancer-v3/weighted/config.go b/pkg/liquidity-source/balancer-v3/weighted/config.go index f6661f901..4c48d48dc 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/config.go +++ b/pkg/liquidity-source/balancer-v3/weighted/config.go @@ -9,4 +9,5 @@ type Config struct { NewPoolLimit int `json:"newPoolLimit"` VaultExplorer string `json:"vaultExplorer"` Factories map[string]string `json:"factories"` + DefaultHook string `json:"defaultHook"` } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index a34197438..770292939 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -63,7 +63,15 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { } // Need to detect the current hook type of pool - hook := hooks.NewBaseHook() + if staticExtra.DefaultHook != "" && !hooks.IsHookSupported(staticExtra.DefaultHook) { + logger.Warnf("[%s] defaultHook is not supported now for pool %s => fallback to BaseHook", DexType, entityPool.Address) + } + + var hook hooks.IHook + switch staticExtra.DefaultHook { + default: + hook = hooks.NewBaseHook() + } vault := vault.New(hook, extra.HooksConfig, extra.IsPoolInRecoveryMode, extra.DecimalScalingFactors, extra.TokenRates, extra.BalancesLiveScaled18, extra.StaticSwapFeePercentage, extra.AggregateSwapFeePercentage) diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go index 75d4d1aa4..73160ea26 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go @@ -72,7 +72,7 @@ func (t *PoolTracker) getNewPoolState( }).Info("Finish updating state.") }() - res, err := t.queryRPC(ctx, p.Address, overrides) + res, shouldDisablePool, err := t.queryRPC(ctx, p.Address, overrides) if err != nil { return p, err } @@ -128,10 +128,17 @@ func (t *PoolTracker) getNewPoolState( p.BlockNumber = res.BlockNumber p.Extra = string(extraBytes) p.Timestamp = time.Now().Unix() - p.Reserves = lo.Map(res.TokenRates, func(v *big.Int, _ int) string { - return v.String() - }) + // Set all reserves to 0 to disable pool temporarily + if shouldDisablePool { + p.Reserves = lo.Map(p.Reserves, func(_ string, _ int) string { + return "0" + }) + } else { + p.Reserves = lo.Map(res.BalancesRaw, func(v *big.Int, _ int) string { + return v.String() + }) + } return p, nil } @@ -139,7 +146,7 @@ func (t *PoolTracker) queryRPC( ctx context.Context, poolAddress string, overrides map[common.Address]gethclient.OverrideAccount, -) (*RpcResult, error) { +) (*RpcResult, bool, error) { var ( aggregateFeePercentages shared.AggregateFeePercentage hooksConfig shared.HooksConfigRPC @@ -219,10 +226,13 @@ func (t *PoolTracker) queryRPC( "dexType": DexType, "poolAddress": poolAddress, }).Error(err.Error()) - return nil, err + return nil, false, err } + var shouldDisablePool bool if hooksConfig.Data.HooksContract != (common.Address{}) { + shouldDisablePool = true + logger.WithFields(logger.Fields{ "dexId": t.config.DexID, "dexType": DexType, @@ -247,5 +257,5 @@ func (t *PoolTracker) queryRPC( IsPoolPaused: isPoolPaused, IsPoolInRecoveryMode: isPoolInRecoveryMode, BlockNumber: res.BlockNumber.Uint64(), - }, nil + }, shouldDisablePool, nil } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go index 354da0702..d8e66fca4 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go @@ -88,7 +88,8 @@ func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]en if strings.EqualFold(PoolType, poolType) { staticExtraBytes, err := json.Marshal(&StaticExtra{ - Vault: subgraphPool.Vault.ID, + Vault: subgraphPool.Vault.ID, + DefaultHook: u.config.DefaultHook, }) if err != nil { return nil, err diff --git a/pkg/liquidity-source/balancer-v3/weighted/type.go b/pkg/liquidity-source/balancer-v3/weighted/type.go index ebfe95620..b8db3585a 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/type.go +++ b/pkg/liquidity-source/balancer-v3/weighted/type.go @@ -21,7 +21,8 @@ type Extra struct { } type StaticExtra struct { - Vault string `json:"vault"` + Vault string `json:"vault"` + DefaultHook string `json:"defaultHook"` } type AmplificationParameter struct { From eb8720582e90dcd07fe07c10f2c69643bc9a4b53 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 10:56:24 +0700 Subject: [PATCH 20/39] math for weighted pool --- .../balancer-v3/math/log_exp_math.go | 297 +++++++++++++++++- .../balancer-v3/vault/scaling_helper.go | 16 +- pkg/msgpack/register_pool_types.go | 4 + 3 files changed, 296 insertions(+), 21 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go index 1f15721b5..465f81bc8 100644 --- a/pkg/liquidity-source/balancer-v3/math/log_exp_math.go +++ b/pkg/liquidity-source/balancer-v3/math/log_exp_math.go @@ -17,18 +17,47 @@ var ( iELEVEN = int256.NewInt(11) iTHIRTEEN = int256.NewInt(13) iFIFTEEN = int256.NewInt(15) - - ONE_E20, _ = uint256.FromDecimal("100000000000000000000") // 1e20 - + iHUNDRED = int256.NewInt(100) + + // 18 decimal constants + iX0, _ = int256.FromDec("128000000000000000000") // 2ˆ7 + iA0, _ = int256.FromDec("38877084059945950922200000000000000000000000000000000000") // eˆ(x0) (no decimals) + iX1, _ = int256.FromDec("64000000000000000000") // 2^6 + iA1, _ = int256.FromDec("6235149080811616882910000000") // eˆ(x1) (no decimals) + + // 20 decimal constants + iX2, _ = int256.FromDec("3200000000000000000000") // 2^5 + iA2, _ = int256.FromDec("7896296018268069516100000000000000") // eˆ(x2) + iX3, _ = int256.FromDec("1600000000000000000000") // 2ˆ4 + iA3, _ = int256.FromDec("888611052050787263676000000") // eˆ(x3) + iX4, _ = int256.FromDec("800000000000000000000") // 2ˆ3 + iA4, _ = int256.FromDec("298095798704172827474000") // eˆ(x4) + iX5, _ = int256.FromDec("400000000000000000000") // 2ˆ2 + iA5, _ = int256.FromDec("5459815003314423907810") // eˆ(x5) + iX6, _ = int256.FromDec("200000000000000000000") // 2ˆ1 + iA6, _ = int256.FromDec("738905609893065022723") // eˆ(x6) + iX7, _ = int256.FromDec("100000000000000000000") // 2ˆ0 + iA7, _ = int256.FromDec("271828182845904523536") // eˆ(x7) + iX8, _ = int256.FromDec("50000000000000000000") // 2ˆ-1 + iA8, _ = int256.FromDec("164872127070012814685") // eˆ(x8) + iX9, _ = int256.FromDec("25000000000000000000") // 2ˆ-2 + iA9, _ = int256.FromDec("128402541668774148407") // eˆ(x9) + iX10, _ = int256.FromDec("12500000000000000000") // 2ˆ-3 + iA10, _ = int256.FromDec("113314845306682631683") // eˆ(x10) + iX11, _ = int256.FromDec("6250000000000000000") // 2ˆ-4 + iA11, _ = int256.FromDec("106449445891785942956") // eˆ(x11) + + iONE_E17 = int256.NewInt(1e17) // 1e17 + iONE_E18 = int256.NewInt(1e18) // 1e18 + iONE_E20, _ = int256.FromDec("100000000000000000000") // 1e20 iONE_E36, _ = int256.FromDec("1000000000000000000000000000000000000") // 1e36 - iONE_E17 = int256.NewInt(1e17) - iONE_E18 = int256.NewInt(1e18) - TWO_254 = new(uint256.Int).Lsh(ONE, 254) - MILD_EXPONENT_BOUND = new(uint256.Int).Div(TWO_254, ONE_E20) + ONE_E20, _ = uint256.FromDecimal("100000000000000000000") // 1e20 + TWO_254 = new(uint256.Int).Lsh(ONE, 254) // 2^254 + MILD_EXPONENT_BOUND = new(uint256.Int).Div(TWO_254, ONE_E20) // 2^254 / uint256(ONE_20) - LN_36_LOWER_BOUND = new(int256.Int).Sub(iONE_E18, iONE_E17) - LN_36_UPPER_BOUND = new(int256.Int).Add(iONE_E18, iONE_E17) + LN_36_LOWER_BOUND = new(int256.Int).Sub(iONE_E18, iONE_E17) // ONE_18 - 1e17 + LN_36_UPPER_BOUND = new(int256.Int).Add(iONE_E18, iONE_E17) // ONE_18 + 1e17 MAX_NATURAL_EXPONENT = new(int256.Int).Mul(int256.NewInt(130), iONE_E18) // 130e18 MIN_NATURAL_EXPONENT = new(int256.Int).Mul(int256.NewInt(-41), iONE_E18) // -41e18 @@ -36,6 +65,7 @@ var ( ErrBaseOutOfBounds = errors.New("Base_OutOfBounds") ErrExponentOutOfBounds = errors.New("Exponent_OutOfBounds") ErrProductOutOfBounds = errors.New("Product_OutOfBounds") + ErrInvalidExponent = errors.New("Invalid_Exponent") ) func Pow(x, y *uint256.Int) (*uint256.Int, error) { @@ -144,7 +174,6 @@ func Ln36(x *int256.Int) (*int256.Int, error) { } zSquared.Quo(zSquared, iONE_E36) - // Initial term num := new(int256.Int).Set(z) seriesSum := new(int256.Int).Set(z) @@ -184,10 +213,252 @@ func Ln36(x *int256.Int) (*int256.Int, error) { return result, nil } -func Ln(x *int256.Int) (*int256.Int, error) { - return nil, nil +func Ln(a *int256.Int) (*int256.Int, error) { + var ( + numerator = new(int256.Int) + negativeExponent = false + overflow bool + ) + + if a.Lt(iONE_E18) { + numerator, overflow = numerator.MulOverflow(iONE_E18, iONE_E18) + if overflow { + return nil, ErrMulOverflow + } + + a.Quo(numerator, a) + negativeExponent = true + } + + var ( + sum = new(int256.Int) + temp = new(int256.Int) + ) + + temp, overflow = temp.MulOverflow(iA0, iONE_E18) + if overflow { + return nil, ErrMulOverflow + } + if a.Cmp(temp) >= 0 { + a.Quo(a, iA0) + sum.Add(sum, iX0) + } + + temp, overflow = temp.MulOverflow(iA1, iONE_E18) + if overflow { + return nil, ErrMulOverflow + } + if a.Cmp(temp) >= 0 { + a.Quo(a, iA1) + sum.Add(sum, iX1) + } + + sum, overflow = sum.MulOverflow(sum, iHUNDRED) + if overflow { + return nil, ErrMulOverflow + } + a, overflow = a.MulOverflow(a, iHUNDRED) + if overflow { + return nil, ErrMulOverflow + } + + tempMul := new(int256.Int) + checkAndAdd := func(an, xn *int256.Int) error { + if a.Cmp(an) >= 0 { + tempMul, overflow = tempMul.MulOverflow(a, iONE_E20) + if overflow { + return ErrMulOverflow + } + a.Quo(tempMul, an) + sum.Add(sum, xn) + } + return nil + } + + for _, term := range [][2]*int256.Int{ + {iA2, iX2}, {iA3, iX3}, {iA4, iX4}, {iA5, iX5}, + {iA6, iX6}, {iA7, iX7}, {iA8, iX8}, {iA9, iX9}, + {iA10, iX10}, {iA11, iX11}, + } { + if err := checkAndAdd(term[0], term[1]); err != nil { + return nil, err + } + } + + aMinus := new(int256.Int) + aMinus, overflow = aMinus.SubOverflow(a, iONE_E20) + if overflow { + return nil, ErrSubOverflow + } + + aPlus := new(int256.Int) + aPlus, overflow = aPlus.AddOverflow(a, iONE_E20) + if overflow { + return nil, ErrAddOverflow + } + + numerator, overflow = numerator.MulOverflow(aMinus, iONE_E20) + if overflow { + return nil, ErrMulOverflow + } + + z := new(int256.Int).Quo(numerator, aPlus) + zSquared, overflow := new(int256.Int).MulOverflow(z, z) + if overflow { + return nil, ErrMulOverflow + } + zSquared.Quo(zSquared, iONE_E20) + + num := new(int256.Int).Set(z) + seriesSum := new(int256.Int).Set(z) + temp2 := new(int256.Int) + + // Helper function for term calculation + calculateTerm := func(divisor *int256.Int) error { + num, overflow = num.MulOverflow(num, zSquared) + if overflow { + return ErrMulOverflow + } + num.Quo(num, iONE_E20) + + temp2.Set(num) + temp2.Quo(temp2, divisor) + + seriesSum, overflow = seriesSum.AddOverflow(seriesSum, temp2) + if overflow { + return ErrAddOverflow + } + return nil + } + + for _, divisor := range []*int256.Int{iTHREE, iFIVE, iSEVEN, iNINE, iELEVEN} { + if err := calculateTerm(divisor); err != nil { + return nil, err + } + } + + seriesSum, overflow = seriesSum.MulOverflow(seriesSum, iTWO) + if overflow { + return nil, ErrMulOverflow + } + + result := new(int256.Int).Add(sum, seriesSum) + result.Quo(result, iHUNDRED) + + if negativeExponent { + result.Neg(result) + } + + return result, nil } func Exp(x *int256.Int) (*int256.Int, error) { - return nil, nil + if x.Lt(MIN_NATURAL_EXPONENT) || x.Gt(MAX_NATURAL_EXPONENT) { + return nil, ErrExponentOutOfBounds + } + + negativeExponent := false + if x.Sign() < 0 { + x.Neg(x) + negativeExponent = true + } + + var ( + temp = new(int256.Int) + firstAN = new(int256.Int).SetInt64(1) + ) + + if x.Cmp(iX0) >= 0 { + x.Sub(x, iX0) + firstAN.Set(iA0) + } else if x.Cmp(iX1) >= 0 { + x.Sub(x, iX1) + firstAN.Set(iA1) + } + + var overflow bool + x, overflow = x.MulOverflow(x, iHUNDRED) + if overflow { + return nil, ErrMulOverflow + } + + product := new(int256.Int).Set(iONE_E20) + + // Helper function for checking and multiplying + checkAndMultiply := func(xn, an *int256.Int) error { + if x.Cmp(xn) >= 0 { + x.Sub(x, xn) + temp, overflow = temp.MulOverflow(product, an) + if overflow { + return ErrMulOverflow + } + product.Quo(temp, iONE_E20) + } + return nil + } + + terms := [][2]*int256.Int{ + {iX2, iA2}, {iX3, iA3}, {iX4, iA4}, {iX5, iA5}, + {iX6, iA6}, {iX7, iA7}, {iX8, iA8}, {iX9, iA9}, + } + + for _, term := range terms { + if err := checkAndMultiply(term[0], term[1]); err != nil { + return nil, err + } + } + + seriesSum := new(int256.Int).Set(iONE_E20) + term := new(int256.Int).Set(x) + termTemp := new(int256.Int) + + // Helper function for term calculation + calculateTerm := func(n int64) error { + // term = ((term * x) / ONE_20) / n + termTemp, overflow = termTemp.MulOverflow(term, x) + if overflow { + return ErrMulOverflow + } + term.Quo(termTemp, iONE_E20) + term.Quo(term, int256.NewInt(n)) + + seriesSum, overflow = seriesSum.AddOverflow(seriesSum, term) + if overflow { + return ErrAddOverflow + } + return nil + } + + seriesSum, overflow = seriesSum.AddOverflow(seriesSum, x) + if overflow { + return nil, ErrAddOverflow + } + + for i := int64(2); i <= 12; i++ { + if err := calculateTerm(i); err != nil { + return nil, err + } + } + + temp, overflow = temp.MulOverflow(product, seriesSum) + if overflow { + return nil, ErrMulOverflow + } + temp.Quo(temp, iONE_E20) + + result, overflow := temp.MulOverflow(temp, firstAN) + if overflow { + return nil, ErrMulOverflow + } + result.Quo(result, iHUNDRED) + + if negativeExponent { + numerator, overflow := new(int256.Int).MulOverflow(iONE_E18, iONE_E18) + if overflow { + return nil, ErrMulOverflow + } + result.Quo(numerator, result) + } + + return result, nil } diff --git a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go index ff06f8a9d..0ee2b6a34 100644 --- a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go +++ b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go @@ -35,11 +35,11 @@ func toRawUndoRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uin return math.FixPoint.DivDown(amount, divisor) } -func toRawUndoRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - divisor, err := math.FixPoint.Mul(scalingFactor, tokenRate) - if err != nil { - return nil, err - } - - return math.FixPoint.DivUp(amount, divisor) -} +// func toRawUndoRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { +// divisor, err := math.FixPoint.Mul(scalingFactor, tokenRate) +// if err != nil { +// return nil, err +// } + +// return math.FixPoint.DivUp(amount, divisor) +// } diff --git a/pkg/msgpack/register_pool_types.go b/pkg/msgpack/register_pool_types.go index 6aecae749..32c11e027 100644 --- a/pkg/msgpack/register_pool_types.go +++ b/pkg/msgpack/register_pool_types.go @@ -11,6 +11,8 @@ import ( pkg_liquiditysource_balancerv2_composablestable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/composable-stable" pkg_liquiditysource_balancerv2_stable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/stable" pkg_liquiditysource_balancerv2_weighted "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/weighted" + pkg_liquiditysource_balancerv3_stable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/stable" + pkg_liquiditysource_balancerv3_weighted "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/weighted" pkg_liquiditysource_bancorv21 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bancor-v21" pkg_liquiditysource_bancorv3 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bancor-v3" pkg_liquiditysource_bebop "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bebop" @@ -144,6 +146,8 @@ func init() { msgpack.RegisterConcreteType(&pkg_liquiditysource_balancerv2_composablestable.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_balancerv2_stable.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_balancerv2_weighted.PoolSimulator{}) + msgpack.RegisterConcreteType(&pkg_liquiditysource_balancerv3_stable.PoolSimulator{}) + msgpack.RegisterConcreteType(&pkg_liquiditysource_balancerv3_weighted.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_bancorv21.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_bancorv3.PoolSimulator{}) msgpack.RegisterConcreteType(&pkg_liquiditysource_bebop.PoolSimulator{}) From 1899886d6376711928ee187bb026f1e0f7c58383 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 13:59:24 +0700 Subject: [PATCH 21/39] math for weighted pool --- .../balancer-v3/math/stable_math_test.go | 63 ++++ .../balancer-v3/math/weighted_math.go | 4 +- .../balancer-v3/math/weighted_math_test.go | 301 +++++++++--------- .../balancer-v3/weighted/pool_simulator.go | 4 +- 4 files changed, 219 insertions(+), 153 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go index 450a94238..c84e72b50 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go @@ -184,3 +184,66 @@ func TestStableMath_ComputeOutGivenExactIn(t *testing.T) { }) } } + +func TestStableMath_ComputeInGivenExactOut(t *testing.T) { + tests := []struct { + name string + amp *uint256.Int + balances []*uint256.Int + tokenIndexIn int + tokenIndexOut int + tokenAmountIn *uint256.Int + invariant *uint256.Int + expectedAmount *uint256.Int + expectedErr error + }{ + { + name: "Equal pool swap", + amp: uint256.NewInt(1000000), + balances: []*uint256.Int{ + uint256.NewInt(1000000), + uint256.NewInt(1000000), + }, + tokenIndexIn: 0, + tokenIndexOut: 1, + tokenAmountIn: uint256.NewInt(100), + invariant: uint256.NewInt(2000000), + expectedAmount: uint256.NewInt(100), + expectedErr: nil, + }, + { + name: "Imbalanced pool swap", + amp: uint256.NewInt(100), + balances: []*uint256.Int{ + uint256.NewInt(1500000), + uint256.NewInt(500000), + }, + tokenIndexIn: 0, + tokenIndexOut: 1, + tokenAmountIn: uint256.NewInt(100), + invariant: uint256.NewInt(1000000), + expectedAmount: uint256.NewInt(352455), + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + amount, err := StableMath.ComputeOutGivenExactIn( + tt.amp, + tt.balances, + tt.tokenIndexIn, + tt.tokenIndexOut, + tt.tokenAmountIn, + tt.invariant, + ) + + if tt.expectedErr != nil { + assert.Equal(t, tt.expectedErr, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedAmount, amount) + } + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math.go b/pkg/liquidity-source/balancer-v3/math/weighted_math.go index c6a15aae2..50a4128e9 100644 --- a/pkg/liquidity-source/balancer-v3/math/weighted_math.go +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math.go @@ -39,7 +39,7 @@ func (s *weightedMath) ComputeOutGivenExactIn( // wO = weightOut // **********************************************************************************************/ - balanceInApplyRate, err := FixPoint.MulDown(amountIn, MAX_IN_RATIO) + balanceInApplyRate, err := FixPoint.MulDown(balanceIn, MAX_IN_RATIO) if err != nil { return nil, err } @@ -88,7 +88,7 @@ func (s *weightedMath) ComputeInGivenExactOut( // wO = weightOut // **********************************************************************************************/ - balanceOutApplyRate, err := FixPoint.MulDown(amountOut, MAX_OUT_RATIO) + balanceOutApplyRate, err := FixPoint.MulDown(balanceOut, MAX_OUT_RATIO) if err != nil { return nil, err } diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go index f91cab3ae..c08a7e853 100644 --- a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go @@ -1,151 +1,154 @@ package math -// func TestCalcOutGivenIn(t *testing.T) { -// t.Run("1.should return OK", func(t *testing.T) { -// // input -// balanceIn := uint256.MustFromDecimal("2133741937219414819371293") -// weightIn := uint256.MustFromDecimal("10") -// balanceOut := uint256.MustFromDecimal("548471973423647283412313") -// weightOut := uint256.MustFromDecimal("20") -// amountIn := uint256.MustFromDecimal("21481937129313123729") - -// // expected -// expected := "2760912942840907991" - -// // calculation -// result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - -// // assert -// assert.Nil(t, err) -// assert.Equal(t, expected, result.Dec()) -// }) - -// t.Run("2.should return OK", func(t *testing.T) { -// // input -// balanceIn := uint256.MustFromDecimal("92174932794319461529478329") -// weightIn := uint256.MustFromDecimal("15") -// balanceOut := uint256.MustFromDecimal("2914754379179427149231562") -// weightOut := uint256.MustFromDecimal("5") -// amountIn := uint256.MustFromDecimal("14957430248210") - -// // expected -// expected := "1389798609308" - -// // calculation -// result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - -// // assert -// assert.Nil(t, err) -// assert.Equal(t, expected, result.Dec()) -// }) - -// t.Run("3.should return OK", func(t *testing.T) { -// // input -// balanceIn := uint256.MustFromDecimal("28430120665864259131432") -// weightIn := uint256.MustFromDecimal("100000000000000000") -// balanceOut := uint256.MustFromDecimal("10098902157921113397") -// weightOut := uint256.MustFromDecimal("30000000000000000") -// amountIn := uint256.MustFromDecimal("6125185803357185587126") - -// // expected -// expected := "4828780052665314529" - -// // calculation -// result, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - -// // assert -// assert.Nil(t, err) -// assert.Equal(t, expected, result.Dec()) -// }) - -// t.Run("4.should return error exceed amount in ratio", func(t *testing.T) { -// // input -// balanceIn := uint256.MustFromDecimal("92174932794319461529478329") -// weightIn := uint256.MustFromDecimal("15") -// balanceOut := uint256.MustFromDecimal("2914754379179427149231562") -// weightOut := uint256.MustFromDecimal("5") -// amountIn := uint256.MustFromDecimal("92174932794319461529478329") - -// // calculation -// _, err := WeightedMath.CalcOutGivenIn(balanceIn, weightIn, balanceOut, weightOut, amountIn) - -// // assert -// assert.ErrorIs(t, err, ErrMaxInRatio) -// }) -// } - -// func Test_weightedMath_CalcInGivenOut(t *testing.T) { -// type args struct { -// balanceIn *uint256.Int -// weightIn *uint256.Int -// balanceOut *uint256.Int -// weightOut *uint256.Int -// amountOut *uint256.Int -// } -// tests := []struct { -// name string -// args args -// want *uint256.Int -// wantErr error -// }{ -// { -// name: "1. should return OK", -// args: args{ -// balanceIn: uint256.MustFromDecimal("2133741937219414819371293"), -// weightIn: uint256.MustFromDecimal("10"), -// balanceOut: uint256.MustFromDecimal("548471973423647283412313"), -// weightOut: uint256.MustFromDecimal("20"), -// amountOut: uint256.MustFromDecimal("21481937129313123729"), -// }, -// want: uint256.MustFromDecimal("167153858139050441751"), -// wantErr: nil, -// }, -// { -// name: "2. should return OK", -// args: args{ -// balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), -// weightIn: uint256.MustFromDecimal("15"), -// balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), -// weightOut: uint256.MustFromDecimal("5"), -// amountOut: uint256.MustFromDecimal("14957430248210"), -// }, -// want: uint256.MustFromDecimal("158591027569670"), -// wantErr: nil, -// }, -// { -// name: "3. should return OK", -// args: args{ -// balanceIn: uint256.MustFromDecimal("28430120665864259131432"), -// weightIn: uint256.MustFromDecimal("100000000000000000"), -// balanceOut: uint256.MustFromDecimal("100989021579211133970000"), -// weightOut: uint256.MustFromDecimal("30000000000000000"), -// amountOut: uint256.MustFromDecimal("6125185803357185587126"), -// }, -// want: uint256.MustFromDecimal("538695515311227973058"), -// wantErr: nil, -// }, -// { -// name: "4. should return error exceed amount out ratio", -// args: args{ -// balanceIn: uint256.MustFromDecimal("92174932794319461529478329"), -// weightIn: uint256.MustFromDecimal("15"), -// balanceOut: uint256.MustFromDecimal("2914754379179427149231562"), -// weightOut: uint256.MustFromDecimal("5"), -// amountOut: uint256.MustFromDecimal("2914754379179427149231562"), -// }, -// want: nil, -// wantErr: ErrMaxOutRatio, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// l := &weightedMath{} -// got, err := l.CalcInGivenOut(tt.args.balanceIn, tt.args.weightIn, tt.args.balanceOut, tt.args.weightOut, tt.args.amountOut) -// if err != nil { -// assert.ErrorIsf(t, err, tt.wantErr, "weightedMath.CalcInGivenOut() error = %v, wantErr %v", err, tt.wantErr) -// return -// } -// assert.Equalf(t, tt.want, got, "weightedMath.CalcInGivenOut() = %v, want %v", got, tt.want) -// }) -// } -// } +import ( + "testing" + + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" +) + +func TestWeightedMath_ComputeOutGivenExactIn(t *testing.T) { + tests := []struct { + name string + balanceIn *uint256.Int + weightIn *uint256.Int + balanceOut *uint256.Int + weightOut *uint256.Int + amountIn *uint256.Int + expectedAmount *uint256.Int + expectedErr error + }{ + { + name: "Normal case", + balanceIn: uint256.MustFromDecimal("48251596353174400713087"), + weightIn: uint256.NewInt(500000000000000000), + balanceOut: uint256.MustFromDecimal("10798853824000000000000"), + weightOut: uint256.NewInt(500000000000000000), + amountIn: uint256.NewInt(1e18), + expectedAmount: uint256.NewInt(223798399260422100), + expectedErr: nil, + }, + { + name: "AmountIn exceeds MAX_IN_RATIO", + balanceIn: uint256.NewInt(1000), + weightIn: uint256.NewInt(50), + balanceOut: uint256.NewInt(500), + weightOut: uint256.NewInt(50), + amountIn: uint256.NewInt(301), // Exceeds MAX_IN_RATIO = 30% of balanceIn + expectedAmount: nil, + expectedErr: ErrMaxInRatio, + }, + { + name: "Zero amountIn", + balanceIn: uint256.NewInt(1000), + weightIn: uint256.NewInt(50), + balanceOut: uint256.NewInt(500), + weightOut: uint256.NewInt(50), + amountIn: uint256.NewInt(0), + expectedAmount: uint256.NewInt(0), + expectedErr: nil, + }, + { + name: "Invalid weights (weightOut == 0)", + balanceIn: uint256.NewInt(1000), + weightIn: uint256.NewInt(50), + balanceOut: uint256.NewInt(500), + weightOut: uint256.NewInt(0), + amountIn: uint256.NewInt(10), + expectedAmount: uint256.NewInt(0), + expectedErr: ErrZeroDivision, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + amount, err := WeightedMath.ComputeOutGivenExactIn( + tt.balanceIn, + tt.weightIn, + tt.balanceOut, + tt.weightOut, + tt.amountIn, + ) + + if tt.expectedErr != nil { + assert.Equal(t, tt.expectedErr, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedAmount, amount) + } + }) + } +} + +func TestWeightedMath_ComputeInGivenExactOut(t *testing.T) { + tests := []struct { + name string + balanceIn *uint256.Int + weightIn *uint256.Int + balanceOut *uint256.Int + weightOut *uint256.Int + amountOut *uint256.Int + expectedAmount *uint256.Int + expectedErr error + }{ + { + name: "Normal case", + balanceIn: uint256.MustFromDecimal("48251596353174400713087"), + weightIn: uint256.NewInt(500000000000000000), + balanceOut: uint256.MustFromDecimal("10798853824000000000000"), + weightOut: uint256.NewInt(500000000000000000), + amountOut: uint256.NewInt(1e17), + expectedAmount: uint256.NewInt(446825598023519286), + expectedErr: nil, + }, + { + name: "AmountOut exceeds MAX_OUT_RATIO", + balanceIn: uint256.NewInt(1000), + weightIn: uint256.NewInt(50), + balanceOut: uint256.NewInt(1000), + weightOut: uint256.NewInt(50), + amountOut: uint256.NewInt(301), // Exceeds MAX_OUT_RATIO = 30% of balanceOut + expectedAmount: nil, + expectedErr: ErrMaxOutRatio, + }, + { + name: "Zero amountOut", + balanceIn: uint256.NewInt(1000), + weightIn: uint256.NewInt(50), + balanceOut: uint256.NewInt(500), + weightOut: uint256.NewInt(50), + amountOut: uint256.NewInt(0), + expectedAmount: uint256.NewInt(0), + expectedErr: nil, + }, + { + name: "Invalid weights (weightIn == 0)", + balanceIn: uint256.NewInt(1000), + weightIn: uint256.NewInt(0), + balanceOut: uint256.NewInt(500), + weightOut: uint256.NewInt(50), + amountOut: uint256.NewInt(10), + expectedAmount: uint256.NewInt(0), + expectedErr: ErrZeroDivision, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + amount, err := WeightedMath.ComputeInGivenExactOut( + tt.balanceIn, + tt.weightIn, + tt.balanceOut, + tt.weightOut, + tt.amountOut, + ) + + if tt.expectedErr != nil { + assert.Equal(t, tt.expectedErr, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedAmount, amount) + } + }) + } +} diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index 770292939..eff3739d0 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -210,7 +210,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { _, err := p.vault.UpdateLiveBalance(tokenIndexIn, amountGivenRaw, shared.ROUND_DOWN) if err != nil { - logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + logger.Warnf("[%s] failed to UpdateBalance for pool %s", DexType, p.Info.Address) return } @@ -222,7 +222,7 @@ func (p *PoolSimulator) UpdateBalance(params poolpkg.UpdateBalanceParams) { _, err = p.vault.UpdateLiveBalance(tokenIndexOut, amountGivenRaw, shared.ROUND_DOWN) if err != nil { - logger.Warnf("[%s] failed to UpdateBalance for %v pool", DexType, p.Info.Address) + logger.Warnf("[%s] failed to UpdateBalance for pool %s", DexType, p.Info.Address) return } } From 3921ad372f47b9a8cdddf692b81b1e2129108ed9 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 14:01:42 +0700 Subject: [PATCH 22/39] update --- .../balancer-v3/stable/pool_simulator.go | 9 ++------- .../balancer-v3/weighted/pool_simulator.go | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index dc57d147d..38669f030 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -1,7 +1,6 @@ package stable import ( - "errors" "math/big" "github.com/goccy/go-json" @@ -19,12 +18,6 @@ import ( "github.com/KyberNetwork/logger" ) -var ( - ErrInvalidSwapFeePercentage = errors.New("invalid swap fee percentage") - ErrInvalidAmp = errors.New("invalid amp") - ErrNotTwoTokens = errors.New("not two tokens") -) - type PoolSimulator struct { poolpkg.Pool @@ -69,6 +62,8 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { var hook hooks.IHook switch staticExtra.DefaultHook { + case hooks.DirectionalFeeHookType: + hook = hooks.NewDirectionalFeeHook(extra.StaticSwapFeePercentage) default: hook = hooks.NewBaseHook() } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index eff3739d0..f83f22de0 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -1,7 +1,6 @@ package weighted import ( - "errors" "math/big" "github.com/goccy/go-json" @@ -19,12 +18,6 @@ import ( "github.com/KyberNetwork/logger" ) -var ( - ErrInvalidSwapFeePercentage = errors.New("invalid swap fee percentage") - ErrInvalidAmp = errors.New("invalid amp") - ErrNotTwoTokens = errors.New("not two tokens") -) - type PoolSimulator struct { poolpkg.Pool @@ -69,6 +62,8 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { var hook hooks.IHook switch staticExtra.DefaultHook { + case hooks.DirectionalFeeHookType: + hook = hooks.NewDirectionalFeeHook(extra.StaticSwapFeePercentage) default: hook = hooks.NewBaseHook() } From 82f206c604e4975cf1b028187a1b5103b35658e4 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 14:21:59 +0700 Subject: [PATCH 23/39] update --- .../balancer-v3/stable/config.go | 14 ++-- .../balancer-v3/stable/pool_tracker.go | 1 + .../balancer-v3/stable/pools_list_updater.go | 71 +++++++++--------- .../balancer-v3/weighted/config.go | 14 ++-- .../balancer-v3/weighted/pool_tracker.go | 1 + .../weighted/pools_list_updater.go | 72 +++++++++---------- 6 files changed, 81 insertions(+), 92 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/config.go b/pkg/liquidity-source/balancer-v3/stable/config.go index 1fdb71b2f..54c99c0cc 100644 --- a/pkg/liquidity-source/balancer-v3/stable/config.go +++ b/pkg/liquidity-source/balancer-v3/stable/config.go @@ -3,11 +3,11 @@ package stable import "net/http" type Config struct { - DexID string `json:"dexID"` - SubgraphAPI string `json:"subgraphAPI"` - SubgraphHeaders http.Header `json:"subgraphHeaders"` - NewPoolLimit int `json:"newPoolLimit"` - VaultExplorer string `json:"vaultExplorer"` - Factories map[string]string `json:"factories"` - DefaultHook string `json:"defaultHook"` + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` + VaultExplorer string `json:"vaultExplorer"` + Factory string `json:"factory"` + DefaultHook string `json:"defaultHook"` } diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go index 482bcae63..5ea29f232 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_tracker.go @@ -250,6 +250,7 @@ func (t *PoolTracker) queryRPC( BalancesRaw: poolData.Data.BalancesRaw, BalancesLiveScaled18: poolData.Data.BalancesLiveScaled18, TokenRates: poolData.Data.TokenRates, + DecimalScalingFactors: poolData.Data.DecimalScalingFactors, StaticSwapFeePercentage: staticSwapFeePercentage, AggregateSwapFeePercentage: aggregateFeePercentages.AggregateSwapFeePercentage, AmplificationParameter: amplificationParameter.Value, diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index be4251f86..134bb0666 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -3,7 +3,6 @@ package stable import ( "context" "errors" - "strings" "time" "github.com/KyberNetwork/ethrpc" @@ -47,13 +46,13 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte }).Infof("Finish updating pools list.") }() - if u.config.Factories == nil { + if u.config.Factory == "" { logger.WithFields(logger.Fields{ "dexId": u.config.DexID, "dexType": DexType, - }).Error("factories config is empty") + }).Error("factory config is empty") - return nil, nil, errors.New("PoolTypeByFactory config is empty") + return nil, nil, errors.New("factory config is empty") } subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) @@ -77,47 +76,41 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]entity.Pool, error) { pools := make([]entity.Pool, 0, len(subgraphPools)) for _, subgraphPool := range subgraphPools { - poolType, found := u.config.Factories[subgraphPool.Factory] - if !found && subgraphPool.Factory != "" { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Warnf("detected a new factory that hasn't been configured : %s", subgraphPool.Factory) + if subgraphPool.Factory != u.config.Factory { continue } + staticExtraBytes, err := json.Marshal(&StaticExtra{ + Vault: subgraphPool.Vault.ID, + DefaultHook: u.config.DefaultHook, + }) + if err != nil { + return nil, err + } - if strings.EqualFold(PoolType, poolType) { - staticExtraBytes, err := json.Marshal(&StaticExtra{ - Vault: subgraphPool.Vault.ID, - DefaultHook: u.config.DefaultHook, - }) - if err != nil { - return nil, err - } + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + ) - var ( - poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) - reserves = make([]string, len(subgraphPool.Tokens)) - ) - for i, token := range subgraphPool.Tokens { - poolTokens[i] = &entity.PoolToken{ - Address: token.Address, - Weight: 1, - Swappable: true, - } - reserves[i] = "0" + for i, token := range subgraphPool.Tokens { + poolTokens[i] = &entity.PoolToken{ + Address: token.Address, + Weight: 1, + Swappable: true, } - - pools = append(pools, entity.Pool{ - Address: subgraphPool.Address, - Exchange: u.config.DexID, - Type: DexType, - Timestamp: time.Now().Unix(), - Tokens: poolTokens, - Reserves: reserves, - StaticExtra: string(staticExtraBytes), - }) + reserves[i] = "0" } + + pools = append(pools, entity.Pool{ + Address: subgraphPool.Address, + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + }) + } return pools, nil diff --git a/pkg/liquidity-source/balancer-v3/weighted/config.go b/pkg/liquidity-source/balancer-v3/weighted/config.go index 4c48d48dc..f801bf542 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/config.go +++ b/pkg/liquidity-source/balancer-v3/weighted/config.go @@ -3,11 +3,11 @@ package weighted import "net/http" type Config struct { - DexID string `json:"dexID"` - SubgraphAPI string `json:"subgraphAPI"` - SubgraphHeaders http.Header `json:"subgraphHeaders"` - NewPoolLimit int `json:"newPoolLimit"` - VaultExplorer string `json:"vaultExplorer"` - Factories map[string]string `json:"factories"` - DefaultHook string `json:"defaultHook"` + DexID string `json:"dexID"` + SubgraphAPI string `json:"subgraphAPI"` + SubgraphHeaders http.Header `json:"subgraphHeaders"` + NewPoolLimit int `json:"newPoolLimit"` + VaultExplorer string `json:"vaultExplorer"` + Factory string `json:"factory"` + DefaultHook string `json:"defaultHook"` } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go index 73160ea26..08d120415 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_tracker.go @@ -250,6 +250,7 @@ func (t *PoolTracker) queryRPC( BalancesRaw: poolData.Data.BalancesRaw, BalancesLiveScaled18: poolData.Data.BalancesLiveScaled18, TokenRates: poolData.Data.TokenRates, + DecimalScalingFactors: poolData.Data.DecimalScalingFactors, StaticSwapFeePercentage: staticSwapFeePercentage, AggregateSwapFeePercentage: aggregateFeePercentages.AggregateSwapFeePercentage, NormalizedWeights: normalizedWeights, diff --git a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go index d8e66fca4..99c67ee91 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go @@ -3,7 +3,6 @@ package weighted import ( "context" "errors" - "strings" "time" "github.com/KyberNetwork/ethrpc" @@ -47,13 +46,13 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte }).Infof("Finish updating pools list.") }() - if u.config.Factories == nil { + if u.config.Factory == "" { logger.WithFields(logger.Fields{ "dexId": u.config.DexID, "dexType": DexType, - }).Error("factories config is empty") + }).Error("factory config is empty") - return nil, nil, errors.New("PoolTypeByFactory config is empty") + return nil, nil, errors.New("factory config is empty") } subgraphPools, newMetadataBytes, err := u.sharedUpdater.GetNewPools(ctx, metadataBytes) @@ -77,47 +76,42 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]entity.Pool, error) { pools := make([]entity.Pool, 0, len(subgraphPools)) for _, subgraphPool := range subgraphPools { - poolType, found := u.config.Factories[subgraphPool.Factory] - if !found && subgraphPool.Factory != "" { - logger.WithFields(logger.Fields{ - "dexId": u.config.DexID, - "dexType": DexType, - }).Warnf("detected a new factory that hasn't been configured : %s", subgraphPool.Factory) + if subgraphPool.Factory != u.config.Factory { continue } - if strings.EqualFold(PoolType, poolType) { - staticExtraBytes, err := json.Marshal(&StaticExtra{ - Vault: subgraphPool.Vault.ID, - DefaultHook: u.config.DefaultHook, - }) - if err != nil { - return nil, err - } + staticExtraBytes, err := json.Marshal(&StaticExtra{ + Vault: subgraphPool.Vault.ID, + DefaultHook: u.config.DefaultHook, + }) + if err != nil { + return nil, err + } - var ( - poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) - reserves = make([]string, len(subgraphPool.Tokens)) - ) - for i, token := range subgraphPool.Tokens { - poolTokens[i] = &entity.PoolToken{ - Address: token.Address, - Weight: 1, - Swappable: true, - } - reserves[i] = "0" - } + var ( + poolTokens = make([]*entity.PoolToken, len(subgraphPool.Tokens)) + reserves = make([]string, len(subgraphPool.Tokens)) + ) - pools = append(pools, entity.Pool{ - Address: subgraphPool.Address, - Exchange: u.config.DexID, - Type: DexType, - Timestamp: time.Now().Unix(), - Tokens: poolTokens, - Reserves: reserves, - StaticExtra: string(staticExtraBytes), - }) + for i, token := range subgraphPool.Tokens { + poolTokens[i] = &entity.PoolToken{ + Address: token.Address, + Weight: 1, + Swappable: true, + } + reserves[i] = "0" } + + pools = append(pools, entity.Pool{ + Address: subgraphPool.Address, + Exchange: u.config.DexID, + Type: DexType, + Timestamp: time.Now().Unix(), + Tokens: poolTokens, + Reserves: reserves, + StaticExtra: string(staticExtraBytes), + }) + } return pools, nil From 5934d94147b3aed434219c131849469133ccc00a Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 14:38:46 +0700 Subject: [PATCH 24/39] update --- .../balancer-v3/shared/pools_list_updater.go | 2 ++ pkg/liquidity-source/balancer-v3/shared/subgraph.go | 7 ++++--- .../balancer-v3/stable/pools_list_updater.go | 4 +--- .../balancer-v3/weighted/pools_list_updater.go | 5 +---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go index 7cf4460c6..e14230662 100644 --- a/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/shared/pools_list_updater.go @@ -23,6 +23,7 @@ type ( SubgraphAPI string SubgraphHeaders http.Header NewPoolLimit int + Factory string } Metadata struct { @@ -81,6 +82,7 @@ func (u *PoolsListUpdater) querySubgraph(ctx context.Context, lastBlockTimestamp } query := BuildSubgraphPoolsQuery( + u.config.Factory, lastBlockTimestamp, u.config.NewPoolLimit, 0, diff --git a/pkg/liquidity-source/balancer-v3/shared/subgraph.go b/pkg/liquidity-source/balancer-v3/shared/subgraph.go index 4a5092d20..1813d5c47 100644 --- a/pkg/liquidity-source/balancer-v3/shared/subgraph.go +++ b/pkg/liquidity-source/balancer-v3/shared/subgraph.go @@ -3,13 +3,13 @@ package shared import ( "fmt" "math/big" + "strings" ) type SubgraphPool struct { ID string `json:"id"` Address string `json:"address"` BlockTimestamp string `json:"blockTimestamp"` - Factory string `json:"factory"` Tokens []struct { Address string `json:"address"` Decimals int `json:"decimals"` @@ -20,6 +20,7 @@ type SubgraphPool struct { } func BuildSubgraphPoolsQuery( + factory string, lastBlockTimestamp *big.Int, first int, skip int, @@ -27,6 +28,7 @@ func BuildSubgraphPoolsQuery( q := `{ pools( where : { + factory: "%s", blockTimestamp_gte: %v }, first: %d, @@ -37,7 +39,6 @@ func BuildSubgraphPoolsQuery( id address blockTimestamp - factory tokens { address decimals @@ -48,5 +49,5 @@ func BuildSubgraphPoolsQuery( } }` - return fmt.Sprintf(q, lastBlockTimestamp, first, skip) + return fmt.Sprintf(q, strings.ToLower(factory), lastBlockTimestamp, first, skip) } diff --git a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go index 134bb0666..33db1e0b4 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/stable/pools_list_updater.go @@ -25,6 +25,7 @@ func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsList SubgraphAPI: config.SubgraphAPI, SubgraphHeaders: config.SubgraphHeaders, NewPoolLimit: config.NewPoolLimit, + Factory: config.Factory, }) return &PoolsListUpdater{ @@ -76,9 +77,6 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]entity.Pool, error) { pools := make([]entity.Pool, 0, len(subgraphPools)) for _, subgraphPool := range subgraphPools { - if subgraphPool.Factory != u.config.Factory { - continue - } staticExtraBytes, err := json.Marshal(&StaticExtra{ Vault: subgraphPool.Vault.ID, DefaultHook: u.config.DefaultHook, diff --git a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go index 99c67ee91..404ab5425 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pools_list_updater.go @@ -25,6 +25,7 @@ func NewPoolsListUpdater(config *Config, ethrpcClient *ethrpc.Client) *PoolsList SubgraphAPI: config.SubgraphAPI, SubgraphHeaders: config.SubgraphHeaders, NewPoolLimit: config.NewPoolLimit, + Factory: config.Factory, }) return &PoolsListUpdater{ @@ -76,10 +77,6 @@ func (u *PoolsListUpdater) GetNewPools(ctx context.Context, metadataBytes []byte func (u *PoolsListUpdater) initPools(subgraphPools []*shared.SubgraphPool) ([]entity.Pool, error) { pools := make([]entity.Pool, 0, len(subgraphPools)) for _, subgraphPool := range subgraphPools { - if subgraphPool.Factory != u.config.Factory { - continue - } - staticExtraBytes, err := json.Marshal(&StaticExtra{ Vault: subgraphPool.Vault.ID, DefaultHook: u.config.DefaultHook, From 8181a7ac147b97ae4a4f4560c037920e04914538 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 16:42:03 +0700 Subject: [PATCH 25/39] update unit tests --- .../balancer-v3/stable/pool_simulator_test.go | 544 +++++++++++------- .../balancer-v3/vault/error.go | 3 +- .../balancer-v3/vault/scaling_helper.go | 10 +- .../balancer-v3/vault/vault.go | 6 +- .../weighted/pool_simulator_test.go | 506 ++++++++-------- pkg/pooltypes/pooltypes.go | 4 +- 6 files changed, 609 insertions(+), 464 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index cf0614084..e6ed8120e 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -10,7 +10,6 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" @@ -18,20 +17,18 @@ import ( ) func TestCalcAmountOut(t *testing.T) { - t.Run("1. should return error balance didnt converge", func(t *testing.T) { - reserves := make([]*big.Int, 3) - reserves[0], _ = new(big.Int).SetString("9999991000000000000", 10) - reserves[1], _ = new(big.Int).SetString("99999910000000000056", 10) - reserves[2], _ = new(big.Int).SetString("8897791020011100123456", 10) + t.Run("1. Swap from token 0 to token 1 successful", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ Reserves: reserves, Tokens: []string{ - "0xdac17f958d2ee523a2206206994597c13d831ec7", - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "0x6b175474e89094c44da98b954eedeac495271d0f", + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", }, }, }, @@ -39,42 +36,49 @@ func TestCalcAmountOut(t *testing.T) { hooks.NewBaseHook(), shared.HooksConfig{}, false, // isPoolInRecoveryMode - []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors - []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates - []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(30000000000000), // swapFeePercentage + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - currentAmp: uint256.NewInt(1000000), + currentAmp: uint256.NewInt(5000000), } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: new(big.Int).SetUint64(99999910000000), + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(1e18), } - tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" - _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + + expectedAmountOut := "1000347432097063736" + expectedSwapFee := "23602591917350" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - assert.ErrorIs(t, err, math.ErrStableComputeBalanceDidNotConverge) + + assert.Nil(t, err) + + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) - t.Run("2. should return OK", func(t *testing.T) { - // input + t.Run("2. Swap from token 1 to token 0 successful", func(t *testing.T) { reserves := make([]*big.Int, 2) - reserves[0], _ = new(big.Int).SetString("151090057727415359409", 10) - reserves[1], _ = new(big.Int).SetString("249356634133584290044", 10) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ Reserves: reserves, Tokens: []string{ - "0x5F9D59db355b4A60501544637b00e94082cA575b", - "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8", + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", }, }, }, @@ -82,23 +86,23 @@ func TestCalcAmountOut(t *testing.T) { hooks.NewBaseHook(), shared.HooksConfig{}, false, // isPoolInRecoveryMode - []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors - []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates - []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(30000000000000), - uint256.NewInt(500000000000000000), + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - currentAmp: uint256.NewInt(1000000), + currentAmp: uint256.NewInt(5000000), } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x5F9D59db355b4A60501544637b00e94082cA575b", - Amount: new(big.Int).SetUint64(10000000), + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(1e18), } - tokenOut := "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8" + tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - expectedAmountOut := "9999700" - expectedSwapFee := "300" + expectedAmountOut := "999611352568542440" + expectedSwapFee := "16947291272107" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ @@ -106,27 +110,25 @@ func TestCalcAmountOut(t *testing.T) { TokenOut: tokenOut, }) }) + assert.Nil(t, err) assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) - t.Run("3. should return OK", func(t *testing.T) { - // input - reserves := make([]*big.Int, 3) - reserves[0], _ = new(big.Int).SetString("9999991000000000013314124321", 10) - reserves[1], _ = new(big.Int).SetString("9999991000000123120010005613", 10) - reserves[2], _ = new(big.Int).SetString("1328897131447911102200123456", 10) + t.Run("3. AmountIn is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ Reserves: reserves, Tokens: []string{ - "0xdac17f958d2ee523a2206206994597c13d831ec7", - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "0x6b175474e89094c44da98b954eedeac495271d0f", + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", }, }, }, @@ -134,68 +136,80 @@ func TestCalcAmountOut(t *testing.T) { hooks.NewBaseHook(), shared.HooksConfig{}, false, // isPoolInRecoveryMode - []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors - []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates - []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(30000000000000), - uint256.NewInt(500000000000000000), + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - currentAmp: uint256.NewInt(1000000), + currentAmp: uint256.NewInt(5000000), } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: new(big.Int).SetUint64(12111222333444555666), + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(799999), // less than MINIMUM_TRADE_AMOUNT (1000000) } - tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" + tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - // expected - expected := "590000000000000000" + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) - // actual - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountInTooSmall, err) + }) + + t.Run("4. AmountOut is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, + }, + }, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + currentAmp: uint256.NewInt(5000000), + } + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(991000), // less than MINIMUM_TRADE_AMOUNT (1000000) + } + tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" + + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - assert.Nil(t, err) - // assert - assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountOutTooSmall, err) }) - t.Run("4. should return OK", func(t *testing.T) { - poolStr := `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-stable", - "type": "balancer-v2-stable", - "timestamp": 1703667290, - "reserves": [ - "1152882153159026494", - "873225053252443292" - ], - "tokens": [ - { - "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }` + t.Run("5. should return OK", func(t *testing.T) { + poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735803357,"reserves":["687804073931103275644","1783969556654743519024"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"694069210892948295209\",\"2124492373418339554414\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009108897721464489\",\"1190879275654308905\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21535329}` + var pool entity.Pool err := json.Unmarshal([]byte(poolStr), &pool) assert.Nil(t, err) @@ -204,13 +218,14 @@ func TestCalcAmountOut(t *testing.T) { assert.Nil(t, err) tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", Amount: big.NewInt(73183418984294781), } - tokenOut := "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0" + tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" // expected - expected := "63551050657042642" + expectedAmountOut := "73208894800314286" + expectedSwapFee := "1727318373403" // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { @@ -223,145 +238,234 @@ func TestCalcAmountOut(t *testing.T) { assert.Nil(t, err) // assert - assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) - + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) } func TestPoolSimulator_CalcAmountIn(t *testing.T) { - type fields struct { - poolStr string - } - - tests := []struct { - name string - fields fields - params poolpkg.CalcAmountInParams - want *poolpkg.CalcAmountInResult - wantErr error - }{ - { - name: "1. should return error ErrStableGetBalanceDidntConverge", - fields: fields{ - poolStr: `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-stable", - "type": "balancer-v2-stable", - "timestamp": 1703667290, - "reserves": [ - "9999991000000000000", - "99999910000000000056", - "8897791020011100123456" - ], - "tokens": [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0x1388\",\"swapFeePercentage\":\"0x2D79883D2000\",\"scalingFactors\":[\"100\",\"1\",\"100\"],\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"Stable\",\"poolVersionsion\":1,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: big.NewInt(999999100000), + t.Run("1. Swap from token 0 to token 1 successful", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, }, - TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", }, - want: nil, - wantErr: math.ErrStableComputeBalanceDidNotConverge, - }, - { - name: "2. should return OK", - fields: fields{ - poolStr: `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-stable", - "type": "balancer-v2-stable", - "timestamp": 1703667290, - "reserves": [ - "1152882153159026494", - "873225053252443292" - ], - "tokens": [ - { - "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + currentAmp: uint256.NewInt(5000000), + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(1e18), + } + tokenIn := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" + + expectedAmountIn := "1179719723071294910" + expectedSwapFee := "23594394461425" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) + + assert.Nil(t, err) + + assert.Equal(t, expectedAmountIn, result.TokenAmountIn.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) + + t.Run("1. Swap from token 1 to token 0 successful", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, + }, }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - Amount: big.NewInt(63551050657042642), + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + currentAmp: uint256.NewInt(5000000), + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(1e18), + } + tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + + expectedAmountIn := "847694017912779287" + expectedSwapFee := "16953880358256" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) + + assert.Nil(t, err) + + assert.Equal(t, expectedAmountIn, result.TokenAmountIn.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) + + t.Run("3. AmountIn is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, }, - TokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", }, - want: &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - Amount: big.NewInt(73154145616700748), + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + currentAmp: uint256.NewInt(5000000), + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(799999), // less than MINIMUM_TRADE_AMOUNT (1000000) + } + tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) + + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountInTooSmall, err) + }) + + t.Run("4. AmountOut is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) + reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, }, }, - wantErr: nil, - }, - } + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.NewInt(1009108897721464489), uint256.NewInt(1190879275654308905)}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("694069210892948295209"), uint256.MustFromDecimal("2124492373418339554414")}, // balancesLiveScaled18 + uint256.NewInt(20000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + currentAmp: uint256.NewInt(5000000), + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(840000), // less than MINIMUM_TRADE_AMOUNT (1000000) + } + tokenIn := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" + + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var pool entity.Pool - err := json.Unmarshal([]byte(tt.fields.poolStr), &pool) - assert.Nil(t, err) + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountOutTooSmall, err) + }) - simulator, err := NewPoolSimulator(pool) - assert.Nil(t, err) + t.Run("5. should return OK", func(t *testing.T) { + poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735803357,"reserves":["687804073931103275644","1783969556654743519024"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"694069210892948295209\",\"2124492373418339554414\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009108897721464489\",\"1190879275654308905\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21535329}` - got, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { - return simulator.CalcAmountIn(tt.params) + var pool entity.Pool + err := json.Unmarshal([]byte(poolStr), &pool) + assert.Nil(t, err) + + s, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(1e18), + } + tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + + // expected + expectedAmountIn := "847694017912779287" + expectedSwapFee := "16953880358256" + + // actual + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, }) - if err != nil { - assert.ErrorIsf(t, err, tt.wantErr, "PoolSimulator.CalcAmountIn() error = %v, wantErr %v", err, tt.wantErr) - return - } - assert.Equalf(t, tt.want.TokenAmountIn.Token, got.TokenAmountIn.Token, "tokenIn = %v, want %v", got.TokenAmountIn.Token, tt.want.TokenAmountIn.Token) - assert.Equalf(t, tt.want.TokenAmountIn.Amount, got.TokenAmountIn.Amount, "amountIn = %v, want %v", got.TokenAmountIn.Amount.String(), tt.want.TokenAmountIn.Amount.String()) }) - } + + assert.Nil(t, err) + + // assert + assert.Equal(t, expectedAmountIn, result.TokenAmountIn.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) } diff --git a/pkg/liquidity-source/balancer-v3/vault/error.go b/pkg/liquidity-source/balancer-v3/vault/error.go index 9b1c4b42b..efba755a4 100644 --- a/pkg/liquidity-source/balancer-v3/vault/error.go +++ b/pkg/liquidity-source/balancer-v3/vault/error.go @@ -3,7 +3,8 @@ package vault import "errors" var ( - ErrTradeAmountTooSmall = errors.New("trade amount is too small") + ErrAmountInTooSmall = errors.New("amount in is too small") + ErrAmountOutTooSmall = errors.New("amount out is too small") ErrProtocolFeesExceedTotalCollected = errors.New("protocolFees exceed totalCollected") ErrVaultIsPaused = errors.New("vault is paused") ErrPoolIsPaused = errors.New("pool is paused") diff --git a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go index 0ee2b6a34..b2aca258a 100644 --- a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go +++ b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go @@ -6,12 +6,18 @@ import ( ) func toScaled18ApplyRateRoundUp(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - scaledAmount := new(uint256.Int).Mul(amount, scalingFactor) + scaledAmount, err := math.FixPoint.Mul(amount, scalingFactor) + if err != nil { + return nil, err + } return math.FixPoint.MulUp(scaledAmount, tokenRate) } func toScaled18ApplyRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { - scaledAmount := new(uint256.Int).Mul(amount, scalingFactor) + scaledAmount, err := math.FixPoint.Mul(amount, scalingFactor) + if err != nil { + return nil, err + } return math.FixPoint.MulDown(scaledAmount, tokenRate) } diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index b4df0c909..41286a29a 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -98,7 +98,7 @@ func (v *Vault) Swap( // _ensureValidSwapAmount if amountGivenScaled18.Lt(MINIMUM_TRADE_AMOUNT) { - return nil, nil, nil, ErrTradeAmountTooSmall + return nil, nil, nil, ErrAmountInTooSmall } amountCalculatedScaled18, err := onSwap(poolSwapParams) @@ -108,7 +108,7 @@ func (v *Vault) Swap( // _ensureValidSwapAmount if amountCalculatedScaled18.Lt(MINIMUM_TRADE_AMOUNT) { - return nil, nil, nil, ErrTradeAmountTooSmall + return nil, nil, nil, ErrAmountOutTooSmall } var amountCalculated *uint256.Int @@ -121,7 +121,7 @@ func (v *Vault) Swap( return nil, nil, nil, err } } else { - totalSwapFeeAmountScaled18, err := math.FixPoint.MulDivUp(amountCalculatedScaled18, v.swapFeePercentage, math.FixPoint.Complement(v.swapFeePercentage)) + totalSwapFeeAmountScaled18, err = math.FixPoint.MulDivUp(amountCalculatedScaled18, v.swapFeePercentage, math.FixPoint.Complement(v.swapFeePercentage)) if err != nil { return nil, nil, nil, err } diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go index e8933e50f..d11faae14 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go @@ -4,13 +4,10 @@ import ( "math/big" "testing" - "github.com/goccy/go-json" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/math" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" poolpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" @@ -18,20 +15,18 @@ import ( ) func TestCalcAmountOut(t *testing.T) { - t.Run("1. should return error balance didnt converge", func(t *testing.T) { - reserves := make([]*big.Int, 3) - reserves[0], _ = new(big.Int).SetString("9999991000000000000", 10) - reserves[1], _ = new(big.Int).SetString("99999910000000000056", 10) - reserves[2], _ = new(big.Int).SetString("8897791020011100123456", 10) + t.Run("1. Swap from token 0 to token 1 successful", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ Reserves: reserves, Tokens: []string{ - "0xdac17f958d2ee523a2206206994597c13d831ec7", - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "0x6b175474e89094c44da98b954eedeac495271d0f", + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", }, }, }, @@ -39,42 +34,49 @@ func TestCalcAmountOut(t *testing.T) { hooks.NewBaseHook(), shared.HooksConfig{}, false, // isPoolInRecoveryMode - []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors - []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates - []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(30000000000000), // swapFeePercentage + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - // currentAmp: uint256.NewInt(1000000), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: new(big.Int).SetUint64(99999910000000), + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(1e18), } - tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" - _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + + expectedAmountOut := "12278617355364789838" + expectedSwapFee := "2103630945830047" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - assert.ErrorIs(t, err, math.ErrStableComputeBalanceDidNotConverge) + + assert.Nil(t, err) + + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) - t.Run("2. should return OK", func(t *testing.T) { - // input + t.Run("2. Swap from token 1 to token 0 successful", func(t *testing.T) { reserves := make([]*big.Int, 2) - reserves[0], _ = new(big.Int).SetString("151090057727415359409", 10) - reserves[1], _ = new(big.Int).SetString("249356634133584290044", 10) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ Reserves: reserves, Tokens: []string{ - "0x5F9D59db355b4A60501544637b00e94082cA575b", - "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8", + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", }, }, }, @@ -82,23 +84,23 @@ func TestCalcAmountOut(t *testing.T) { hooks.NewBaseHook(), shared.HooksConfig{}, false, // isPoolInRecoveryMode - []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors - []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates - []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(30000000000000), - uint256.NewInt(500000000000000000), + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - // currentAmp: uint256.NewInt(1000000), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0x5F9D59db355b4A60501544637b00e94082cA575b", - Amount: new(big.Int).SetUint64(10000000), + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(1e18), } - tokenOut := "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8" + tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - expectedAmountOut := "9999700" - expectedSwapFee := "300" + expectedAmountOut := "80912682117686948" + expectedSwapFee := "2971053459918506" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ @@ -106,27 +108,25 @@ func TestCalcAmountOut(t *testing.T) { TokenOut: tokenOut, }) }) + assert.Nil(t, err) assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) - t.Run("3. should return OK", func(t *testing.T) { - // input - reserves := make([]*big.Int, 3) - reserves[0], _ = new(big.Int).SetString("9999991000000000013314124321", 10) - reserves[1], _ = new(big.Int).SetString("9999991000000123120010005613", 10) - reserves[2], _ = new(big.Int).SetString("1328897131447911102200123456", 10) + t.Run("3. AmountIn is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) s := PoolSimulator{ Pool: poolpkg.Pool{ Info: poolpkg.PoolInfo{ Reserves: reserves, Tokens: []string{ - "0xdac17f958d2ee523a2206206994597c13d831ec7", - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "0x6b175474e89094c44da98b954eedeac495271d0f", + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", }, }, }, @@ -134,234 +134,266 @@ func TestCalcAmountOut(t *testing.T) { hooks.NewBaseHook(), shared.HooksConfig{}, false, // isPoolInRecoveryMode - []*uint256.Int{uint256.NewInt(1e18), uint256.NewInt(1e18)}, // decimalScalingFactors - []*uint256.Int{uint256.NewInt(1026650641510258300), uint256.NewInt(1105219353582858337)}, // tokenRates - []*uint256.Int{uint256.MustFromDecimal("151090057727415359409"), uint256.MustFromDecimal("249356634133584290044")}, // balancesLiveScaled18 - uint256.NewInt(30000000000000), - uint256.NewInt(500000000000000000), + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage ), - // currentAmp: uint256.NewInt(1000000), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: new(big.Int).SetUint64(12111222333444555666), + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(799999), // less than MINIMUM_TRADE_AMOUNT (1000000) } - tokenOut := "0x6b175474e89094c44da98b954eedeac495271d0f" + tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - // expected - expected := "590000000000000000" - - // actual - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - assert.Nil(t, err) - // assert - assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountInTooSmall, err) }) - t.Run("4. should return OK", func(t *testing.T) { - poolStr := `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-stable", - "type": "balancer-v2-stable", - "timestamp": 1703667290, - "reserves": [ - "1152882153159026494", - "873225053252443292" - ], - "tokens": [ - { - "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }` - var pool entity.Pool - err := json.Unmarshal([]byte(poolStr), &pool) - assert.Nil(t, err) + t.Run("4. AmountOut is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) - s, err := NewPoolSimulator(pool) - assert.Nil(t, err) + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, + }, + }, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, + } tokenAmountIn := poolpkg.TokenAmount{ - Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - Amount: big.NewInt(73183418984294781), + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(991000), // less than MINIMUM_TRADE_AMOUNT (1000000) } - tokenOut := "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0" - - // expected - expected := "63551050657042642" + tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - // actual - result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ TokenAmountIn: tokenAmountIn, TokenOut: tokenOut, }) }) - assert.Nil(t, err) - - // assert - assert.Equal(t, expected, result.TokenAmountOut.Amount.String()) - + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountOutTooSmall, err) }) } func TestPoolSimulator_CalcAmountIn(t *testing.T) { - type fields struct { - poolStr string - } - - tests := []struct { - name string - fields fields - params poolpkg.CalcAmountInParams - want *poolpkg.CalcAmountInResult - wantErr error - }{ - { - name: "1. should return error ErrStableGetBalanceDidntConverge", - fields: fields{ - poolStr: `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-stable", - "type": "balancer-v2-stable", - "timestamp": 1703667290, - "reserves": [ - "9999991000000000000", - "99999910000000000056", - "8897791020011100123456" - ], - "tokens": [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0x1388\",\"swapFeePercentage\":\"0x2D79883D2000\",\"scalingFactors\":[\"100\",\"1\",\"100\"],\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"Stable\",\"poolVersionsion\":1,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0xdac17f958d2ee523a2206206994597c13d831ec7", - Amount: big.NewInt(999999100000), + t.Run("1. Swap from token 0 to token 1 successful", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, }, - TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", }, - want: nil, - wantErr: math.ErrStableComputeBalanceDidNotConverge, - }, - { - name: "2. should return OK", - fields: fields{ - poolStr: `{ - "address": "0x851523a36690bf267bbfec389c823072d82921a9", - "exchange": "balancer-v2-stable", - "type": "balancer-v2-stable", - "timestamp": 1703667290, - "reserves": [ - "1152882153159026494", - "873225053252443292" - ], - "tokens": [ - { - "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - }, - { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "name": "", - "symbol": "", - "decimals": 0, - "weight": 1, - "swappable": true - } - ], - "extra": "{\"amp\":\"0xf4240\",\"swapFeePercentage\":\"0x16bcc41e90000\",\"scalingFactors\":[\"0xFFB10F9BCF7D41A\",\"0xde0b6b3a7640000\"],\"paused\":false}", - "staticExtra": "{\"poolId\":\"0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed\",\"poolType\":\"MetaStable\",\"poolVersionsion\":1,\"poolSpecialization\":2,\"vault\":\"0xba12222222228d8ba445958a75a0704d566bf2c8\"}" - }`, - }, - params: poolpkg.CalcAmountInParams{ - TokenAmountOut: poolpkg.TokenAmount{ - Token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - Amount: big.NewInt(63551050657042642), + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(1e18), + } + tokenIn := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" + + expectedAmountIn := "68442734257727536" + expectedSwapFee := "171106835644319" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) + + assert.Nil(t, err) + + assert.Equal(t, expectedAmountIn, result.TokenAmountIn.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) + + t.Run("2. Swap from token 1 to token 0 successful", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, }, - TokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", }, - want: &poolpkg.CalcAmountInResult{ - TokenAmountIn: &poolpkg.TokenAmount{ - Token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - Amount: big.NewInt(73154145616700748), + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(1e18), + } + tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + + expectedAmountIn := "14710037031320992773" + expectedSwapFee := "36775092578302482" + + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) + + assert.Nil(t, err) + + assert.Equal(t, expectedAmountIn, result.TokenAmountIn.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) + + t.Run("3. AmountIn is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, }, }, - wantErr: nil, - }, - } + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + Amount: big.NewInt(799999), // less than MINIMUM_TRADE_AMOUNT (1000000) + } + tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var pool entity.Pool - err := json.Unmarshal([]byte(tt.fields.poolStr), &pool) - assert.Nil(t, err) + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) - simulator, err := NewPoolSimulator(pool) - assert.Nil(t, err) + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountInTooSmall, err) + }) - got, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { - return simulator.CalcAmountIn(tt.params) + t.Run("4. AmountOut is too small", func(t *testing.T) { + reserves := make([]*big.Int, 2) + reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) + reserves[1], _ = new(big.Int).SetString("8876513774745869289662", 10) + + s := PoolSimulator{ + Pool: poolpkg.Pool{ + Info: poolpkg.PoolInfo{ + Reserves: reserves, + Tokens: []string{ + "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", + "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + }, + }, + }, + vault: vault.New( + hooks.NewBaseHook(), + shared.HooksConfig{}, + false, // isPoolInRecoveryMode + []*uint256.Int{uint256.NewInt(1), uint256.NewInt(1)}, // decimalScalingFactors + []*uint256.Int{uint256.MustFromDecimal("1189479974914033532"), uint256.MustFromDecimal("1000890753869723446")}, // tokenRates + []*uint256.Int{uint256.MustFromDecimal("720118889801487374560"), uint256.MustFromDecimal("8876514414974844966787")}, // balancesLiveScaled18 + uint256.NewInt(2500000000000000), // swapFeePercentage + uint256.NewInt(500000000000000000), // aggregateSwapFeePercentage + ), + normalizedWeights: []*uint256.Int{uint256.NewInt(500000000000000000), uint256.NewInt(500000000000000000)}, + } + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: big.NewInt(999900), // less than MINIMUM_TRADE_AMOUNT (1000000) + } + tokenIn := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" + + _, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, }) - if err != nil { - assert.ErrorIsf(t, err, tt.wantErr, "PoolSimulator.CalcAmountIn() error = %v, wantErr %v", err, tt.wantErr) - return - } - assert.Equalf(t, tt.want.TokenAmountIn.Token, got.TokenAmountIn.Token, "tokenIn = %v, want %v", got.TokenAmountIn.Token, tt.want.TokenAmountIn.Token) - assert.Equalf(t, tt.want.TokenAmountIn.Amount, got.TokenAmountIn.Amount, "amountIn = %v, want %v", got.TokenAmountIn.Amount.String(), tt.want.TokenAmountIn.Amount.String()) }) - } + + assert.Error(t, err) + assert.Equal(t, vault.ErrAmountOutTooSmall, err) + }) } diff --git a/pkg/pooltypes/pooltypes.go b/pkg/pooltypes/pooltypes.go index 6016f3153..16c4b30fb 100644 --- a/pkg/pooltypes/pooltypes.go +++ b/pkg/pooltypes/pooltypes.go @@ -7,7 +7,7 @@ import ( balancerv2stable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/stable" balancerv2weighted "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v2/weighted" balancerv3stable "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/stable" - + balancerv3weighted "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/weighted" bancorv21 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bancor-v21" bancorv3 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bancor-v3" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/bebop" @@ -215,6 +215,7 @@ type Types struct { BalancerV2Stable string BalancerV2ComposableStable string BalancerV3Stable string + BalancerV3Weighted string VelocoreV2CPMM string VelocoreV2WombatStable string Fulcrom string @@ -361,6 +362,7 @@ var ( BalancerV2Stable: balancerv2stable.DexType, BalancerV2ComposableStable: balancerv2composablestable.DexType, BalancerV3Stable: balancerv3stable.DexType, + BalancerV3Weighted: balancerv3weighted.DexType, VelocoreV2CPMM: velocorev2cpmm.DexType, VelocoreV2WombatStable: velocorev2wombatstable.DexType, Fulcrom: fulcrom.DexTypeFulcrom, From 7aa1294a3c0d1008077c98e11ee170a0e827b72a Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 16:55:34 +0700 Subject: [PATCH 26/39] update unit tests --- .../balancer-v3/stable/pool_simulator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index e6ed8120e..199dfff81 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -243,7 +243,7 @@ func TestCalcAmountOut(t *testing.T) { }) } -func TestPoolSimulator_CalcAmountIn(t *testing.T) { +func TestCalcAmountIn(t *testing.T) { t.Run("1. Swap from token 0 to token 1 successful", func(t *testing.T) { reserves := make([]*big.Int, 2) reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) @@ -294,7 +294,7 @@ func TestPoolSimulator_CalcAmountIn(t *testing.T) { assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) }) - t.Run("1. Swap from token 1 to token 0 successful", func(t *testing.T) { + t.Run("2. Swap from token 1 to token 0 successful", func(t *testing.T) { reserves := make([]*big.Int, 2) reserves[0], _ = new(big.Int).SetString("687804073931103275644", 10) reserves[1], _ = new(big.Int).SetString("1783969556654743519024", 10) From 07cc9a1fa17eb90ad4c582b57846a690285a0f2e Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 17:42:59 +0700 Subject: [PATCH 27/39] fix data race --- .../balancer-v3/math/stable_math.go | 56 ++++++++++++++----- .../weighted/pool_simulator_test.go | 2 +- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go index a356ccbcf..1167b890a 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -2,6 +2,7 @@ package math import ( "errors" + "sync" "github.com/holiman/uint256" ) @@ -15,7 +16,9 @@ var ( var StableMath *stableMath -type stableMath struct{} +type stableMath struct { + mu sync.RWMutex +} func init() { StableMath = &stableMath{} @@ -26,7 +29,7 @@ func (s *stableMath) ComputeOutGivenExactIn( balances []*uint256.Int, tokenIndexIn, tokenIndexOut int, tokenAmountIn, invariant *uint256.Int, -) (*uint256.Int, error) { +) (amountOut *uint256.Int, err error) { /************************************************************************************************************** // outGivenExactIn token x for y - polynomial equation to solve // // ay = amount out to calculate // @@ -39,21 +42,32 @@ func (s *stableMath) ComputeOutGivenExactIn( // P = product of final balances but y // **************************************************************************************************************/ - balances[tokenIndexIn].Add(balances[tokenIndexIn], tokenAmountIn) + s.mu.Lock() + defer s.mu.Unlock() + + balances[tokenIndexIn], err = FixPoint.Add(balances[tokenIndexIn], tokenAmountIn) + if err != nil { + return nil, err + } finalBalanceOut, err := s.ComputeBalance(amplificationParameter, balances, invariant, tokenIndexOut) if err != nil { return nil, err } - balances[tokenIndexIn].Sub(balances[tokenIndexIn], tokenAmountIn) + balances[tokenIndexIn], err = FixPoint.Sub(balances[tokenIndexIn], tokenAmountIn) + if err != nil { + return nil, err + } - amountOut, err := FixPoint.Sub(balances[tokenIndexOut], finalBalanceOut) + amountOut, err = FixPoint.Sub(balances[tokenIndexOut], finalBalanceOut) if err != nil { return nil, err } - return amountOut.SubUint64(amountOut, 1), nil + amountOut.SubUint64(amountOut, 1) + + return } func (s *stableMath) ComputeInGivenExactOut( @@ -61,7 +75,7 @@ func (s *stableMath) ComputeInGivenExactOut( balances []*uint256.Int, tokenIndexIn, tokenIndexOut int, tokenAmountOut, invariant *uint256.Int, -) (*uint256.Int, error) { +) (amountIn *uint256.Int, err error) { /************************************************************************************************************** // inGivenExactOut token x for y - polynomial equation to solve // // ax = amount in to calculate // @@ -74,21 +88,32 @@ func (s *stableMath) ComputeInGivenExactOut( // P = product of final balances but x // **************************************************************************************************************/ - balances[tokenIndexOut].Sub(balances[tokenIndexOut], tokenAmountOut) + s.mu.Lock() + defer s.mu.Unlock() + + balances[tokenIndexOut], err = FixPoint.Sub(balances[tokenIndexOut], tokenAmountOut) + if err != nil { + return nil, err + } finalBalanceIn, err := s.ComputeBalance(amplificationParameter, balances, invariant, tokenIndexIn) if err != nil { return nil, err } - balances[tokenIndexOut].Add(balances[tokenIndexOut], tokenAmountOut) + balances[tokenIndexOut], err = FixPoint.Add(balances[tokenIndexOut], tokenAmountOut) + if err != nil { + return nil, err + } - amountOut, err := FixPoint.Sub(finalBalanceIn, balances[tokenIndexIn]) + amountIn, err = FixPoint.Sub(finalBalanceIn, balances[tokenIndexIn]) if err != nil { return nil, err } - return amountOut.AddUint64(amountOut, 1), nil + amountIn.AddUint64(amountIn, 1) + + return } func (s *stableMath) ComputeBalance( @@ -177,9 +202,11 @@ func (s *stableMath) ComputeInvariant(amplificationParameter *uint256.Int, balan numTokens := uint256.NewInt(uint64(len(balances))) sum := uint256.NewInt(0) - for _, balance := range balances { - sum.Add(sum, balance) + s.mu.RLock() + for i := range balances { + sum.Add(sum, balances[i]) } + s.mu.RUnlock() if sum.IsZero() { return sum, nil @@ -200,12 +227,15 @@ func (s *stableMath) ComputeInvariant(amplificationParameter *uint256.Int, balan // D_P = D^(n+1)/(n^n * P) D_P.Set(invariant) + + s.mu.RLock() for _, balance := range balances { // D_P = D_P * D / (x_i * n) tmp.Mul(balance, numTokens) D_P.Mul(D_P, invariant) D_P.Div(D_P, tmp) } + s.mu.RUnlock() // (A * n * S / AP + D_P * n) * D numer.Mul(ampTimesN, sum) diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go index d11faae14..0bea0653a 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go @@ -206,7 +206,7 @@ func TestCalcAmountOut(t *testing.T) { }) } -func TestPoolSimulator_CalcAmountIn(t *testing.T) { +func TestCalcAmountIn(t *testing.T) { t.Run("1. Swap from token 0 to token 1 successful", func(t *testing.T) { reserves := make([]*big.Int, 2) reserves[0], _ = new(big.Int).SetString("720118889801352582380", 10) From 140656a09d4e597fdfce1e3552e9f3bd22ed8e50 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 18:56:31 +0700 Subject: [PATCH 28/39] fix math of stable pool --- .../balancer-v3/stable/pool_simulator_test.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index 199dfff81..15ce4d95f 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -51,7 +51,7 @@ func TestCalcAmountOut(t *testing.T) { } tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - expectedAmountOut := "1000347432097063736" + expectedAmountOut := "1000347432097063734" expectedSwapFee := "23602591917350" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { @@ -101,7 +101,7 @@ func TestCalcAmountOut(t *testing.T) { } tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - expectedAmountOut := "999611352568542440" + expectedAmountOut := "999611352568542438" expectedSwapFee := "16947291272107" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { @@ -208,7 +208,7 @@ func TestCalcAmountOut(t *testing.T) { }) t.Run("5. should return OK", func(t *testing.T) { - poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735803357,"reserves":["687804073931103275644","1783969556654743519024"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"694069210892948295209\",\"2124492373418339554414\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009108897721464489\",\"1190879275654308905\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21535329}` + poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816509,"reserves":["619469949959861143118","1841897390394044699179"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"625118423384960111487\",\"2193481638791868130942\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009118236365565374\",\"1190881560629502583\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536418}` var pool entity.Pool err := json.Unmarshal([]byte(poolStr), &pool) @@ -219,13 +219,13 @@ func TestCalcAmountOut(t *testing.T) { tokenAmountIn := poolpkg.TokenAmount{ Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", - Amount: big.NewInt(73183418984294781), + Amount: big.NewInt(1e18), } tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" // expected - expectedAmountOut := "73208894800314286" - expectedSwapFee := "1727318373403" + expectedAmountOut := "1000445828427074752" + expectedSwapFee := "23602418779361" // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { @@ -278,7 +278,7 @@ func TestCalcAmountIn(t *testing.T) { } tokenIn := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - expectedAmountIn := "1179719723071294910" + expectedAmountIn := "1179719723071294912" expectedSwapFee := "23594394461425" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { @@ -328,7 +328,7 @@ func TestCalcAmountIn(t *testing.T) { } tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - expectedAmountIn := "847694017912779287" + expectedAmountIn := "847694017912779289" expectedSwapFee := "16953880358256" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { @@ -435,7 +435,7 @@ func TestCalcAmountIn(t *testing.T) { }) t.Run("5. should return OK", func(t *testing.T) { - poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735803357,"reserves":["687804073931103275644","1783969556654743519024"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"694069210892948295209\",\"2124492373418339554414\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009108897721464489\",\"1190879275654308905\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21535329}` + poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816256,"reserves":["619469960947444228741","1841897397011807636504"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"625118323594477223912\",\"2193481567863040629311\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009118057376654946\",\"1190881517842211888\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536397}` var pool entity.Pool err := json.Unmarshal([]byte(poolStr), &pool) @@ -451,8 +451,8 @@ func TestCalcAmountIn(t *testing.T) { tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" // expected - expectedAmountIn := "847694017912779287" - expectedSwapFee := "16953880358256" + expectedAmountIn := "847783902418696208" + expectedSwapFee := "16955678048374" // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { From 07a0517beac9532c03a3c7c604eba03222d41b6b Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 18:56:46 +0700 Subject: [PATCH 29/39] update --- .../balancer-v3/math/fixed_point_math.go | 17 ++++++++++++ .../balancer-v3/math/stable_math.go | 27 +++++++++++++++---- .../balancer-v3/math/stable_math_test.go | 12 ++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go index 35cc5d7c6..1edfa07b0 100644 --- a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go @@ -93,6 +93,23 @@ func (f *fixPoint) DivDown(a, b *uint256.Int) (*uint256.Int, error) { return aInflated.Div(aInflated, b), nil } +func (f *fixPoint) DivRawUp(a, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + + // result = a == 0 ? 0 : 1 + (a - 1) / b + if a.IsZero() { + return ZERO, nil + } + + delta := new(uint256.Int).Sub(a, ONE) + delta.Div(delta, b) + delta.Add(ONE, delta) + + return delta, nil +} + func (f *fixPoint) PowUp(x, y *uint256.Int) (*uint256.Int, error) { if y.Eq(ONE_E18) { return x, nil diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go index 1167b890a..fda3a2094 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -146,7 +146,12 @@ func (s *stableMath) ComputeBalance( // c = (D^2 * AP)/(An * P_D) * x_i numerator := new(uint256.Int).Mul(invariantSquared, _AMP_PRECISION) denominator := new(uint256.Int).Mul(ampTimesN, balanceProduct) - c := new(uint256.Int).Div(numerator, denominator) + + c, err := FixPoint.DivRawUp(numerator, denominator) + if err != nil { + return nil, err + } + c.Mul(c, balances[tokenIndex]) // b = S + (D * AP)/An @@ -157,7 +162,10 @@ func (s *stableMath) ComputeBalance( // y = (D^2 + c)/(D + b) numerator.Add(invariantSquared, c) denominator.Add(invariant, b) - tokenBalance := new(uint256.Int).Div(numerator, denominator) + tokenBalance, err := FixPoint.DivRawUp(numerator, denominator) + if err != nil { + return nil, err + } prevTokenBalance := new(uint256.Int) for i := 0; i < 255; i++ { @@ -171,15 +179,24 @@ func (s *stableMath) ComputeBalance( denominator.Add(denominator, b) denominator.Sub(denominator, invariant) - tokenBalance.Div(numerator, denominator) + tokenBalance, err = FixPoint.DivRawUp(numerator, denominator) + if err != nil { + return nil, err + } if tokenBalance.Gt(prevTokenBalance) { - mulResult.Sub(tokenBalance, prevTokenBalance) + mulResult, err = FixPoint.Sub(tokenBalance, prevTokenBalance) + if err != nil { + return nil, err + } if mulResult.Cmp(ONE) <= 0 { return tokenBalance, nil } } else { - mulResult.Sub(prevTokenBalance, tokenBalance) + mulResult, err = FixPoint.Sub(prevTokenBalance, tokenBalance) + if err != nil { + return nil, err + } if mulResult.Cmp(ONE) <= 0 { return tokenBalance, nil } diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go index c84e72b50..5b69771db 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go @@ -91,7 +91,7 @@ func TestStableMath_ComputeBalance(t *testing.T) { }, invariant: uint256.NewInt(2000000), tokenIndex: 0, - expectedBalance: uint256.NewInt(1000000), + expectedBalance: uint256.NewInt(1000001), expectedErr: nil, }, { @@ -103,7 +103,7 @@ func TestStableMath_ComputeBalance(t *testing.T) { }, invariant: uint256.MustFromDecimal("400033876069185101476"), tokenIndex: 1, - expectedBalance: uint256.MustFromDecimal("249100103697704706751"), + expectedBalance: uint256.MustFromDecimal("249100103697704706752"), expectedErr: nil, }, } @@ -145,7 +145,7 @@ func TestStableMath_ComputeOutGivenExactIn(t *testing.T) { tokenIndexOut: 1, tokenAmountIn: uint256.NewInt(100), invariant: uint256.NewInt(2000000), - expectedAmount: uint256.NewInt(100), + expectedAmount: uint256.NewInt(98), expectedErr: nil, }, { @@ -159,7 +159,7 @@ func TestStableMath_ComputeOutGivenExactIn(t *testing.T) { tokenIndexOut: 1, tokenAmountIn: uint256.NewInt(100), invariant: uint256.NewInt(1000000), - expectedAmount: uint256.NewInt(352455), + expectedAmount: uint256.NewInt(352454), expectedErr: nil, }, } @@ -208,7 +208,7 @@ func TestStableMath_ComputeInGivenExactOut(t *testing.T) { tokenIndexOut: 1, tokenAmountIn: uint256.NewInt(100), invariant: uint256.NewInt(2000000), - expectedAmount: uint256.NewInt(100), + expectedAmount: uint256.NewInt(98), expectedErr: nil, }, { @@ -222,7 +222,7 @@ func TestStableMath_ComputeInGivenExactOut(t *testing.T) { tokenIndexOut: 1, tokenAmountIn: uint256.NewInt(100), invariant: uint256.NewInt(1000000), - expectedAmount: uint256.NewInt(352455), + expectedAmount: uint256.NewInt(352454), expectedErr: nil, }, } From 742d399d8cac9b9a6788e8a8167caef8ac05bd25 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 19:34:41 +0700 Subject: [PATCH 30/39] update --- .../balancer-v3/math/stable_math_test.go | 18 +++++++++--------- .../balancer-v3/vault/scaling_helper.go | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go index 5b69771db..53f7edbb2 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math_test.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math_test.go @@ -27,12 +27,12 @@ func TestStableMath_ComputeInvariant(t *testing.T) { }, { name: "Imbalanced 2 token pool", - amp: uint256.NewInt(1000000), + amp: uint256.NewInt(200000), balances: []*uint256.Int{ - uint256.MustFromDecimal("150946570888469782860"), - uint256.MustFromDecimal("249100103697704706751"), + uint256.MustFromDecimal("340867122491122140643"), + uint256.MustFromDecimal("384610409069784884043"), }, - expectedInvariant: uint256.MustFromDecimal("400033876069185101475"), + expectedInvariant: uint256.MustFromDecimal("725470946757739599230"), expectedErr: nil, }, { @@ -96,14 +96,14 @@ func TestStableMath_ComputeBalance(t *testing.T) { }, { name: "Imbalanced 2 token pool", - amp: uint256.NewInt(1000000), + amp: uint256.NewInt(200000), balances: []*uint256.Int{ - uint256.MustFromDecimal("150946570888469782860"), - uint256.MustFromDecimal("249100103697704706751"), + uint256.MustFromDecimal("340867122491122140643"), + uint256.MustFromDecimal("384610409069784884043"), }, - invariant: uint256.MustFromDecimal("400033876069185101476"), + invariant: uint256.MustFromDecimal("725470946757739599230"), tokenIndex: 1, - expectedBalance: uint256.MustFromDecimal("249100103697704706752"), + expectedBalance: uint256.MustFromDecimal("384610409069784884044"), expectedErr: nil, }, } diff --git a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go index b2aca258a..f3259df59 100644 --- a/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go +++ b/pkg/liquidity-source/balancer-v3/vault/scaling_helper.go @@ -22,14 +22,14 @@ func toScaled18ApplyRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) } func computeRateRoundUp(rate *uint256.Int) *uint256.Int { - div := new(uint256.Int).Div(rate, math.ONE_E18) - div.Mul(div, math.ONE_E18) + divisor := new(uint256.Int).Div(rate, math.ONE_E18) + divisor.Mul(divisor, math.ONE_E18) - if div.Eq(rate) { - return new(uint256.Int).Set(rate) + if divisor.Eq(rate) { + return divisor.Set(rate) } - return new(uint256.Int).Add(rate, math.ONE) + return divisor.Add(rate, math.ONE) } func toRawUndoRateRoundDown(amount, scalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { From 0fb97c8719d4951494fd819440f9d672676f519c Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Thu, 2 Jan 2025 19:43:10 +0700 Subject: [PATCH 31/39] cleanup --- .../balancer-v3/vault/vault.go | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index 41286a29a..5506e6531 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -51,8 +51,7 @@ func (v *Vault) Swap( vaultSwapParams shared.VaultSwapParams, onSwap func(param shared.PoolSwapParams) (*uint256.Int, error), ) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - amountGivenScaled18, err := v.ComputeAmountGivenScaled18(vaultSwapParams.Kind, vaultSwapParams.AmountGivenRaw, - v.decimalScalingFactors[vaultSwapParams.IndexOut], v.tokenRates[vaultSwapParams.IndexOut]) + amountGivenScaled18, err := v.ComputeAmountGivenScaled18(vaultSwapParams.Kind, vaultSwapParams.IndexOut, vaultSwapParams.AmountGivenRaw) if err != nil { return nil, nil, nil, err } @@ -75,12 +74,10 @@ func (v *Vault) Swap( } if v.hooksConfig.ShouldCallComputeDynamicSwapFee { - swapFeePercentage, err := v.callComputeDynamicSwapFeeHook(poolSwapParams) + poolSwapParams.SwapFeePercentage, err = v.callComputeDynamicSwapFeeHook(poolSwapParams) if err != nil { return nil, nil, nil, err } - - poolSwapParams.SwapFeePercentage = swapFeePercentage } var totalSwapFeeAmountScaled18 *uint256.Int @@ -140,8 +137,7 @@ func (v *Vault) Swap( } } - totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(totalSwapFeeAmountScaled18, v.aggregateSwapFeePercentage, - v.decimalScalingFactors[poolSwapParams.IndexIn], v.tokenRates[poolSwapParams.IndexIn]) + totalSwapFee, aggregateFee, err := v.ComputeAggregateSwapFees(poolSwapParams.IndexIn, totalSwapFeeAmountScaled18, v.aggregateSwapFeePercentage) if err != nil { return nil, nil, nil, err } @@ -157,20 +153,18 @@ func (v *Vault) Swap( return amountCalculated, totalSwapFee, aggregateFee, nil } -func (v *Vault) ComputeAmountGivenScaled18(kind shared.SwapKind, amountGiven, decimalScalingFactor, tokenRate *uint256.Int) (*uint256.Int, error) { +func (v *Vault) ComputeAmountGivenScaled18(kind shared.SwapKind, index int, amountGiven *uint256.Int) (*uint256.Int, error) { if kind == shared.EXACT_IN { - return toScaled18ApplyRateRoundDown(amountGiven, decimalScalingFactor, tokenRate) + return toScaled18ApplyRateRoundDown(amountGiven, v.decimalScalingFactors[index], v.tokenRates[index]) } - return toScaled18ApplyRateRoundUp(amountGiven, decimalScalingFactor, computeRateRoundUp(tokenRate)) + return toScaled18ApplyRateRoundUp(amountGiven, v.decimalScalingFactors[index], computeRateRoundUp(v.tokenRates[index])) } -func (v *Vault) ComputeAggregateSwapFees( - totalSwapFeeAmountScaled18, aggregateSwapFeePercentage, - decimalScalingFactor, tokenRate *uint256.Int, +func (v *Vault) ComputeAggregateSwapFees(index int, totalSwapFeeAmountScaled18, aggregateSwapFeePercentage *uint256.Int, ) (totalSwapFeeAmountRaw, aggregateSwapFeeAmountRaw *uint256.Int, err error) { if totalSwapFeeAmountScaled18.Sign() > 0 { - totalSwapFeeAmountRaw, err = toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, decimalScalingFactor, tokenRate) + totalSwapFeeAmountRaw, err = toRawUndoRateRoundDown(totalSwapFeeAmountScaled18, v.decimalScalingFactors[index], v.tokenRates[index]) if err != nil { return nil, nil, err } From 74bf0b654e53cf6eb41f8f1f09989b1e344258a0 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 10:44:31 +0700 Subject: [PATCH 32/39] copy instead of lock --- pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go index f83f22de0..e1a677105 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator.go @@ -57,7 +57,11 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { // Need to detect the current hook type of pool if staticExtra.DefaultHook != "" && !hooks.IsHookSupported(staticExtra.DefaultHook) { - logger.Warnf("[%s] defaultHook is not supported now for pool %s => fallback to BaseHook", DexType, entityPool.Address) + logger.Warnf( + "[%s] Pool Address: %s | Warning: defaultHook is not supported => falling back to BaseHook", + DexType, + entityPool.Address, + ) } var hook hooks.IHook From d7fbbd08fdb94a5e08e3cb0075a9abcf4ac657b2 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 10:44:41 +0700 Subject: [PATCH 33/39] copy instead of lock --- .../balancer-v3/math/stable_math.go | 39 +++++++------------ .../balancer-v3/stable/pool_simulator.go | 6 ++- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go index fda3a2094..b5951df05 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -2,7 +2,6 @@ package math import ( "errors" - "sync" "github.com/holiman/uint256" ) @@ -16,9 +15,7 @@ var ( var StableMath *stableMath -type stableMath struct { - mu sync.RWMutex -} +type stableMath struct{} func init() { StableMath = &stableMath{} @@ -42,20 +39,17 @@ func (s *stableMath) ComputeOutGivenExactIn( // P = product of final balances but y // **************************************************************************************************************/ - s.mu.Lock() - defer s.mu.Unlock() - - balances[tokenIndexIn], err = FixPoint.Add(balances[tokenIndexIn], tokenAmountIn) - if err != nil { - return nil, err - } + // because balances will be overwritten during calculations, to avoid using locks we will make a copy for + // safety calculations across goroutines + var _balances = make([]*uint256.Int, len(balances)) + copy(_balances, balances) - finalBalanceOut, err := s.ComputeBalance(amplificationParameter, balances, invariant, tokenIndexOut) + _balances[tokenIndexIn], err = FixPoint.Add(balances[tokenIndexIn], tokenAmountIn) if err != nil { return nil, err } - balances[tokenIndexIn], err = FixPoint.Sub(balances[tokenIndexIn], tokenAmountIn) + finalBalanceOut, err := s.ComputeBalance(amplificationParameter, _balances, invariant, tokenIndexOut) if err != nil { return nil, err } @@ -88,20 +82,17 @@ func (s *stableMath) ComputeInGivenExactOut( // P = product of final balances but x // **************************************************************************************************************/ - s.mu.Lock() - defer s.mu.Unlock() - - balances[tokenIndexOut], err = FixPoint.Sub(balances[tokenIndexOut], tokenAmountOut) - if err != nil { - return nil, err - } + // because balances will be overwritten during calculations, to avoid using locks we will make a copy for + // safety calculations across goroutines + var _balances = make([]*uint256.Int, len(balances)) + copy(_balances, balances) - finalBalanceIn, err := s.ComputeBalance(amplificationParameter, balances, invariant, tokenIndexIn) + _balances[tokenIndexOut], err = FixPoint.Sub(balances[tokenIndexOut], tokenAmountOut) if err != nil { return nil, err } - balances[tokenIndexOut], err = FixPoint.Add(balances[tokenIndexOut], tokenAmountOut) + finalBalanceIn, err := s.ComputeBalance(amplificationParameter, _balances, invariant, tokenIndexIn) if err != nil { return nil, err } @@ -219,11 +210,9 @@ func (s *stableMath) ComputeInvariant(amplificationParameter *uint256.Int, balan numTokens := uint256.NewInt(uint64(len(balances))) sum := uint256.NewInt(0) - s.mu.RLock() for i := range balances { sum.Add(sum, balances[i]) } - s.mu.RUnlock() if sum.IsZero() { return sum, nil @@ -245,14 +234,12 @@ func (s *stableMath) ComputeInvariant(amplificationParameter *uint256.Int, balan // D_P = D^(n+1)/(n^n * P) D_P.Set(invariant) - s.mu.RLock() for _, balance := range balances { // D_P = D_P * D / (x_i * n) tmp.Mul(balance, numTokens) D_P.Mul(D_P, invariant) D_P.Div(D_P, tmp) } - s.mu.RUnlock() // (A * n * S / AP + D_P * n) * D numer.Mul(ampTimesN, sum) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go index 38669f030..d4279f7cd 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator.go @@ -57,7 +57,11 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) { // Need to detect the current hook type of pool if staticExtra.DefaultHook != "" && !hooks.IsHookSupported(staticExtra.DefaultHook) { - logger.Warnf("[%s] defaultHook is not supported now for pool %s => fallback to BaseHook", DexType, entityPool.Address) + logger.Warnf( + "[%s] Pool Address: %s | Warning: defaultHook is not supported => falling back to BaseHook", + DexType, + entityPool.Address, + ) } var hook hooks.IHook From 6751f28b59e82a9c51a8d9648094e32990576b36 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 11:02:54 +0700 Subject: [PATCH 34/39] update --- pkg/liquidity-source/balancer-v3/math/stable_math.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go index b5951df05..920631cd0 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -40,11 +40,11 @@ func (s *stableMath) ComputeOutGivenExactIn( **************************************************************************************************************/ // because balances will be overwritten during calculations, to avoid using locks we will make a copy for - // safety calculations across goroutines + // safety calculations across goroutines (due to this slice isn't large, just only a few elements per pool) var _balances = make([]*uint256.Int, len(balances)) copy(_balances, balances) - _balances[tokenIndexIn], err = FixPoint.Add(balances[tokenIndexIn], tokenAmountIn) + _balances[tokenIndexIn], err = FixPoint.Add(_balances[tokenIndexIn], tokenAmountIn) if err != nil { return nil, err } @@ -54,7 +54,7 @@ func (s *stableMath) ComputeOutGivenExactIn( return nil, err } - amountOut, err = FixPoint.Sub(balances[tokenIndexOut], finalBalanceOut) + amountOut, err = FixPoint.Sub(_balances[tokenIndexOut], finalBalanceOut) if err != nil { return nil, err } @@ -83,11 +83,11 @@ func (s *stableMath) ComputeInGivenExactOut( **************************************************************************************************************/ // because balances will be overwritten during calculations, to avoid using locks we will make a copy for - // safety calculations across goroutines + // safety calculations across goroutines (due to this slice isn't large, just only a few elements per pool) var _balances = make([]*uint256.Int, len(balances)) copy(_balances, balances) - _balances[tokenIndexOut], err = FixPoint.Sub(balances[tokenIndexOut], tokenAmountOut) + _balances[tokenIndexOut], err = FixPoint.Sub(_balances[tokenIndexOut], tokenAmountOut) if err != nil { return nil, err } @@ -97,7 +97,7 @@ func (s *stableMath) ComputeInGivenExactOut( return nil, err } - amountIn, err = FixPoint.Sub(finalBalanceIn, balances[tokenIndexIn]) + amountIn, err = FixPoint.Sub(finalBalanceIn, _balances[tokenIndexIn]) if err != nil { return nil, err } From 0d1d4da556ce839e2663a002956c781b49913240 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 15:09:47 +0700 Subject: [PATCH 35/39] finish --- .../balancer-v3/stable/pool_simulator_test.go | 36 ++++---- .../balancer-v3/vault/vault.go | 10 +-- .../weighted/pool_simulator_test.go | 87 +++++++++++++++++-- 3 files changed, 108 insertions(+), 25 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go index 15ce4d95f..5112ba8a2 100644 --- a/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/stable/pool_simulator_test.go @@ -51,8 +51,8 @@ func TestCalcAmountOut(t *testing.T) { } tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - expectedAmountOut := "1000347432097063734" - expectedSwapFee := "23602591917350" + expectedAmountOut := "847659059612802449" + expectedSwapFee := "20000000000000" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ @@ -101,8 +101,8 @@ func TestCalcAmountOut(t *testing.T) { } tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - expectedAmountOut := "999611352568542438" - expectedSwapFee := "16947291272107" + expectedAmountOut := "1179670809463600235" + expectedSwapFee := "20000000000000" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ @@ -192,7 +192,7 @@ func TestCalcAmountOut(t *testing.T) { tokenAmountIn := poolpkg.TokenAmount{ Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", - Amount: big.NewInt(991000), // less than MINIMUM_TRADE_AMOUNT (1000000) + Amount: big.NewInt(840000), // less than MINIMUM_TRADE_AMOUNT (1000000) } tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" @@ -207,8 +207,9 @@ func TestCalcAmountOut(t *testing.T) { assert.Equal(t, vault.ErrAmountOutTooSmall, err) }) + // Mock state from https://etherscan.io/tx/0x92f38cf0d3c11276c220d4790c57a1f316b99a5e676e182b81d576b71f779012 t.Run("5. should return OK", func(t *testing.T) { - poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816509,"reserves":["619469949959861143118","1841897390394044699179"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"625118423384960111487\",\"2193481638791868130942\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009118236365565374\",\"1190881560629502583\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536418}` + poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816509,"reserves":["619469949959861143118","1841897390394044699179"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"100000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"625134427981060649446\",\"2193655709385971229274\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009146942992102450\",\"1190985849893040213\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536418}` var pool entity.Pool err := json.Unmarshal([]byte(poolStr), &pool) @@ -217,15 +218,17 @@ func TestCalcAmountOut(t *testing.T) { s, err := NewPoolSimulator(pool) assert.Nil(t, err) + amountIn, _ := new(big.Int).SetString("1189123158260799643", 10) + tokenAmountIn := poolpkg.TokenAmount{ Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", - Amount: big.NewInt(1e18), + Amount: amountIn, } tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" // expected - expectedAmountOut := "1000445828427074752" - expectedSwapFee := "23602418779361" + expectedAmountOut := "1008017884740935660" + expectedSwapFee := "23782463165215" // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { @@ -434,8 +437,9 @@ func TestCalcAmountIn(t *testing.T) { assert.Equal(t, vault.ErrAmountOutTooSmall, err) }) + // Mock state from https://etherscan.io/tx/0x92f38cf0d3c11276c220d4790c57a1f316b99a5e676e182b81d576b71f779012 t.Run("5. should return OK", func(t *testing.T) { - poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816256,"reserves":["619469960947444228741","1841897397011807636504"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"500000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"625118323594477223912\",\"2193481567863040629311\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009118057376654946\",\"1190881517842211888\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536397}` + poolStr := `{"address":"0xc4ce391d82d164c166df9c8336ddf84206b2f812","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816509,"reserves":["619469949959861143118","1841897390394044699179"],"tokens":[{"address":"0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9","weight":1,"swappable":true},{"address":"0x775f661b0bd1739349b9a2a3ef60be277c5d2d29","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"20000000000000\",\"aggregateSwapFeePercentage\":\"100000000000000000\",\"amplificationParameter\":\"5000000\",\"balancesLiveScaled18\":[\"625134427981060649446\",\"2193655709385971229274\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1009146942992102450\",\"1190985849893040213\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536418}` var pool entity.Pool err := json.Unmarshal([]byte(poolStr), &pool) @@ -444,15 +448,17 @@ func TestCalcAmountIn(t *testing.T) { s, err := NewPoolSimulator(pool) assert.Nil(t, err) + amountOut, _ := new(big.Int).SetString("1008017884740935660", 10) + tokenAmountOut := poolpkg.TokenAmount{ - Token: "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9", - Amount: big.NewInt(1e18), + Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", + Amount: amountOut, } - tokenIn := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" + tokenIn := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" // expected - expectedAmountIn := "847783902418696208" - expectedSwapFee := "16955678048374" + expectedAmountIn := "1189123158260799642" + expectedSwapFee := "23782463165215" // actual result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { diff --git a/pkg/liquidity-source/balancer-v3/vault/vault.go b/pkg/liquidity-source/balancer-v3/vault/vault.go index 5506e6531..779404cdb 100644 --- a/pkg/liquidity-source/balancer-v3/vault/vault.go +++ b/pkg/liquidity-source/balancer-v3/vault/vault.go @@ -51,7 +51,7 @@ func (v *Vault) Swap( vaultSwapParams shared.VaultSwapParams, onSwap func(param shared.PoolSwapParams) (*uint256.Int, error), ) (*uint256.Int, *uint256.Int, *uint256.Int, error) { - amountGivenScaled18, err := v.ComputeAmountGivenScaled18(vaultSwapParams.Kind, vaultSwapParams.IndexOut, vaultSwapParams.AmountGivenRaw) + amountGivenScaled18, err := v.ComputeAmountGivenScaled18(vaultSwapParams) if err != nil { return nil, nil, nil, err } @@ -153,12 +153,12 @@ func (v *Vault) Swap( return amountCalculated, totalSwapFee, aggregateFee, nil } -func (v *Vault) ComputeAmountGivenScaled18(kind shared.SwapKind, index int, amountGiven *uint256.Int) (*uint256.Int, error) { - if kind == shared.EXACT_IN { - return toScaled18ApplyRateRoundDown(amountGiven, v.decimalScalingFactors[index], v.tokenRates[index]) +func (v *Vault) ComputeAmountGivenScaled18(param shared.VaultSwapParams) (*uint256.Int, error) { + if param.Kind == shared.EXACT_IN { + return toScaled18ApplyRateRoundDown(param.AmountGivenRaw, v.decimalScalingFactors[param.IndexIn], v.tokenRates[param.IndexIn]) } - return toScaled18ApplyRateRoundUp(amountGiven, v.decimalScalingFactors[index], computeRateRoundUp(v.tokenRates[index])) + return toScaled18ApplyRateRoundUp(param.AmountGivenRaw, v.decimalScalingFactors[param.IndexOut], computeRateRoundUp(v.tokenRates[param.IndexOut])) } func (v *Vault) ComputeAggregateSwapFees(index int, totalSwapFeeAmountScaled18, aggregateSwapFeePercentage *uint256.Int, diff --git a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go index 0bea0653a..167ac8e68 100644 --- a/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go +++ b/pkg/liquidity-source/balancer-v3/weighted/pool_simulator_test.go @@ -4,9 +4,11 @@ import ( "math/big" "testing" + "github.com/goccy/go-json" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/hooks" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/shared" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/balancer-v3/vault" @@ -49,8 +51,8 @@ func TestCalcAmountOut(t *testing.T) { } tokenOut := "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29" - expectedAmountOut := "12278617355364789838" - expectedSwapFee := "2103630945830047" + expectedAmountOut := "14588365766046319212" + expectedSwapFee := "2500000000000000" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ @@ -99,8 +101,8 @@ func TestCalcAmountOut(t *testing.T) { } tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" - expectedAmountOut := "80912682117686948" - expectedSwapFee := "2971053459918506" + expectedAmountOut := "68085611533624911" + expectedSwapFee := "2500000000000000" result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ @@ -190,7 +192,7 @@ func TestCalcAmountOut(t *testing.T) { tokenAmountIn := poolpkg.TokenAmount{ Token: "0x775f661b0bd1739349b9a2a3ef60be277c5d2d29", - Amount: big.NewInt(991000), // less than MINIMUM_TRADE_AMOUNT (1000000) + Amount: big.NewInt(1300000), // less than MINIMUM_TRADE_AMOUNT (1000000) } tokenOut := "0x0fe906e030a44ef24ca8c7dc7b7c53a6c4f00ce9" @@ -204,6 +206,44 @@ func TestCalcAmountOut(t *testing.T) { assert.Error(t, err) assert.Equal(t, vault.ErrAmountOutTooSmall, err) }) + + // Mock state from https://gnosisscan.io/tx/0x14579e3588ad7a76bfd850168baf41a581ed049c3a355a6c3c891cdccc2b0836 + t.Run("5. should return OK", func(t *testing.T) { + poolStr := `{"address":"0x272d6be442e30d7c87390edeb9b96f1e84cecd8d","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816509,"reserves":["619469949959861143118","1841897390394044699179"],"tokens":[{"address":"0x773cda0cade2a3d86e6d4e30699d40bb95174ff2","weight":1,"swappable":true},{"address":"0x7c16f0185a26db0ae7a9377f23bc18ea7ce5d644","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"2500000000000000\",\"aggregateSwapFeePercentage\":\"100000000000000000\",\"normalizedWeights\":[\"500000000000000000\",\"500000000000000000\"],\"balancesLiveScaled18\":[\"718362766363614682950\",\"8898955182296732614690\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1189577407040530520\",\"1000892729180982664\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536418}` + + var pool entity.Pool + err := json.Unmarshal([]byte(poolStr), &pool) + assert.Nil(t, err) + + s, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + amountIn, _ := new(big.Int).SetString("999108067073568238", 10) + + tokenAmountIn := poolpkg.TokenAmount{ + Token: "0x7c16f0185a26db0ae7a9377f23bc18ea7ce5d644", + Amount: amountIn, + } + tokenOut := "0x773cda0cade2a3d86e6d4e30699d40bb95174ff2" + + // expected + expectedAmountOut := "67682487794870862" + expectedSwapFee := "2497770167683920" + + // actual + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountOutResult, error) { + return s.CalcAmountOut(poolpkg.CalcAmountOutParams{ + TokenAmountIn: tokenAmountIn, + TokenOut: tokenOut, + }) + }) + + assert.Nil(t, err) + + // assert + assert.Equal(t, expectedAmountOut, result.TokenAmountOut.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) } func TestCalcAmountIn(t *testing.T) { @@ -396,4 +436,41 @@ func TestCalcAmountIn(t *testing.T) { assert.Error(t, err) assert.Equal(t, vault.ErrAmountOutTooSmall, err) }) + + t.Run("5. should return OK", func(t *testing.T) { + poolStr := `{"address":"0x272d6be442e30d7c87390edeb9b96f1e84cecd8d","exchange":"balancer-v3-stable","type":"balancer-v3-stable","timestamp":1735816509,"reserves":["619469949959861143118","1841897390394044699179"],"tokens":[{"address":"0x773cda0cade2a3d86e6d4e30699d40bb95174ff2","weight":1,"swappable":true},{"address":"0x7c16f0185a26db0ae7a9377f23bc18ea7ce5d644","weight":1,"swappable":true}],"extra":"{\"hooksConfig\":{\"enableHookAdjustedAmounts\":false,\"shouldCallComputeDynamicSwapFee\":false,\"shouldCallBeforeSwap\":false,\"shouldCallAfterSwap\":false},\"staticSwapFeePercentage\":\"2500000000000000\",\"aggregateSwapFeePercentage\":\"100000000000000000\",\"normalizedWeights\":[\"500000000000000000\",\"500000000000000000\"],\"balancesLiveScaled18\":[\"718362766363614682950\",\"8898955182296732614690\"],\"decimalScalingFactors\":[\"1\",\"1\"],\"tokenRates\":[\"1189577407040530520\",\"1000892729180982664\"],\"isVaultPaused\":false,\"isPoolPaused\":false,\"isPoolInRecoveryMode\":false}","staticExtra":"{\"vault\":\"0xba1333333333a1ba1108e8412f11850a5c319ba9\",\"defaultHook\":\"\"}","blockNumber":21536418}` + + var pool entity.Pool + err := json.Unmarshal([]byte(poolStr), &pool) + assert.Nil(t, err) + + s, err := NewPoolSimulator(pool) + assert.Nil(t, err) + + amountOut, _ := new(big.Int).SetString("67682487794870862", 10) + + tokenAmountOut := poolpkg.TokenAmount{ + Token: "0x773cda0cade2a3d86e6d4e30699d40bb95174ff2", + Amount: amountOut, + } + tokenIn := "0x7c16f0185a26db0ae7a9377f23bc18ea7ce5d644" + + // expected + expectedAmountIn := "999108067073574530" + expectedSwapFee := "2497770167683936" + + // actual + result, err := testutil.MustConcurrentSafe(t, func() (*poolpkg.CalcAmountInResult, error) { + return s.CalcAmountIn(poolpkg.CalcAmountInParams{ + TokenAmountOut: tokenAmountOut, + TokenIn: tokenIn, + }) + }) + + assert.Nil(t, err) + + // assert + assert.Equal(t, expectedAmountIn, result.TokenAmountIn.Amount.String()) + assert.Equal(t, expectedSwapFee, result.Fee.Amount.String()) + }) } From 8bab997ae04cbf5fdfa3d44b49e0a4ec8c3e4e37 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 15:44:35 +0700 Subject: [PATCH 36/39] use v3Utils lib for optimization --- .../balancer-v3/math/fixed_point_math.go | 57 ++++--------------- 1 file changed, 11 insertions(+), 46 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go index 1edfa07b0..0c8380d02 100644 --- a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go @@ -3,6 +3,7 @@ package math import ( "errors" + v3Utils "github.com/KyberNetwork/uniswapv3-sdk-uint256/utils" "github.com/holiman/uint256" ) @@ -28,69 +29,33 @@ func init() { } func (f *fixPoint) MulDivUp(a, b, c *uint256.Int) (*uint256.Int, error) { - if c.IsZero() { - return nil, ErrZeroDivision - } - - product, err := f.Mul(a, b) - if err != nil { - return nil, err - } - - // result = a == 0 ? 0 : (a * b - 1) / c + 1 - if product.IsZero() { - return ZERO, nil - } - - product.Sub(product, ONE) - product.Div(product, c) - product.Add(product, ONE) - - return product, nil + return v3Utils.MulDivRoundingUp(a, b, c) } func (f *fixPoint) MulUp(a, b *uint256.Int) (*uint256.Int, error) { - product, err := f.Mul(a, b) - if err != nil { - return nil, ErrMulOverflow - } - - // result = product == 0 ? 0 : ((product - 1) / FixedPoint.ONE) + 1 - if product.IsZero() { - return ZERO, nil - } - - product.Sub(product, ONE) - product.Div(product, ONE_E18) - product.Add(product, ONE) - - return product, nil + return v3Utils.MulDivRoundingUp(a, b, ONE_E18) } func (f *fixPoint) MulDown(a, b *uint256.Int) (*uint256.Int, error) { - product, err := f.Mul(a, b) - if err != nil { + res, overflow := new(uint256.Int).MulDivOverflow(a, b, ONE_E18) + if overflow { return nil, ErrMulOverflow } - return product.Div(product, ONE_E18), nil + return res, nil } func (f *fixPoint) DivUp(a, b *uint256.Int) (*uint256.Int, error) { - return f.MulDivUp(a, ONE_E18, b) + return v3Utils.MulDivRoundingUp(a, ONE_E18, b) } func (f *fixPoint) DivDown(a, b *uint256.Int) (*uint256.Int, error) { - if b.IsZero() { - return nil, ErrZeroDivision - } - - aInflated, err := f.Mul(a, ONE_E18) - if err != nil { - return nil, err + res, overflow := new(uint256.Int).MulDivOverflow(a, ONE_E18, b) + if overflow { + return nil, ErrMulOverflow } - return aInflated.Div(aInflated, b), nil + return res, nil } func (f *fixPoint) DivRawUp(a, b *uint256.Int) (*uint256.Int, error) { From 5ef0ecbf8bb1126ef326e2cd2457223d0f3a6129 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 15:54:17 +0700 Subject: [PATCH 37/39] add condition --- pkg/liquidity-source/balancer-v3/math/fixed_point_math.go | 8 ++++++++ .../balancer-v3/math/weighted_math_test.go | 1 + 2 files changed, 9 insertions(+) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go index 0c8380d02..41633c671 100644 --- a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go @@ -46,10 +46,18 @@ func (f *fixPoint) MulDown(a, b *uint256.Int) (*uint256.Int, error) { } func (f *fixPoint) DivUp(a, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + return v3Utils.MulDivRoundingUp(a, ONE_E18, b) } func (f *fixPoint) DivDown(a, b *uint256.Int) (*uint256.Int, error) { + if b.IsZero() { + return nil, ErrZeroDivision + } + res, overflow := new(uint256.Int).MulDivOverflow(a, ONE_E18, b) if overflow { return nil, ErrMulOverflow diff --git a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go index c08a7e853..0cf5e825b 100644 --- a/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go +++ b/pkg/liquidity-source/balancer-v3/math/weighted_math_test.go @@ -38,6 +38,7 @@ func TestWeightedMath_ComputeOutGivenExactIn(t *testing.T) { expectedAmount: nil, expectedErr: ErrMaxInRatio, }, + { name: "Zero amountIn", balanceIn: uint256.NewInt(1000), From 4fd787aca36fbab9d80d71bb58550c76add5ebbc Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Fri, 3 Jan 2025 16:00:11 +0700 Subject: [PATCH 38/39] cleanup --- .../balancer-v3/math/fixed_point_math.go | 3 +-- pkg/liquidity-source/balancer-v3/math/stable_math.go | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go index 41633c671..04fec7a3d 100644 --- a/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go +++ b/pkg/liquidity-source/balancer-v3/math/fixed_point_math.go @@ -10,9 +10,8 @@ import ( var ( ErrAddOverflow = errors.New("ADD_OVERFLOW") ErrSubOverflow = errors.New("SUB_OVERFLOW") - ErrZeroDivision = errors.New("ZERO_DIVISION") - ErrDivInternal = errors.New("DIV_INTERNAL") ErrMulOverflow = errors.New("MUL_OVERFLOW") + ErrZeroDivision = errors.New("ZERO_DIVISION") ONE_E18 = uint256.NewInt(1e18) // 18 decimal places TWO_E18 = new(uint256.Int).Mul(ONE_E18, TWO) diff --git a/pkg/liquidity-source/balancer-v3/math/stable_math.go b/pkg/liquidity-source/balancer-v3/math/stable_math.go index 920631cd0..7f59f481e 100644 --- a/pkg/liquidity-source/balancer-v3/math/stable_math.go +++ b/pkg/liquidity-source/balancer-v3/math/stable_math.go @@ -10,7 +10,7 @@ var ( ErrStableInvariantDidNotConverge = errors.New("stable invariant didn't converge") ErrStableComputeBalanceDidNotConverge = errors.New("stable computeBalance didn't converge") - _AMP_PRECISION = uint256.NewInt(1e3) + AMP_PRECISION = uint256.NewInt(1e3) ) var StableMath *stableMath @@ -135,7 +135,7 @@ func (s *stableMath) ComputeBalance( invariantSquared := new(uint256.Int).Mul(invariant, invariant) // c = (D^2 * AP)/(An * P_D) * x_i - numerator := new(uint256.Int).Mul(invariantSquared, _AMP_PRECISION) + numerator := new(uint256.Int).Mul(invariantSquared, AMP_PRECISION) denominator := new(uint256.Int).Mul(ampTimesN, balanceProduct) c, err := FixPoint.DivRawUp(numerator, denominator) @@ -146,7 +146,7 @@ func (s *stableMath) ComputeBalance( c.Mul(c, balances[tokenIndex]) // b = S + (D * AP)/An - b := new(uint256.Int).Mul(invariant, _AMP_PRECISION) + b := new(uint256.Int).Mul(invariant, AMP_PRECISION) b.Div(b, ampTimesN) b.Add(b, sumBalances) @@ -243,15 +243,15 @@ func (s *stableMath) ComputeInvariant(amplificationParameter *uint256.Int, balan // (A * n * S / AP + D_P * n) * D numer.Mul(ampTimesN, sum) - numer.Div(numer, _AMP_PRECISION) + numer.Div(numer, AMP_PRECISION) tmp.Mul(D_P, numTokens) numer.Add(numer, tmp) numer.Mul(numer, invariant) // ((A * n - AP) * D / AP + (n + 1) * D_P) - denom.Sub(ampTimesN, _AMP_PRECISION) + denom.Sub(ampTimesN, AMP_PRECISION) denom.Mul(denom, invariant) - denom.Div(denom, _AMP_PRECISION) + denom.Div(denom, AMP_PRECISION) tmp.AddUint64(numTokens, 1) tmp.Mul(tmp, D_P) denom.Add(denom, tmp) From b01c4b01faa94a9181e5d6751364099d7cb2c4f9 Mon Sep 17 00:00:00 2001 From: sunspirit99 Date: Wed, 15 Jan 2025 12:05:51 +0700 Subject: [PATCH 39/39] update --- pkg/valueobject/exchange.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/valueobject/exchange.go b/pkg/valueobject/exchange.go index 03921b415..5111688d8 100644 --- a/pkg/valueobject/exchange.go +++ b/pkg/valueobject/exchange.go @@ -180,8 +180,10 @@ var ( ExchangeGyroscope3CLP Exchange = "gyroscope-3clp" ExchangeGyroscopeECLP Exchange = "gyroscope-eclp" - ExchangeBalancerV3Weighted Exchange = "balancer-v3-weighted" - ExchangeBalancerV3Stable Exchange = "balancer-v3-stable" + ExchangeBalancerV3Weighted Exchange = "balancer-v3-weighted" + ExchangeBalancerV3Stable Exchange = "balancer-v3-stable" + ExchangeBeethovenXV3Weighted Exchange = "beethovenx-v3-weighted" + ExchangeBeethovenXV3Stable Exchange = "beethovenx-v3-stable" ExchangeSynthSwapPerp Exchange = "synthswap-perp" ExchangeSwapBasedPerp Exchange = "swapbased-perp" @@ -543,6 +545,8 @@ var AMMSourceSet = map[Exchange]struct{}{ ExchangeBalancerV2ComposableStable: {}, ExchangeBalancerV3Weighted: {}, ExchangeBalancerV3Stable: {}, + ExchangeBeethovenXV3Weighted: {}, + ExchangeBeethovenXV3Stable: {}, ExchangeBeethovenXWeighted: {}, ExchangeBeethovenXStable: {}, ExchangeBeethovenXComposableStable: {},