From c62b941a6704fec8f8128fcd6ae0ad021743746a Mon Sep 17 00:00:00 2001 From: SunSpirit <48086732+sunspirit99@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:13:14 +0700 Subject: [PATCH] feat : Integrate swap-x-v2 (#685) --- pkg/liquidity-source/solidly-v2/constant.go | 2 +- .../solidly-v2/pool_list_updater.go | 2 +- pkg/liquidity-source/swap-x-v2/abis.go | 34 + .../swap-x-v2/abis/Factory.json | 526 ++++++++ pkg/liquidity-source/swap-x-v2/abis/Pool.json | 1132 +++++++++++++++++ pkg/liquidity-source/swap-x-v2/config.go | 8 + pkg/liquidity-source/swap-x-v2/constant.go | 17 + pkg/liquidity-source/swap-x-v2/embed.go | 9 + .../swap-x-v2/pool_list_updater.go | 355 ++++++ .../swap-x-v2/pool_tracker.go | 167 +++ pkg/pooltypes/pooltypes.go | 3 + 11 files changed, 2253 insertions(+), 2 deletions(-) create mode 100644 pkg/liquidity-source/swap-x-v2/abis.go create mode 100644 pkg/liquidity-source/swap-x-v2/abis/Factory.json create mode 100644 pkg/liquidity-source/swap-x-v2/abis/Pool.json create mode 100644 pkg/liquidity-source/swap-x-v2/config.go create mode 100644 pkg/liquidity-source/swap-x-v2/constant.go create mode 100644 pkg/liquidity-source/swap-x-v2/embed.go create mode 100644 pkg/liquidity-source/swap-x-v2/pool_list_updater.go create mode 100644 pkg/liquidity-source/swap-x-v2/pool_tracker.go diff --git a/pkg/liquidity-source/solidly-v2/constant.go b/pkg/liquidity-source/solidly-v2/constant.go index 99c40168c..fdd6ec3d3 100644 --- a/pkg/liquidity-source/solidly-v2/constant.go +++ b/pkg/liquidity-source/solidly-v2/constant.go @@ -16,7 +16,7 @@ const ( factoryMethodIsPaused = "isPaused" factoryMethodAllPairs = "allPairs" factoryMethodAllPairsLength = "allPairsLength" - factoryMethodStableFee = "stableFees" + factoryMethodStableFees = "stableFees" factoryMethodVolatileFees = "volatileFees" poolMethodMetadata = "metadata" diff --git a/pkg/liquidity-source/solidly-v2/pool_list_updater.go b/pkg/liquidity-source/solidly-v2/pool_list_updater.go index 7891b69a0..37d75c5f1 100644 --- a/pkg/liquidity-source/solidly-v2/pool_list_updater.go +++ b/pkg/liquidity-source/solidly-v2/pool_list_updater.go @@ -254,7 +254,7 @@ func (u *PoolsListUpdater) listPoolData( listPoolMetadataRequest.AddCall(ðrpc.Call{ ABI: factoryABI, Target: u.config.FactoryAddress, - Method: factoryMethodStableFee, + Method: factoryMethodStableFees, Params: []interface{}{}, }, []interface{}{&stableFee}) listPoolMetadataRequest.AddCall(ðrpc.Call{ diff --git a/pkg/liquidity-source/swap-x-v2/abis.go b/pkg/liquidity-source/swap-x-v2/abis.go new file mode 100644 index 000000000..e6b975ffd --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/abis.go @@ -0,0 +1,34 @@ +package swapxv2 + +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/swap-x-v2/abis/Factory.json b/pkg/liquidity-source/swap-x-v2/abis/Factory.json new file mode 100644 index 000000000..c9372a6ca --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/abis/Factory.json @@ -0,0 +1,526 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": 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": [], + "name": "MAX_FEE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_TREASURY_FEE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptFeeManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "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": [ + { + "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": "dibs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_stable", + "type": "bool" + } + ], + "name": "getFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getInitializable", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "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": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "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": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pairCodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "pairs", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingFeeManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_dibs", + "type": "address" + } + ], + "name": "setDibs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_stable", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "setFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_feeManager", + "type": "address" + } + ], + "name": "setFeeManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_state", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_feehandler", + "type": "address" + } + ], + "name": "setStakingFeeAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newFee", + "type": "uint256" + } + ], + "name": "setStakingFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_tresFee", + "type": "uint256" + } + ], + "name": "setTreasuryFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stableFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingFeeHandler", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingNFTFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "volatileFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/pkg/liquidity-source/swap-x-v2/abis/Pool.json b/pkg/liquidity-source/swap-x-v2/abis/Pool.json new file mode 100644 index 000000000..43f35ab97 --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/abis/Pool.json @@ -0,0 +1,1132 @@ +[ + { + "inputs": [], + "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": "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": [], + "name": "claimStakingFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "claimable0", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "claimable1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "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": "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": "index0", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "index1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isStable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "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 Pair.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" + } + ], + "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": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyIndex0", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyIndex1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "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": "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/swap-x-v2/config.go b/pkg/liquidity-source/swap-x-v2/config.go new file mode 100644 index 000000000..21ded7467 --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/config.go @@ -0,0 +1,8 @@ +package swapxv2 + +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/swap-x-v2/constant.go b/pkg/liquidity-source/swap-x-v2/constant.go new file mode 100644 index 000000000..9083e30b1 --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/constant.go @@ -0,0 +1,17 @@ +package swapxv2 + +const ( + DexType = "swap-x-v2" +) + +const ( + factoryMethodIsPaused = "isPaused" + factoryMethodAllPairs = "allPairs" + factoryMethodAllPairsLength = "allPairsLength" + factoryMethodGetFee = "getFee" + factoryMethodStableFee = "stableFee" + factoryMethodVolatileFee = "volatileFee" + + poolMethodMetadata = "metadata" + poolMethodGetReserves = "getReserves" +) diff --git a/pkg/liquidity-source/swap-x-v2/embed.go b/pkg/liquidity-source/swap-x-v2/embed.go new file mode 100644 index 000000000..299d45376 --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/embed.go @@ -0,0 +1,9 @@ +package swapxv2 + +import _ "embed" + +//go:embed abis/Pool.json +var poolABIJson []byte + +//go:embed abis/Factory.json +var factoryABIJson []byte diff --git a/pkg/liquidity-source/swap-x-v2/pool_list_updater.go b/pkg/liquidity-source/swap-x-v2/pool_list_updater.go new file mode 100644 index 000000000..4b0e1c6f4 --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/pool_list_updater.go @@ -0,0 +1,355 @@ +package swapxv2 + +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 + } + + PoolsListUpdaterMetadata struct { + Offset int `json:"offset"` + } +) + +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: factoryMethodIsPaused, + Params: nil, + }, []interface{}{&pairFactoryData.IsPaused}) + + 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 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 { + var 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, +) ([]velodromev2.PoolMetadata, *big.Int, *big.Int, *big.Int, error) { + var ( + stableFee, volatileFee *big.Int + + poolMetadataList = make([]velodromev2.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: factoryMethodVolatileFee, + Params: []interface{}{}, + }, []interface{}{&volatileFee}) + + 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, volatileFee, resp.BlockNumber, nil +} + +func (u *PoolsListUpdater) newMetadata(newOffset int) ([]byte, error) { + metadata := 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 velodromev2.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/swap-x-v2/pool_tracker.go b/pkg/liquidity-source/swap-x-v2/pool_tracker.go new file mode 100644 index 000000000..56d0be514 --- /dev/null +++ b/pkg/liquidity-source/swap-x-v2/pool_tracker.go @@ -0,0 +1,167 @@ +package swapxv2 + +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") + }() + + var staticExtra velodromev2.PoolStaticExtra + if err := json.Unmarshal([]byte(p.StaticExtra), &staticExtra); err != nil { + return p, err + } + + reserveData, isPaused, fee, blockNumber, err := d.getPoolData(ctx, p.Address, staticExtra.Stable, overrides) + if err != nil { + return p, err + } + + return d.updatePool(p, reserveData, isPaused, fee, blockNumber) +} + +func (d *PoolTracker) getPoolData( + ctx context.Context, + poolAddress string, + stable bool, + 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) + } + + if stable { + req.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: d.config.FactoryAddress, + Method: factoryMethodStableFee, + Params: nil, + }, []interface{}{&fee}) + } else { + req.AddCall(ðrpc.Call{ + ABI: factoryABI, + Target: d.config.FactoryAddress, + Method: factoryMethodVolatileFee, + Params: nil, + }, []interface{}{&fee}) + } + 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: 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/pooltypes/pooltypes.go b/pkg/pooltypes/pooltypes.go index 7bf637985..e0b1cbb43 100644 --- a/pkg/pooltypes/pooltypes.go +++ b/pkg/pooltypes/pooltypes.go @@ -58,6 +58,7 @@ import ( 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" + swapxv2 "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swap-x-v2" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swell/rsweth" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/swell/sweth" syncswapv2aqua "github.com/KyberNetwork/kyberswap-dex-lib/pkg/liquidity-source/syncswapv2/aqua" @@ -275,6 +276,7 @@ type Types struct { LO1inch string VirtualFun string BeetsSS string + SwapXV2 string } var ( @@ -420,5 +422,6 @@ var ( LO1inch: lo1inch.DexType, VirtualFun: virtualfun.DexType, BeetsSS: beetsss.DexType, + SwapXV2: swapxv2.DexType, } )