diff --git a/package.json b/package.json index 0d8297852..5f0a33edf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "4.0.5", + "version": "4.0.8", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", diff --git a/src/abi/fluid-dex/fluid-dex.abi.json b/src/abi/fluid-dex/fluid-dex.abi.json index 14ada197a..cdd5e0f1a 100644 --- a/src/abi/fluid-dex/fluid-dex.abi.json +++ b/src/abi/fluid-dex/fluid-dex.abi.json @@ -1,9 +1,119 @@ [ + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "dexId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "liquidity", + "type": "address" + }, + { + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "shift", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "address", + "name": "colOperations", + "type": "address" + }, + { + "internalType": "address", + "name": "debtOperations", + "type": "address" + }, + { + "internalType": "address", + "name": "perfectOperationsAndSwapOut", + "type": "address" + } + ], + "internalType": "struct Structs.Implementations", + "name": "implementations", + "type": "tuple" + }, + { + "internalType": "address", + "name": "deployerContract", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "supplyToken0Slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "borrowToken0Slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "supplyToken1Slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "borrowToken1Slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "exchangePriceToken0Slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "exchangePriceToken1Slot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "oracleMapping", + "type": "uint256" + } + ], + "internalType": "struct Structs.ConstantViews", + "name": "constantViews_", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [ { "internalType": "uint256", - "name": "errorId", + "name": "errorId_", "type": "uint256" } ], @@ -14,7 +124,18 @@ "inputs": [ { "internalType": "uint256", - "name": "shares", + "name": "errorId", + "type": "uint256" + } + ], + "name": "FluidDexFactoryError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares_", "type": "uint256" } ], @@ -87,7 +208,7 @@ "type": "uint256" } ], - "internalType": "struct IFluidDexT1.PricesAndExchangePrice", + "internalType": "struct Structs.PricesAndExchangePrice", "name": "pex_", "type": "tuple" } @@ -117,6 +238,346 @@ "name": "FluidDexSwapResult", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorId_", + "type": "uint256" + } + ], + "name": "FluidLiquidityCalcsError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorId_", + "type": "uint256" + } + ], + "name": "FluidSafeTransferError", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "int256", + "name": "routing", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amtOut", + "type": "uint256" + } + ], + "name": "LogArbitrage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "LogBorrowDebtLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token0Amt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token1Amt", + "type": "uint256" + } + ], + "name": "LogBorrowPerfectDebtLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "LogDepositColLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token0Amt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token1Amt", + "type": "uint256" + } + ], + "name": "LogDepositPerfectColLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + ], + "name": "LogPauseSwapAndArbitrage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + ], + "name": "LogUnpauseSwapAndArbitrage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token0Amt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token1Amt", + "type": "uint256" + } + ], + "name": "LogPaybackDebtInOneToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "LogPaybackDebtLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token0Amt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token1Amt", + "type": "uint256" + } + ], + "name": "LogPaybackPerfectDebtLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token0Amt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token1Amt", + "type": "uint256" + } + ], + "name": "LogWithdrawColInOneToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "LogWithdrawColLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token0Amt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "token1Amt", + "type": "uint256" + } + ], + "name": "LogWithdrawPerfectColLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "swap0to1", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, { "inputs": [], "name": "DEX_ID", @@ -148,9 +609,9 @@ "type": "uint256" }, { - "internalType": "bool", - "name": "estimate_", - "type": "bool" + "internalType": "address", + "name": "to_", + "type": "address" } ], "name": "borrow", @@ -182,9 +643,9 @@ "type": "uint256" }, { - "internalType": "bool", - "name": "estimate_", - "type": "bool" + "internalType": "address", + "name": "to_", + "type": "address" } ], "name": "borrowPerfect", @@ -248,11 +709,11 @@ }, { "internalType": "address", - "name": "perfectOperationsAndOracle", + "name": "perfectOperationsAndSwapOut", "type": "address" } ], - "internalType": "struct IFluidDexT1.Implementations", + "internalType": "struct Structs.Implementations", "name": "implementations", "type": "tuple" }, @@ -307,7 +768,7 @@ "type": "uint256" } ], - "internalType": "struct IFluidDexT1.ConstantViews", + "internalType": "struct Structs.ConstantViews", "name": "constantsView_", "type": "tuple" } @@ -342,7 +803,7 @@ "type": "uint256" } ], - "internalType": "struct IFluidDexT1.ConstantViews2", + "internalType": "struct Structs.ConstantViews2", "name": "constantsView2_", "type": "tuple" } @@ -476,7 +937,7 @@ "type": "uint256" } ], - "internalType": "struct IFluidDexT1.CollateralReserves", + "internalType": "struct Structs.CollateralReserves", "name": "c_", "type": "tuple" } @@ -547,7 +1008,7 @@ "type": "uint256" } ], - "internalType": "struct IFluidDexT1.DebtReserves", + "internalType": "struct Structs.DebtReserves", "name": "d_", "type": "tuple" } @@ -562,6 +1023,29 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "token_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data_", + "type": "bytes" + } + ], + "name": "liquidityCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -605,7 +1089,7 @@ "type": "uint256" } ], - "internalType": "struct IFluidDexT1.Oracle[]", + "internalType": "struct Structs.Oracle[]", "name": "twaps_", "type": "tuple[]" }, @@ -778,6 +1262,40 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "swap0to1_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amountIn_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin_", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to_", + "type": "address" + } + ], + "name": "swapInWithCallback", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut_", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { @@ -812,6 +1330,40 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "swap0to1_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amountOut_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax_", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to_", + "type": "address" + } + ], + "name": "swapOutWithCallback", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn_", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { @@ -830,9 +1382,9 @@ "type": "uint256" }, { - "internalType": "bool", - "name": "estimate_", - "type": "bool" + "internalType": "address", + "name": "to_", + "type": "address" } ], "name": "withdraw", @@ -864,9 +1416,9 @@ "type": "uint256" }, { - "internalType": "bool", - "name": "estimate_", - "type": "bool" + "internalType": "address", + "name": "to_", + "type": "address" } ], "name": "withdrawPerfect", @@ -903,9 +1455,9 @@ "type": "uint256" }, { - "internalType": "bool", - "name": "estimate_", - "type": "bool" + "internalType": "address", + "name": "to_", + "type": "address" } ], "name": "withdrawPerfectInOneToken", @@ -918,5 +1470,9 @@ ], "stateMutability": "nonpayable", "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" } ] diff --git a/src/abi/usual-m-usd0/usualCollateralDao.abi.json b/src/abi/usual-m-usd0/usualCollateralDao.abi.json new file mode 100644 index 000000000..9fe20473a --- /dev/null +++ b/src/abi/usual-m-usd0/usualCollateralDao.abi.json @@ -0,0 +1,681 @@ +[ + { "inputs": [], "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": "AmountIsZero", "type": "error" }, + { "inputs": [], "name": "AmountTooBig", "type": "error" }, + { "inputs": [], "name": "AmountTooLow", "type": "error" }, + { "inputs": [], "name": "ApprovalFailed", "type": "error" }, + { "inputs": [], "name": "CBRIsNull", "type": "error" }, + { "inputs": [], "name": "CBRIsTooHigh", "type": "error" }, + { "inputs": [], "name": "EnforcedPause", "type": "error" }, + { "inputs": [], "name": "ExpectedPause", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "ExpiredSignature", + "type": "error" + }, + { "inputs": [], "name": "FailedInnerCall", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "currentNonce", "type": "uint256" } + ], + "name": "InvalidAccountNonce", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "approvalDeadline", + "type": "uint256" + }, + { "internalType": "uint256", "name": "intentDeadline", "type": "uint256" } + ], + "name": "InvalidDeadline", + "type": "error" + }, + { "inputs": [], "name": "InvalidInitialization", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "InvalidOrderAmount", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "InvalidSigner", + "type": "error" + }, + { "inputs": [], "name": "InvalidToken", "type": "error" }, + { "inputs": [], "name": "MathOverflowedMulDiv", "type": "error" }, + { "inputs": [], "name": "NoOrdersIdsProvided", "type": "error" }, + { "inputs": [], "name": "NotAuthorized", "type": "error" }, + { "inputs": [], "name": "NotInitializing", "type": "error" }, + { "inputs": [], "name": "NullContract", "type": "error" }, + { "inputs": [], "name": "RedeemFeeTooBig", "type": "error" }, + { "inputs": [], "name": "RedeemMustBePaused", "type": "error" }, + { "inputs": [], "name": "RedeemMustNotBePaused", "type": "error" }, + { "inputs": [], "name": "ReentrancyGuardReentrantCall", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { "inputs": [], "name": "SameValue", "type": "error" }, + { "inputs": [], "name": "SwapMustBePaused", "type": "error" }, + { "inputs": [], "name": "SwapMustNotBePaused", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "cbrCoef", + "type": "uint256" + } + ], + "name": "CBRActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CBRDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenSwapped", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalAmountInTokenDecimals", + "type": "uint256" + } + ], + "name": "IntentConsumed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenSwapped", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountInTokenDecimals", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountInUSD", + "type": "uint256" + } + ], + "name": "IntentMatched", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "nonceInvalidated", + "type": "uint256" + } + ], + "name": "NonceInvalidated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newThreshold", + "type": "uint256" + } + ], + "name": "NonceThresholdSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "rwaToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "returnedRwaAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stableFeeAmount", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "redeemFee", + "type": "uint256" + } + ], + "name": "RedeemFeeUpdated", + "type": "event" + }, + { "anonymous": false, "inputs": [], "name": "RedeemPaused", "type": "event" }, + { + "anonymous": false, + "inputs": [], + "name": "RedeemUnPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenSwapped", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountInUSD", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { "anonymous": false, "inputs": [], "name": "SwapPaused", "type": "event" }, + { "anonymous": false, "inputs": [], "name": "SwapUnPaused", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DaoCollateralStorageV0Location", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "coefficient", "type": "uint256" } + ], + "name": "activateCBR", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cbrCoef", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "deactivateCBR", + "outputs": [], + "stateMutability": "nonpayable", + "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": "_registryContract", + "type": "address" + }, + { "internalType": "uint256", "name": "_redeemFee", "type": "uint256" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_registryContract", + "type": "address" + } + ], + "name": "initializeV1", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "invalidateNonce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newNonce", "type": "uint256" } + ], + "name": "invalidateUpToNonce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isCBROn", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isRedeemPaused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isSwapPaused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonceThreshold", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "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": "address", "name": "owner", "type": "address" } + ], + "name": "orderAmountTakenCurrentNonce", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseRedeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseSwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "rwaToken", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOut", "type": "uint256" } + ], + "name": "redeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "rwaToken", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "redeemDao", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "redeemFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "threshold", "type": "uint256" } + ], + "name": "setNonceThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_redeemFee", "type": "uint256" } + ], + "name": "setRedeemFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "rwaToken", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOut", "type": "uint256" } + ], + "name": "swap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "rwaToken", "type": "address" }, + { + "internalType": "uint256", + "name": "amountInTokenDecimals", + "type": "uint256" + }, + { "internalType": "bool", "name": "partialMatching", "type": "bool" }, + { + "internalType": "uint256[]", + "name": "orderIdsToTake", + "type": "uint256[]" + }, + { + "components": [ + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "internalType": "struct Approval", + "name": "approval", + "type": "tuple" + } + ], + "name": "swapRWAtoStbc", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "orderIdsToTake", + "type": "uint256[]" + }, + { + "components": [ + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "internalType": "struct Approval", + "name": "approval", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "address", "name": "rwaToken", "type": "address" }, + { + "internalType": "uint256", + "name": "amountInTokenDecimals", + "type": "uint256" + }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "bytes", "name": "signature", "type": "bytes" } + ], + "internalType": "struct Intent", + "name": "intent", + "type": "tuple" + }, + { "internalType": "bool", "name": "partialMatching", "type": "bool" } + ], + "name": "swapRWAtoStbcIntent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "rwaToken", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOut", "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": "swapWithPermit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseRedeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseSwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/abi/usual-m-wrapped-m/usualM.abi.json b/src/abi/usual-m-wrapped-m/usualM.abi.json new file mode 100644 index 000000000..53ea08cd6 --- /dev/null +++ b/src/abi/usual-m-wrapped-m/usualM.abi.json @@ -0,0 +1,509 @@ +[ + { "type": "constructor", "inputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "DECIMALS_NUMBER", + "inputs": [], + "outputs": [{ "name": "", "type": "uint8", "internalType": "uint8" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DOMAIN_SEPARATOR", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "UsualMStorageV0Location", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { "name": "owner", "type": "address", "internalType": "address" }, + { "name": "spender", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { "name": "spender", "type": "address", "internalType": "address" }, + { "name": "value", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "blacklist", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [{ "name": "", "type": "uint8", "internalType": "uint8" }], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "eip712Domain", + "inputs": [], + "outputs": [ + { "name": "fields", "type": "bytes1", "internalType": "bytes1" }, + { "name": "name", "type": "string", "internalType": "string" }, + { "name": "version", "type": "string", "internalType": "string" }, + { "name": "chainId", "type": "uint256", "internalType": "uint256" }, + { + "name": "verifyingContract", + "type": "address", + "internalType": "address" + }, + { "name": "salt", "type": "bytes32", "internalType": "bytes32" }, + { "name": "extensions", "type": "uint256[]", "internalType": "uint256[]" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getWrappableAmount", + "inputs": [ + { "name": "amount", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { "name": "wrappedM_", "type": "address", "internalType": "address" }, + { + "name": "registryAccess_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "isBlacklisted", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mintCap", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [{ "name": "", "type": "string", "internalType": "string" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonces", + "inputs": [ + { "name": "owner", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "paused", + "inputs": [], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "permit", + "inputs": [ + { "name": "owner", "type": "address", "internalType": "address" }, + { "name": "spender", "type": "address", "internalType": "address" }, + { "name": "value", "type": "uint256", "internalType": "uint256" }, + { "name": "deadline", "type": "uint256", "internalType": "uint256" }, + { "name": "v", "type": "uint8", "internalType": "uint8" }, + { "name": "r", "type": "bytes32", "internalType": "bytes32" }, + { "name": "s", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "registryAccess", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setMintCap", + "inputs": [ + { "name": "newMintCap", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [{ "name": "", "type": "string", "internalType": "string" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { "name": "to", "type": "address", "internalType": "address" }, + { "name": "value", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { "name": "from", "type": "address", "internalType": "address" }, + { "name": "to", "type": "address", "internalType": "address" }, + { "name": "value", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unBlacklist", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unpause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unwrap", + "inputs": [ + { "name": "recipient", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "wrap", + "inputs": [ + { "name": "recipient", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "wrapWithPermit", + "inputs": [ + { "name": "recipient", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" }, + { "name": "deadline", "type": "uint256", "internalType": "uint256" }, + { "name": "v", "type": "uint8", "internalType": "uint8" }, + { "name": "r", "type": "bytes32", "internalType": "bytes32" }, + { "name": "s", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "wrappedM", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Blacklist", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "EIP712DomainChanged", + "inputs": [], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MintCapSet", + "inputs": [ + { + "name": "newMintCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Paused", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "UnBlacklist", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Unpaused", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { "type": "error", "name": "Blacklisted", "inputs": [] }, + { "type": "error", "name": "ECDSAInvalidSignature", "inputs": [] }, + { + "type": "error", + "name": "ECDSAInvalidSignatureLength", + "inputs": [ + { "name": "length", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "ECDSAInvalidSignatureS", + "inputs": [{ "name": "s", "type": "bytes32", "internalType": "bytes32" }] + }, + { + "type": "error", + "name": "ERC20InsufficientAllowance", + "inputs": [ + { "name": "spender", "type": "address", "internalType": "address" }, + { "name": "allowance", "type": "uint256", "internalType": "uint256" }, + { "name": "needed", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "ERC20InsufficientBalance", + "inputs": [ + { "name": "sender", "type": "address", "internalType": "address" }, + { "name": "balance", "type": "uint256", "internalType": "uint256" }, + { "name": "needed", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "ERC20InvalidApprover", + "inputs": [ + { "name": "approver", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "ERC20InvalidReceiver", + "inputs": [ + { "name": "receiver", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "ERC20InvalidSender", + "inputs": [ + { "name": "sender", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "ERC20InvalidSpender", + "inputs": [ + { "name": "spender", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "ERC2612ExpiredSignature", + "inputs": [ + { "name": "deadline", "type": "uint256", "internalType": "uint256" } + ] + }, + { + "type": "error", + "name": "ERC2612InvalidSigner", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" }, + { "name": "owner", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "EnforcedPause", "inputs": [] }, + { "type": "error", "name": "ExpectedPause", "inputs": [] }, + { + "type": "error", + "name": "InvalidAccountNonce", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" }, + { "name": "currentNonce", "type": "uint256", "internalType": "uint256" } + ] + }, + { "type": "error", "name": "InvalidAmount", "inputs": [] }, + { "type": "error", "name": "InvalidInitialization", "inputs": [] }, + { "type": "error", "name": "InvalidUInt96", "inputs": [] }, + { "type": "error", "name": "MintCapExceeded", "inputs": [] }, + { "type": "error", "name": "NotAuthorized", "inputs": [] }, + { "type": "error", "name": "NotInitializing", "inputs": [] }, + { "type": "error", "name": "SameValue", "inputs": [] }, + { "type": "error", "name": "ZeroAddress", "inputs": [] }, + { "type": "error", "name": "ZeroRegistryAccess", "inputs": [] }, + { "type": "error", "name": "ZeroWrappedM", "inputs": [] } +] diff --git a/src/dex/fluid-dex/constants.ts b/src/dex/fluid-dex/constants.ts new file mode 100644 index 000000000..da6d99534 --- /dev/null +++ b/src/dex/fluid-dex/constants.ts @@ -0,0 +1 @@ +export const MIN_SWAP_LIQUIDITY = 10n ** 4n; diff --git a/src/dex/fluid-dex/fluid-dex-e2e.test.ts b/src/dex/fluid-dex/fluid-dex-e2e.test.ts index a6873d07d..333aed44d 100644 --- a/src/dex/fluid-dex/fluid-dex-e2e.test.ts +++ b/src/dex/fluid-dex/fluid-dex-e2e.test.ts @@ -118,6 +118,23 @@ describe('FluidDex E2E', () => { describe('Mainnet', () => { const network = Network.MAINNET; + describe('ETH -> INST', () => { + const tokenASymbol: string = 'ETH'; + const tokenBSymbol: string = 'INST'; + + const tokenAAmount: string = '100000000000000'; + const tokenBAmount: string = '100000000000000'; + + testForNetwork( + network, + dexKey, + tokenASymbol, + tokenBSymbol, + tokenAAmount, + tokenBAmount, + ); + }); + describe('ETH -> wstETH', () => { const tokenASymbol: string = 'wstETH'; const tokenBSymbol: string = 'ETH'; diff --git a/src/dex/fluid-dex/fluid-dex-events.test.ts b/src/dex/fluid-dex/fluid-dex-events.test.ts index 19d6a89d7..f5bad501d 100644 --- a/src/dex/fluid-dex/fluid-dex-events.test.ts +++ b/src/dex/fluid-dex/fluid-dex-events.test.ts @@ -10,14 +10,16 @@ import { FluidDexLiquidityProxyState } from './types'; import { FluidDexConfig } from './config'; import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy'; import { FluidDexFactory } from './fluid-dex-factory'; +import { FluidDexEventPool } from './fluid-dex-pool'; +import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; jest.setTimeout(50 * 1000); -async function fetchLiquidityProxyState( - liquidityProxy: FluidDexLiquidityProxy, +async function fetchState( + statefulEventSubscriber: StatefulEventSubscriber, blockNumber: number, -): Promise { - return liquidityProxy.generateState(blockNumber); +): Promise { + return statefulEventSubscriber.generateState(blockNumber); } // eventName -> blockNumbers @@ -64,7 +66,7 @@ describe('FluidDex EventPool Mainnet', function () { liquidityProxy, liquidityProxy.addressesSubscribed, (_blockNumber: number) => - fetchLiquidityProxyState(liquidityProxy, _blockNumber), + fetchState(liquidityProxy, _blockNumber), blockNumber, `${dexKey}_${poolAddress}`, dexHelper.provider, @@ -126,4 +128,52 @@ describe('FluidDex EventPool Mainnet', function () { }, ); }); + + describe('Pool events', () => { + let dexPool: FluidDexEventPool; + + const eventsToTest: Record = { + '0x8710039D5de6840EdE452A85672B32270a709aE2': { + LogPauseSwapAndArbitrage: [21337128], + }, + '0x2886a01a0645390872a9eb99dae1283664b0c524': { + LogPauseSwapAndArbitrage: [21374547], + }, + }; + + Object.entries(eventsToTest).forEach( + ([poolAddress, events]: [string, EventMappings]) => { + describe(`Events for ${poolAddress}`, () => { + beforeEach(() => { + dexPool = new FluidDexEventPool( + dexKey, + poolAddress, + network, + dexHelper, + logger, + ); + }); + Object.entries(events).forEach( + ([eventName, blockNumbers]: [string, number[]]) => { + describe(`${eventName}`, () => { + blockNumbers.forEach((blockNumber: number) => { + it(`State after ${blockNumber}`, async function () { + await testEventSubscriber( + dexPool, + dexPool.addressesSubscribed, + (_blockNumber: number) => + fetchState(dexPool, _blockNumber), + blockNumber, + `${dexKey}_${poolAddress}`, + dexHelper.provider, + ); + }); + }); + }); + }, + ); + }); + }, + ); + }); }); diff --git a/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts b/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts index ae61daa84..f6f7b890b 100644 --- a/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts +++ b/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts @@ -28,7 +28,7 @@ export class FluidDexLiquidityProxy extends StatefulEventSubscriber { + handlers: { + [event: string]: ( + event: any, + state: DeepReadonly, + log: Readonly, + ) => DeepReadonly | null; + } = {}; + + logDecoder: (log: Log) => any; + addressesSubscribed: Address[]; + protected poolIface = new Interface(FluidDexPoolABI); + + constructor( + readonly parentName: string, + readonly poolAddress: string, + protected network: number, + protected dexHelper: IDexHelper, + logger: Logger, + ) { + super(parentName, 'pool', dexHelper, logger); + + this.logDecoder = (log: Log) => this.poolIface.parseLog(log); + this.addressesSubscribed = [poolAddress]; + + // Add handlers + this.handlers['LogPauseSwapAndArbitrage'] = + this.handleLogPauseSwapAndArbitrage.bind(this); + this.handlers['LogUnpauseSwapAndArbitrage'] = + this.handleLogUnpauseSwapAndArbitrage.bind(this); + } + + /** + * The function is called every time any of the subscribed + * addresses release log. The function accepts the current + * state, updates the state according to the log, and returns + * the updated state. + * @param state - Current state of event subscriber + * @param log - Log released by one of the subscribed addresses + * @returns Updates state of the event subscriber after the log + */ + async processLog( + state: DeepReadonly, + log: Readonly, + ): Promise | null> { + try { + let event; + try { + event = this.logDecoder(log); + } catch (e) { + return null; + } + + if (event.name in this.handlers) { + return this.handlers[event.name](event, state, log); + } + } catch (e) { + catchParseLogError(e, this.logger); + } + + return null; + } + + handleLogPauseSwapAndArbitrage(): PoolState { + return { isSwapAndArbitragePaused: true }; + } + + handleLogUnpauseSwapAndArbitrage(): PoolState { + return { isSwapAndArbitragePaused: false }; + } + + async getStateOrGenerate( + blockNumber: number, + readonly: boolean = false, + ): Promise> { + let state = this.getState(blockNumber); + if (!state) { + state = await this.generateState(blockNumber); + if (!readonly) this.setState(state, blockNumber); + } + return state; + } + + /** + * The function generates state using on-chain calls. This + * function is called to regenerate state if the event based + * system fails to fetch events and the local state is no + * more correct. + * @param blockNumber - Blocknumber for which the state should + * should be generated + * @returns state of the event subscriber at blocknumber + */ + async generateState(blockNumber: number): Promise> { + const multicallData = [ + { + target: this.addressesSubscribed[0], + callData: this.poolIface.encodeFunctionData('readFromStorage', [ + hexZeroPad(hexlify(1), 32), + ]), + decodeFunction: uint256ToBigInt, + }, + ]; + + const storageResults = await this.dexHelper.multiWrapper.tryAggregate< + bigint | DecodedStateMultiCallResultWithRelativeBitmaps + >( + false, + multicallData, + blockNumber, + this.dexHelper.multiWrapper.defaultBatchSize, + false, + ); + + const isSwapAndArbitragePaused = + BigInt(storageResults[0].returnData.toString()) >> 255n === 1n; + + return { isSwapAndArbitragePaused }; + } +} diff --git a/src/dex/fluid-dex/fluid-dex.ts b/src/dex/fluid-dex/fluid-dex.ts index e8ea87ad6..fa3284ea4 100644 --- a/src/dex/fluid-dex/fluid-dex.ts +++ b/src/dex/fluid-dex/fluid-dex.ts @@ -34,6 +34,8 @@ import { generalDecoder } from '../../lib/decoders'; import { BigNumber } from 'ethers'; import { sqrt } from './utils'; import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy'; +import { FluidDexEventPool } from './fluid-dex-pool'; +import { MIN_SWAP_LIQUIDITY } from './constants'; export class FluidDex extends SimpleExchange implements IDex { readonly hasConstantPriceLargeAmounts = false; @@ -46,14 +48,14 @@ export class FluidDex extends SimpleExchange implements IDex { pools: FluidDexPool[] = []; + eventPools: FluidDexEventPool[] = []; + readonly factory: FluidDexFactory; readonly liquidityProxy: FluidDexLiquidityProxy; readonly fluidDexPoolIface: Interface; - FEE_100_PERCENT = BigInt(1000000); - constructor( readonly network: Network, readonly dexKey: string, @@ -110,6 +112,20 @@ export class FluidDex extends SimpleExchange implements IDex { await this.factory.initialize(blockNumber); this.pools = await this.fetchFluidDexPools(blockNumber); + this.eventPools = await Promise.all( + this.pools.map(async pool => { + const eventPool = new FluidDexEventPool( + this.dexKey, + pool.address, + this.network, + this.dexHelper, + this.logger, + ); + await eventPool.initialize(blockNumber); + return eventPool; + }), + ); + await this.liquidityProxy.initialize(blockNumber); } @@ -147,29 +163,24 @@ export class FluidDex extends SimpleExchange implements IDex { side: SwapSide, blockNumber: number, ): Promise { - const pool = this.getPoolByTokenPair(srcToken.address, destToken.address); - return pool ? [pool.id] : []; + const pools = this.getPoolsByTokenPair(srcToken.address, destToken.address); + return pools.map(pool => pool.id); } - getPoolByTokenPair( - srcToken: Address, - destToken: Address, - ): FluidDexPool | null { + getPoolsByTokenPair(srcToken: Address, destToken: Address): FluidDexPool[] { const srcAddress = srcToken.toLowerCase(); const destAddress = destToken.toLowerCase(); // A pair must have 2 different tokens. - if (srcAddress === destAddress) return null; + if (srcAddress === destAddress) return []; - for (const pool of this.pools) { - if ( + const pools = this.pools.filter( + pool => (srcAddress === pool.token0 && destAddress === pool.token1) || - (srcAddress === pool.token1 && destAddress === pool.token0) - ) { - return pool; - } - } - return null; + (srcAddress === pool.token1 && destAddress === pool.token0), + ); + + return pools; } // Returns pool prices for amounts. @@ -187,61 +198,93 @@ export class FluidDex extends SimpleExchange implements IDex { try { if (srcToken.address.toLowerCase() === destToken.address.toLowerCase()) return null; - // Get the pool to use. - const pool = this.getPoolByTokenPair(srcToken.address, destToken.address); - if (!pool) return null; - // Make sure the pool meets the optional limitPools filter. - if (limitPools && !limitPools.includes(pool.id)) return null; + + // Get the pools to use. + let pools = this.getPoolsByTokenPair(srcToken.address, destToken.address); + + if (limitPools) { + pools = pools.filter(pool => limitPools.includes(pool.id)); + } + + if (!pools.length) return null; const liquidityProxyState = await this.liquidityProxy.getStateOrGenerate( blockNumber, ); - const currentPoolReserves = liquidityProxyState.poolsReserves.find( - poolReserve => - poolReserve.pool.toLowerCase() === pool.address.toLowerCase(), - ); - if (!currentPoolReserves) { - return null; - } - const prices = amounts.map(amount => { - if (side === SwapSide.SELL) { - return this.swapIn( - srcToken.address.toLowerCase() === pool.token0.toLowerCase(), - amount, - currentPoolReserves.collateralReserves, - currentPoolReserves.debtReserves, - srcToken.decimals, - destToken.decimals, - BigInt(currentPoolReserves.fee), - currentPoolReserves.dexLimits, - Math.floor(Date.now() / 1000), + const poolsPrices = await Promise.all( + pools.map(async pool => { + const currentPoolReserves = liquidityProxyState.poolsReserves.find( + poolReserve => + poolReserve.pool.toLowerCase() === pool.address.toLowerCase(), ); - } else { - return this.swapOut( - srcToken.address.toLowerCase() === pool.token0.toLowerCase(), - amount, - currentPoolReserves.collateralReserves, - currentPoolReserves.debtReserves, - srcToken.decimals, - destToken.decimals, - BigInt(currentPoolReserves.fee), - currentPoolReserves.dexLimits, - Math.floor(Date.now() / 1000), + + const eventPool = this.eventPools.find( + eventPool => + eventPool.poolAddress.toLowerCase() === + pool.address.toLowerCase(), ); - } - }); - return [ - { - prices: prices, - unit: getBigIntPow(destToken.decimals), - data: {}, - exchange: this.dexKey, - poolIdentifier: pool.id, - gasCost: FLUID_DEX_GAS_COST, - poolAddresses: [pool.address], - }, - ]; + + if (!eventPool) { + this.logger.warn( + `${this.dexKey}-${this.network}: Event pool ${pool.address} was not found...`, + ); + return null; + } + + const state = await eventPool.getStateOrGenerate(blockNumber); + + if (!currentPoolReserves || state.isSwapAndArbitragePaused === true) { + return null; + } + + const prices = amounts.map(amount => { + if (side === SwapSide.SELL) { + return this.swapIn( + srcToken.address.toLowerCase() === pool.token0.toLowerCase(), + amount, + currentPoolReserves.collateralReserves, + currentPoolReserves.debtReserves, + srcToken.decimals, + destToken.decimals, + BigInt(currentPoolReserves.fee), + currentPoolReserves.dexLimits, + Math.floor(Date.now() / 1000), + ); + } else { + return this.swapOut( + srcToken.address.toLowerCase() === pool.token0.toLowerCase(), + amount, + currentPoolReserves.collateralReserves, + currentPoolReserves.debtReserves, + srcToken.decimals, + destToken.decimals, + BigInt(currentPoolReserves.fee), + currentPoolReserves.dexLimits, + Math.floor(Date.now() / 1000), + ); + } + }); + + return { + prices: prices, + unit: getBigIntPow(destToken.decimals), + data: { + poolId: pool.id, + }, + exchange: this.dexKey, + poolIdentifier: pool.id, + gasCost: FLUID_DEX_GAS_COST, + poolAddresses: [pool.address], + }; + }), + ); + + const notNullResults = poolsPrices.filter( + res => res !== null, + ) as ExchangePrices; + + return notNullResults; } catch (e) { this.logger.error( `Error_getPricesVolume ${srcToken.address || srcToken.symbol}, ${ @@ -272,10 +315,9 @@ export class FluidDex extends SimpleExchange implements IDex { ): AdapterExchangeParam { // Encode here the payload for adapter const payload = ''; - const pool = this.getPoolByTokenPair(srcToken, destToken); return { - targetExchange: pool!.address, + targetExchange: '0x', payload, networkFee: '0', }; @@ -313,7 +355,11 @@ export class FluidDex extends SimpleExchange implements IDex { side === SwapSide.SELL ? 'amountOut_' : 'amountIn_', ); - const pool = this.getPoolByTokenPair(srcToken, destToken); + const pool = this.pools.find(pool => pool.id === data.poolId); + if (!pool) + throw new Error( + `${this.dexKey}-${this.network}: Pool with id: ${data.poolId} was not found`, + ); if (side === SwapSide.SELL) { if (pool!.token0.toLowerCase() !== srcToken.toLowerCase()) { @@ -584,11 +630,84 @@ export class FluidDex extends SimpleExchange implements IDex { if (priceDiff > maxAllowedDiff) { return 0n; } + + if (amountInCollateral > 0) { + let reservesRatioValid = swap0To1 + ? this.verifyToken1Reserves( + colReserveIn + amountInCollateral, + colReserveOut - amountOutCollateral, + oldPrice, + ) + : this.verifyToken0Reserves( + colReserveOut - amountOutCollateral, + colReserveIn + amountInCollateral, + oldPrice, + ); + if (!reservesRatioValid) { + return 0n; + } + } + + if (amountInDebt > 0) { + let reservesRatioValid = swap0To1 + ? this.verifyToken1Reserves( + debtReserveIn + amountInDebt, + debtReserveOut - amountOutDebt, + oldPrice, + ) + : this.verifyToken0Reserves( + debtReserveOut - amountOutDebt, + debtReserveIn + amountInDebt, + oldPrice, + ); + if (!reservesRatioValid) { + return 0n; + } + } + const totalAmountOut = amountOutCollateral + amountOutDebt; return totalAmountOut; } + /** + * Checks if token0 reserves are sufficient compared to token1 reserves. + * This helps prevent edge cases and ensures high precision in calculations. + * @param {number} token0Reserves - The reserves of token0. + * @param {number} token1Reserves - The reserves of token1. + * @param {number} price - The current price used for calculation. + * @returns {boolean} - Returns false if token0 reserves are too low, true otherwise. + */ + protected verifyToken0Reserves( + token0Reserves: bigint, + token1Reserves: bigint, + price: bigint, + ): boolean { + return ( + token0Reserves >= + (token1Reserves * 10n ** 27n) / (price * MIN_SWAP_LIQUIDITY) + ); + } + + /** + * Checks if token1 reserves are sufficient compared to token0 reserves. + * This helps prevent edge cases and ensures high precision in calculations. + * @param {number} token0Reserves - The reserves of token0. + * @param {number} token1Reserves - The reserves of token1. + * @param {number} price - The current price used for calculation. + * @returns {boolean} - Returns false if token1 reserves are too low, true otherwise. + */ + protected verifyToken1Reserves( + token0Reserves: bigint, + token1Reserves: bigint, + price: bigint, + ): boolean { + return ( + token1Reserves >= + (token0Reserves * price) / (10n ** 27n * MIN_SWAP_LIQUIDITY) + ); + } + /** * Calculates the currently available swappable amount for a token limit considering expansion since last syncTime. * @param syncTime - timestamp in seconds when the limits were synced @@ -765,7 +884,7 @@ export class FluidDex extends SimpleExchange implements IDex { syncTime, ); - if (amountIn == 2n ** 256n - 1n) { + if (amountIn === 2n ** 256n - 1n) { return amountIn; } const ans = (amountIn * BigInt(10 ** inDecimals)) / BigInt(10 ** 12); @@ -985,6 +1104,39 @@ export class FluidDex extends SimpleExchange implements IDex { return 2n ** 256n - 1n; } + if (amountInCollateral > 0) { + let reservesRatioValid = swap0to1 + ? this.verifyToken1Reserves( + colReserveIn + amountInCollateral, + colReserveOut - amountOutCollateral, + oldPrice, + ) + : this.verifyToken0Reserves( + colReserveOut - amountOutCollateral, + colReserveIn + amountInCollateral, + oldPrice, + ); + if (!reservesRatioValid) { + return 0n; + } + } + if (amountInDebt > 0) { + let reservesRatioValid = swap0to1 + ? this.verifyToken1Reserves( + debtReserveIn + amountInDebt, + debtReserveOut - amountOutDebt, + oldPrice, + ) + : this.verifyToken0Reserves( + debtReserveOut - amountOutDebt, + debtReserveIn + amountInDebt, + oldPrice, + ); + if (!reservesRatioValid) { + return 0n; + } + } + const totalAmountIn = amountInCollateral + amountInDebt; return totalAmountIn; diff --git a/src/dex/fluid-dex/types.ts b/src/dex/fluid-dex/types.ts index cffe433ce..0fb982e07 100644 --- a/src/dex/fluid-dex/types.ts +++ b/src/dex/fluid-dex/types.ts @@ -72,7 +72,9 @@ export interface PoolWithReserves { debtReserves: DebtReserves; } -export type FluidDexData = {}; +export type FluidDexData = { + poolId: string; +}; // Each pool has a contract address and token pairs. export type FluidDexPool = { diff --git a/src/dex/index.ts b/src/dex/index.ts index 4161334ad..89e9008f8 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -91,13 +91,15 @@ import { ConcentratorArusd } from './concentrator-arusd/concentrator-arusd'; import { FxProtocolRusd } from './fx-protocol-rusd/fx-protocol-rusd'; import { AaveGsm } from './aave-gsm/aave-gsm'; import { LitePsm } from './lite-psm/lite-psm'; -import { UsualBond } from './usual-bond/usual-bond'; import { StkGHO } from './stkgho/stkgho'; import { BalancerV3 } from './balancer-v3/balancer-v3'; import { balancerV3Merge } from './balancer-v3/optimizer'; import { SkyConverter } from './sky-converter/sky-converter'; import { Cables } from './cables/cables'; import { Stader } from './stader/stader'; +import { UsualBond } from './usual/usual-bond'; +import { UsualMWrappedM } from './usual/usual-m-wrapped-m'; +import { UsualMUsd0 } from './usual/usual-m-usd0'; const LegacyDexes = [ CurveV2, @@ -190,6 +192,8 @@ const Dexes = [ SkyConverter, Cables, FluidDex, + UsualMWrappedM, + UsualMUsd0, ]; export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder< diff --git a/src/dex/maverick-v2/maverick-math/maverick-pool-math.ts b/src/dex/maverick-v2/maverick-math/maverick-pool-math.ts index f487fe7ef..02e0e956b 100644 --- a/src/dex/maverick-v2/maverick-math/maverick-pool-math.ts +++ b/src/dex/maverick-v2/maverick-math/maverick-pool-math.ts @@ -634,7 +634,7 @@ export class MaverickPoolMath { activeTick += delta.tokenAIn ? 1n : -1n; if (MaverickDeltaMath.pastMaxTick(delta, activeTick)) { - this.state.activeTick += delta.tokenAIn ? 1n : -1n; + this.state.activeTick += delta.tokenAIn ? -1n : 1n; return delta; } } diff --git a/src/dex/usual-bond/config.ts b/src/dex/usual-bond/config.ts deleted file mode 100644 index 09fe45eca..000000000 --- a/src/dex/usual-bond/config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DexParams } from './types'; -import { DexConfigMap } from '../../types'; -import { Network } from '../../constants'; - -export const UsualBondConfig: DexConfigMap = { - UsualBond: { - [Network.MAINNET]: { - usd0Address: '0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5', - usd0ppAddress: '0x35D8949372D46B7a3D5A56006AE77B215fc69bC0', - }, - }, -}; diff --git a/src/dex/usual-bond/usual-bond-integration.test.ts b/src/dex/usual-bond/usual-bond-integration.test.ts deleted file mode 100644 index 3844bc200..000000000 --- a/src/dex/usual-bond/usual-bond-integration.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* eslint-disable no-console */ -import dotenv from 'dotenv'; -dotenv.config(); - -import { Interface } from '@ethersproject/abi'; -import { DummyDexHelper } from '../../dex-helper/index'; -import { Network, SwapSide } from '../../constants'; -import { BI_POWS } from '../../bigint-constants'; -import { UsualBond } from './usual-bond'; -import { - checkPoolPrices, - checkConstantPoolPrices, - checkPoolsLiquidity, -} from '../../../tests/utils'; -import { Tokens } from '../../../tests/constants-e2e'; - -async function testPricingOnNetwork( - usualBond: UsualBond, - network: Network, - dexKey: string, - blockNumber: number, - srcTokenAddress: string, - destTokenAddress: string, - side: SwapSide, - amounts: bigint[], - funcNameToCheck: string, -) { - const networkTokens = Tokens[network]; - - console.log(amounts); - - const pools = await usualBond.getPoolIdentifiers( - networkTokens['USD0'], - networkTokens['USD0++'], - side, - blockNumber, - ); - console.log(`${'USD0'} <> ${'USD0++'} Pool Identifiers: `, pools); - - expect(pools.length).toBeGreaterThan(0); - - const poolPrices = await usualBond.getPricesVolume( - networkTokens['USD0'], - networkTokens['USD0++'], - amounts, - side, - blockNumber, - pools, - ); - console.log(`${'USD0'} <> ${'USD0++'} Pool Prices: `, poolPrices); - - expect(poolPrices).not.toBeNull(); - if (usualBond.hasConstantPriceLargeAmounts) { - checkConstantPoolPrices(poolPrices!, amounts, dexKey); - } else { - checkPoolPrices(poolPrices!, amounts, side, dexKey); - } - - // Check if onchain pricing equals to calculated ones - checkPoolPrices(poolPrices!, amounts, side, dexKey); -} - -describe('UsualBond', function () { - const dexKey = 'UsualBond'; - let blockNumber: number; - let usualBond: UsualBond; - - describe('Mainnet', () => { - const network = Network.MAINNET; - const dexHelper = new DummyDexHelper(network); - - // Don't forget to update relevant tokens in constant-e2e.ts - - const amountsForSell = [ - 0n, - 1n * BI_POWS[18], - 2n * BI_POWS[18], - 3n * BI_POWS[18], - 4n * BI_POWS[18], - 5n * BI_POWS[18], - 6n * BI_POWS[18], - 7n * BI_POWS[18], - 8n * BI_POWS[18], - 9n * BI_POWS[18], - 10n * BI_POWS[18], - ]; - - beforeAll(async () => { - blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); - usualBond = new UsualBond(network, dexKey, dexHelper); - if (usualBond.initializePricing) { - await usualBond.initializePricing(blockNumber); - } - }); - - it('getPoolIdentifiers and getPricesVolume SELL', async function () { - await testPricingOnNetwork( - usualBond, - network, - dexKey, - blockNumber, - 'USD0', - 'USD0++', - SwapSide.SELL, - amountsForSell, - '', - ); - }); - - it('getTopPoolsForToken: USD0', async function () { - const tokenA = Tokens[network]['USD0']; - const dexHelper = new DummyDexHelper(network); - const usualBond = new UsualBond(network, dexKey, dexHelper); - - const poolLiquidity = await usualBond.getTopPoolsForToken( - tokenA.address, - 10, - ); - console.log( - `${tokenA.symbol} Top Pools:`, - JSON.stringify(poolLiquidity, null, 2), - ); - - checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); - }); - - it('getTopPoolsForToken: USD0++', async function () { - const tokenA = Tokens[network]['USD0++']; - const dexHelper = new DummyDexHelper(network); - const usualBond = new UsualBond(network, dexKey, dexHelper); - - const poolLiquidity = await usualBond.getTopPoolsForToken( - tokenA.address, - 10, - ); - console.log( - `${tokenA.symbol} Top Pools:`, - JSON.stringify(poolLiquidity, null, 2), - ); - - checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); - }); - }); -}); diff --git a/src/dex/usual-bond/types.ts b/src/dex/usual/types.ts similarity index 55% rename from src/dex/usual-bond/types.ts rename to src/dex/usual/types.ts index ce90c4eb4..ef8151986 100644 --- a/src/dex/usual-bond/types.ts +++ b/src/dex/usual/types.ts @@ -5,6 +5,6 @@ export type PoolState = {}; export type UsualBondData = {}; export type DexParams = { - usd0Address: Address; - usd0ppAddress: Address; + fromToken: { address: Address; decimals: number }; + toToken: { address: Address; decimals: number }; }; diff --git a/src/dex/usual/usual-bond.ts b/src/dex/usual/usual-bond.ts new file mode 100644 index 000000000..ac39a92bc --- /dev/null +++ b/src/dex/usual/usual-bond.ts @@ -0,0 +1,69 @@ +import { + Address, + NumberAsString, + DexExchangeParam, + DexConfigMap, +} from '../../types'; +import { SwapSide, Network } from '../../constants'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { UsualBondData, DexParams } from './types'; +import { Interface, JsonFragment } from '@ethersproject/abi'; +import USD0PP_ABI from '../../abi/usual-bond/usd0pp.abi.json'; +import { Usual } from './usual'; +import { getDexKeysWithNetwork } from '../../utils'; + +const Config: DexConfigMap = { + UsualBond: { + [Network.MAINNET]: { + fromToken: { + address: '0x73a15fed60bf67631dc6cd7bc5b6e8da8190acf5', + decimals: 18, + }, + toToken: { + address: '0x35d8949372d46b7a3d5a56006ae77b215fc69bc0', + decimals: 18, + }, + }, + }, +}; + +export class UsualBond extends Usual { + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(Config); + + usd0ppIface: Interface; + + constructor( + readonly network: Network, + readonly dexKey: string, + readonly dexHelper: IDexHelper, + ) { + super(network, dexKey, dexHelper, Config[dexKey][network]); + this.usd0ppIface = new Interface(USD0PP_ABI as JsonFragment[]); + } + + async getDexParam( + srcToken: Address, + destToken: Address, + srcAmount: NumberAsString, + destAmount: NumberAsString, + recipient: Address, + data: UsualBondData, + side: SwapSide, + ): Promise { + if (this.isFromToken(srcToken) && this.isToToken(destToken)) { + const exchangeData = this.usd0ppIface.encodeFunctionData('mint', [ + srcAmount, + ]); + + return { + needWrapNative: false, + dexFuncHasRecipient: false, + exchangeData, + targetExchange: this.config.toToken.address, + returnAmountPos: undefined, + }; + } + throw new Error('LOGIC ERROR'); + } +} diff --git a/src/dex/usual-bond/usual-bond-e2e.test.ts b/src/dex/usual/usual-e2e.test.ts similarity index 66% rename from src/dex/usual-bond/usual-bond-e2e.test.ts rename to src/dex/usual/usual-e2e.test.ts index f6d987514..1c3008a15 100644 --- a/src/dex/usual-bond/usual-bond-e2e.test.ts +++ b/src/dex/usual/usual-e2e.test.ts @@ -3,11 +3,7 @@ import dotenv from 'dotenv'; dotenv.config(); import { testE2E } from '../../../tests/utils-e2e'; -import { - Tokens, - Holders, - NativeTokenSymbols, -} from '../../../tests/constants-e2e'; +import { Tokens, Holders } from '../../../tests/constants-e2e'; import { Network, ContractMethod, SwapSide } from '../../constants'; import { StaticJsonRpcProvider } from '@ethersproject/providers'; import { generateConfig } from '../../config'; @@ -78,3 +74,49 @@ describe('UsualBond E2E', () => { ); }); }); + +describe('UsualMWrappedM E2E', () => { + const dexKey = 'UsualMWrappedM'; + + describe('Mainnet', () => { + const network = Network.MAINNET; + + const tokenASymbol: string = 'WrappedM'; + const tokenBSymbol: string = 'UsualM'; + + const tokenAAmount: string = '100000'; + const tokenBAmount: string = '100000'; + + testForNetwork( + network, + dexKey, + tokenASymbol, + tokenBSymbol, + tokenAAmount, + tokenBAmount, + ); + }); +}); + +describe('UsualM<>Usd0 E2E', () => { + const dexKey = 'UsualMUsd0'; + + describe('Mainnet', () => { + const network = Network.MAINNET; + + const tokenASymbol: string = 'UsualM'; + const tokenBSymbol: string = 'USD0'; + + const tokenAAmount: string = '1000000'; + const tokenBAmount: string = '1000000'; + + testForNetwork( + network, + dexKey, + tokenASymbol, + tokenBSymbol, + tokenAAmount, + tokenBAmount, + ); + }); +}); diff --git a/src/dex/usual/usual-integration.test.ts b/src/dex/usual/usual-integration.test.ts new file mode 100644 index 000000000..3041c157c --- /dev/null +++ b/src/dex/usual/usual-integration.test.ts @@ -0,0 +1,319 @@ +/* eslint-disable no-console */ +import dotenv from 'dotenv'; +dotenv.config(); + +import { Interface } from '@ethersproject/abi'; +import { DummyDexHelper } from '../../dex-helper/index'; +import { Network, SwapSide } from '../../constants'; +import { BI_POWS } from '../../bigint-constants'; +import { + checkPoolPrices, + checkConstantPoolPrices, + checkPoolsLiquidity, +} from '../../../tests/utils'; +import { Tokens } from '../../../tests/constants-e2e'; +import { UsualMWrappedM } from './usual-m-wrapped-m'; +import { UsualMUsd0 } from './usual-m-usd0'; +import { Usual } from './usual'; +import { UsualBond } from './usual-bond'; + +async function testPricingOnNetwork( + usual: Usual, + network: Network, + dexKey: string, + blockNumber: number, + srcTokenSymbol: string, + destTokenSymbol: string, + side: SwapSide, + amounts: bigint[], + funcNameToCheck: string, +) { + const networkTokens = Tokens[network]; + + console.log(amounts); + + const pools = await usual.getPoolIdentifiers( + networkTokens[srcTokenSymbol], + networkTokens[destTokenSymbol], + side, + blockNumber, + ); + console.log( + `${srcTokenSymbol} <> ${destTokenSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await usual.getPricesVolume( + networkTokens[srcTokenSymbol], + networkTokens[destTokenSymbol], + amounts, + side, + blockNumber, + pools, + ); + console.log( + `${srcTokenSymbol} <> ${destTokenSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + if (usual.hasConstantPriceLargeAmounts) { + checkConstantPoolPrices(poolPrices!, amounts, dexKey); + } else { + checkPoolPrices(poolPrices!, amounts, side, dexKey); + } + + // Check if onchain pricing equals to calculated ones + checkPoolPrices(poolPrices!, amounts, side, dexKey); +} + +describe('UsualBond', function () { + const dexKey = 'UsualBond'; + let blockNumber: number; + let usualBond: UsualBond; + + describe('Mainnet', () => { + const network = Network.MAINNET; + const dexHelper = new DummyDexHelper(network); + + // Don't forget to update relevant tokens in constant-e2e.ts + + const amountsForSell = [ + 0n, + 1n * BI_POWS[18], + 2n * BI_POWS[18], + 3n * BI_POWS[18], + 4n * BI_POWS[18], + 5n * BI_POWS[18], + 6n * BI_POWS[18], + 7n * BI_POWS[18], + 8n * BI_POWS[18], + 9n * BI_POWS[18], + 10n * BI_POWS[18], + ]; + + beforeAll(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + usualBond = new UsualBond(network, dexKey, dexHelper); + if (usualBond.initializePricing) { + await usualBond.initializePricing(blockNumber); + } + }); + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + await testPricingOnNetwork( + usualBond, + network, + dexKey, + blockNumber, + 'USD0', + 'USD0++', + SwapSide.SELL, + amountsForSell, + '', + ); + }); + + it('getTopPoolsForToken: USD0', async function () { + const tokenA = Tokens[network]['USD0']; + const dexHelper = new DummyDexHelper(network); + const usualBond = new UsualBond(network, dexKey, dexHelper); + + const poolLiquidity = await usualBond.getTopPoolsForToken( + tokenA.address, + 10, + ); + console.log( + `${tokenA.symbol} Top Pools:`, + JSON.stringify(poolLiquidity, null, 2), + ); + + checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); + }); + + it('getTopPoolsForToken: USD0++', async function () { + const tokenA = Tokens[network]['USD0++']; + const dexHelper = new DummyDexHelper(network); + const usualBond = new UsualBond(network, dexKey, dexHelper); + + const poolLiquidity = await usualBond.getTopPoolsForToken( + tokenA.address, + 10, + ); + console.log( + `${tokenA.symbol} Top Pools:`, + JSON.stringify(poolLiquidity, null, 2), + ); + + checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); + }); + }); +}); + +describe('WrappedM<>UsualM', function () { + const dexKey = 'UsualMWrappedM'; + let blockNumber: number; + let usualMWrappedM: UsualMWrappedM; + + describe('Mainnet', () => { + const network = Network.MAINNET; + const dexHelper = new DummyDexHelper(network); + + // Don't forget to update relevant tokens in constant-e2e.ts + + const amountsForSell = [ + 0n, + 1n * BI_POWS[18], + 2n * BI_POWS[18], + 3n * BI_POWS[18], + 4n * BI_POWS[18], + 5n * BI_POWS[18], + 6n * BI_POWS[18], + 7n * BI_POWS[18], + 8n * BI_POWS[18], + 9n * BI_POWS[18], + 10n * BI_POWS[18], + ]; + + beforeAll(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + usualMWrappedM = new UsualMWrappedM(network, dexKey, dexHelper); + if (usualMWrappedM.initializePricing) { + await usualMWrappedM.initializePricing(blockNumber); + } + }); + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + await testPricingOnNetwork( + usualMWrappedM, + network, + dexKey, + blockNumber, + 'WrappedM', + 'UsualM', + SwapSide.SELL, + amountsForSell, + '', + ); + }); + + it('getTopPoolsForToken: WrappedM', async function () { + const tokenA = Tokens[network]['WrappedM']; + const dexHelper = new DummyDexHelper(network); + const usualMWrappedM = new UsualMWrappedM(network, dexKey, dexHelper); + + const poolLiquidity = await usualMWrappedM.getTopPoolsForToken( + tokenA.address, + 10, + ); + console.log( + `${tokenA.symbol} Top Pools:`, + JSON.stringify(poolLiquidity, null, 2), + ); + + checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); + }); + + it('getTopPoolsForToken: UsualM', async function () { + const tokenA = Tokens[network]['UsualM']; + const dexHelper = new DummyDexHelper(network); + const usualMWrappedM = new UsualMWrappedM(network, dexKey, dexHelper); + + const poolLiquidity = await usualMWrappedM.getTopPoolsForToken( + tokenA.address, + 10, + ); + console.log( + `${tokenA.symbol} Top Pools:`, + JSON.stringify(poolLiquidity, null, 2), + ); + + checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); + }); + }); +}); + +describe('UsualM<>USD0', function () { + const dexKey = 'UsualMUsd0'; + let blockNumber: number; + let usualMUsd0: UsualMUsd0; + + describe('Mainnet', () => { + const network = Network.MAINNET; + const dexHelper = new DummyDexHelper(network); + + // Don't forget to update relevant tokens in constant-e2e.ts + + const amountsForSell = [ + 0n, + 1n * BI_POWS[18], + 2n * BI_POWS[18], + 3n * BI_POWS[18], + 4n * BI_POWS[18], + 5n * BI_POWS[18], + 6n * BI_POWS[18], + 7n * BI_POWS[18], + 8n * BI_POWS[18], + 9n * BI_POWS[18], + 10n * BI_POWS[18], + ]; + + beforeAll(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + usualMUsd0 = new UsualMUsd0(network, dexKey, dexHelper); + if (usualMUsd0.initializePricing) { + await usualMUsd0.initializePricing(blockNumber); + } + }); + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + await testPricingOnNetwork( + usualMUsd0, + network, + dexKey, + blockNumber, + 'UsualM', + 'USD0', + SwapSide.SELL, + amountsForSell, + '', + ); + }); + + it('getTopPoolsForToken: UsualM', async function () { + const tokenA = Tokens[network]['UsualM']; + const dexHelper = new DummyDexHelper(network); + const usualMUsd0 = new UsualMUsd0(network, dexKey, dexHelper); + + const poolLiquidity = await usualMUsd0.getTopPoolsForToken( + tokenA.address, + 10, + ); + console.log( + `${tokenA.symbol} Top Pools:`, + JSON.stringify(poolLiquidity, null, 2), + ); + + checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); + }); + + it('getTopPoolsForToken: USD0', async function () { + const tokenA = Tokens[network]['USD0']; + const dexHelper = new DummyDexHelper(network); + const usualMUsd0 = new UsualMUsd0(network, dexKey, dexHelper); + + const poolLiquidity = await usualMUsd0.getTopPoolsForToken( + tokenA.address, + 10, + ); + console.log( + `${tokenA.symbol} Top Pools:`, + JSON.stringify(poolLiquidity, null, 2), + ); + + checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey); + }); + }); +}); diff --git a/src/dex/usual/usual-m-usd0.ts b/src/dex/usual/usual-m-usd0.ts new file mode 100644 index 000000000..653450cee --- /dev/null +++ b/src/dex/usual/usual-m-usd0.ts @@ -0,0 +1,76 @@ +import { + Address, + NumberAsString, + DexExchangeParam, + DexConfigMap, +} from '../../types'; +import { SwapSide, Network } from '../../constants'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { DexParams } from './types'; +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { Usual } from './usual'; +import { getDexKeysWithNetwork } from '../../utils'; +import USUAL_DAO_COLLATERAL_ABI from '../../abi/usual-m-usd0/usualCollateralDao.abi.json'; + +const Config: DexConfigMap = + { + UsualMUsd0: { + [Network.MAINNET]: { + usualDaoCollateralAddress: '0xde6e1F680C4816446C8D515989E2358636A38b04', + fromToken: { + address: '0x4cbc25559dbbd1272ec5b64c7b5f48a2405e6470', + decimals: 6, + }, + toToken: { + address: '0x73a15fed60bf67631dc6cd7bc5b6e8da8190acf5', + decimals: 18, + }, + }, + }, + }; + +export class UsualMUsd0 extends Usual { + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(Config); + + usualDaoCollateralIface: Interface; + + constructor( + readonly network: Network, + readonly dexKey: string, + readonly dexHelper: IDexHelper, + ) { + super(network, dexKey, dexHelper, Config[dexKey][network]); + this.usualDaoCollateralIface = new Interface( + USUAL_DAO_COLLATERAL_ABI as JsonFragment[], + ); + } + + async getDexParam( + srcToken: Address, + destToken: Address, + srcAmount: NumberAsString, + destAmount: NumberAsString, + recipient: Address, + data: {}, + side: SwapSide, + ): Promise { + if (this.isFromToken(srcToken) && this.isToToken(destToken)) { + const exchangeData = this.usualDaoCollateralIface.encodeFunctionData( + 'swap', + [srcToken, srcAmount, destAmount], + ); + + return { + needWrapNative: false, + dexFuncHasRecipient: false, + exchangeData, + targetExchange: + Config[this.dexKey][this.network].usualDaoCollateralAddress, + returnAmountPos: undefined, + }; + } + + throw new Error('LOGIC ERROR'); + } +} diff --git a/src/dex/usual/usual-m-wrapped-m.ts b/src/dex/usual/usual-m-wrapped-m.ts new file mode 100644 index 000000000..8eb5d822f --- /dev/null +++ b/src/dex/usual/usual-m-wrapped-m.ts @@ -0,0 +1,71 @@ +import { + Address, + NumberAsString, + DexExchangeParam, + DexConfigMap, +} from '../../types'; +import { SwapSide, Network } from '../../constants'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { DexParams } from './types'; +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { Usual } from './usual'; +import { getDexKeysWithNetwork } from '../../utils'; +import USUALM_ABI from '../../abi/usual-m-wrapped-m/usualM.abi.json'; + +const Config: DexConfigMap = { + UsualMWrappedM: { + [Network.MAINNET]: { + fromToken: { + address: '0x437cc33344a0b27a429f795ff6b469c72698b291', + decimals: 6, + }, + toToken: { + address: '0x4cbc25559dbbd1272ec5b64c7b5f48a2405e6470', + decimals: 6, + }, + }, + }, +}; + +export class UsualMWrappedM extends Usual { + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(Config); + + usualMIface: Interface; + + constructor( + readonly network: Network, + readonly dexKey: string, + readonly dexHelper: IDexHelper, + ) { + super(network, dexKey, dexHelper, Config[dexKey][network]); + this.usualMIface = new Interface(USUALM_ABI as JsonFragment[]); + } + + async getDexParam( + srcToken: Address, + destToken: Address, + srcAmount: NumberAsString, + destAmount: NumberAsString, + recipient: Address, + data: {}, + side: SwapSide, + ): Promise { + if (this.isFromToken(srcToken) && this.isToToken(destToken)) { + const exchangeData = this.usualMIface.encodeFunctionData( + 'wrap(address, uint256)', + [recipient, srcAmount], + ); + + return { + needWrapNative: false, + dexFuncHasRecipient: true, + exchangeData, + targetExchange: this.config.toToken.address, + returnAmountPos: undefined, + }; + } + + throw new Error('LOGIC ERROR'); + } +} diff --git a/src/dex/usual-bond/usual-bond.ts b/src/dex/usual/usual.ts similarity index 51% rename from src/dex/usual-bond/usual-bond.ts rename to src/dex/usual/usual.ts index 295dc0db4..b17019e5d 100644 --- a/src/dex/usual-bond/usual-bond.ts +++ b/src/dex/usual/usual.ts @@ -5,47 +5,30 @@ import { PoolPrices, AdapterExchangeParam, Logger, - NumberAsString, - DexExchangeParam, PoolLiquidity, } from '../../types'; import { SwapSide, Network } from '../../constants'; import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; -import { getDexKeysWithNetwork } from '../../utils'; import { IDex } from '../idex'; import { IDexHelper } from '../../dex-helper/idex-helper'; import { UsualBondData, DexParams } from './types'; import { SimpleExchange } from '../simple-exchange'; -import { UsualBondConfig } from './config'; -import { Interface, JsonFragment } from '@ethersproject/abi'; -import USD0PP_ABI from '../../abi/usual-bond/usd0pp.abi.json'; import { BI_POWS } from '../../bigint-constants'; -export class UsualBond extends SimpleExchange implements IDex { - protected config: DexParams; - +export class Usual extends SimpleExchange implements IDex { readonly hasConstantPriceLargeAmounts = true; readonly needWrapNative = false; readonly isFeeOnTransferSupported = false; - public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = - getDexKeysWithNetwork(UsualBondConfig); - - usd0ppIface: Interface; logger: Logger; constructor( readonly network: Network, readonly dexKey: string, readonly dexHelper: IDexHelper, + readonly config: DexParams, ) { super(dexHelper, dexKey); - const config = UsualBondConfig[dexKey][network]; - this.usd0ppIface = new Interface(USD0PP_ABI as JsonFragment[]); - this.config = { - usd0Address: config.usd0Address.toLowerCase(), - usd0ppAddress: config.usd0ppAddress.toLowerCase(), - }; this.logger = dexHelper.getLogger(dexKey); } @@ -53,20 +36,16 @@ export class UsualBond extends SimpleExchange implements IDex { // No initialization needed for constant price } - getConfig() { - return this.config; + isFromToken(token: string) { + return token.toLowerCase() === this.config.fromToken.address.toLowerCase(); } - is_usd0(token: string) { - return token.toLowerCase() === this.config.usd0Address.toLowerCase(); + isToToken(token: string) { + return token.toLowerCase() === this.config.toToken.address.toLowerCase(); } - is_usd0pp(token: string) { - return token.toLowerCase() === this.config.usd0ppAddress.toLowerCase(); - } - - is_usd0_swap_token(srcToken: string, destToken: string) { - return this.is_usd0(srcToken) && this.is_usd0pp(destToken); + isValidTokens(srcToken: string, destToken: string) { + return this.isFromToken(srcToken) && this.isToToken(destToken); } getAdapters() { @@ -92,8 +71,8 @@ export class UsualBond extends SimpleExchange implements IDex { return []; } - if (this.is_usd0_swap_token(srcTokenAddress, destTokenAddress)) { - return [`${this.dexKey}_${this.config.usd0ppAddress}`]; + if (this.isValidTokens(srcTokenAddress, destTokenAddress)) { + return [`${this.dexKey}_${this.config.toToken}`]; } return []; @@ -111,24 +90,25 @@ export class UsualBond extends SimpleExchange implements IDex { return null; } - const isUSD0SwapToken = this.is_usd0_swap_token( - srcToken.address, - destToken.address, - ); + const isValidSwap = this.isValidTokens(srcToken.address, destToken.address); - if (!isUSD0SwapToken) { + if (!isValidSwap) { return null; } - const unitOut = BI_POWS[18]; // 1:1 swap - const amountsOut = amounts; // 1:1 swap, so output amounts are the same as input + const unitOut = BI_POWS[this.config.toToken.decimals]; // 1:1 swap + const amountsOut = amounts.map( + amount => + (amount * BI_POWS[this.config.toToken.decimals]) / + BI_POWS[this.config.fromToken.decimals], + ); // 1:1 swap, so output amounts are the same as input return [ { unit: unitOut, prices: amountsOut, data: {}, - poolAddresses: [this.config.usd0ppAddress], + poolAddresses: [this.config.toToken.address], exchange: this.dexKey, gasCost: 70000, poolIdentifier: this.dexKey, @@ -151,55 +131,27 @@ export class UsualBond extends SimpleExchange implements IDex { const payload = '0x'; return { - targetExchange: this.config.usd0ppAddress, + targetExchange: this.config.toToken.address, payload, networkFee: '0', }; } - async getDexParam( - srcToken: Address, - destToken: Address, - srcAmount: NumberAsString, - destAmount: NumberAsString, - recipient: Address, - data: UsualBondData, - side: SwapSide, - ): Promise { - if (this.is_usd0(srcToken) && this.is_usd0pp(destToken)) { - const exchangeData = this.usd0ppIface.encodeFunctionData('mint', [ - srcAmount, - ]); - - return { - needWrapNative: false, - dexFuncHasRecipient: false, - exchangeData, - targetExchange: this.config.usd0ppAddress, - returnAmountPos: undefined, - }; - } - throw new Error('LOGIC ERROR'); - } - async getTopPoolsForToken( tokenAddress: Address, limit: number, ): Promise { - const isUsd0 = this.is_usd0(tokenAddress); - if (!isUsd0 && !this.is_usd0pp(tokenAddress)) return []; + const isFromToken = this.isFromToken(tokenAddress); + const isToToken = this.isToToken(tokenAddress); + + if (!(isFromToken || isToToken)) return []; return [ { exchange: this.dexKey, - address: this.config.usd0ppAddress, + address: this.config.toToken.address, connectorTokens: [ - { - decimals: 18, - address: isUsd0 - ? this.config.usd0ppAddress - : this.config.usd0Address, - }, + isFromToken ? this.config.toToken : this.config.fromToken, ], liquidityUSD: 1000000000, // Just returning a big number so this DEX will be preferred }, diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index de23167f1..744baed67 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -131,6 +131,10 @@ export const Tokens: { addBalance: balancesFn, addAllowance: allowedFn, }, + INST: { + address: '0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb', + decimals: 18, + }, aEthUSDC: { address: '0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c', decimals: 6, @@ -555,6 +559,16 @@ export const Tokens: { decimals: 18, symbol: 'wUSDL', }, + WrappedM: { + address: '0x437cc33344a0B27A429f795ff6B469C72698B291', + decimals: 6, + symbol: 'wM', + }, + UsualM: { + address: '0x4cbc25559dbbd1272ec5b64c7b5f48a2405e6470', + decimals: 6, + symbol: 'USUALM', + }, }, [Network.POLYGON]: { jGBP: { @@ -1685,12 +1699,10 @@ export const Holders: { [network: number]: { [tokenAddress: string]: Address }; } = { [Network.MAINNET]: { - wUSDL: '0x50fc9731dAcE42CaA45D166bfF404bBB7464bF21', USDS: '0xB1796E8f1eEcF23027c1E3C00fE303629A189d10', sUSDS: '0xd564B3aE673CAa49D054Bf185bD72a6853763eE7', SKY: '0x0ddda327A6614130CCb20bc0097313A282176A01', MKR: '0xe9aAA7A9DDc0877626C1779AbC29993aD89A6c1f', - ETHx: '0xFCC1A2c71F01B7f58Ed538a6B4AAa5A0724eB5A6', // Idle tokens AA_wstETH: '0xd7C1b48877A7dFA7D51cf1144c89C0A3F134F935', 'AA_idle_cpPOR-USDC': '0x085c8eaccA6911fE60aE3f8FbAe5F3012E3A05Ec', @@ -1794,10 +1806,12 @@ export const Holders: { weETH: '0x267ed5f71EE47D3E45Bb1569Aa37889a2d10f91e', rUSD: '0xEC2eda1C4F981E468ABF62424a10B69B738b498E', arUSD: '0xeFc24206053a452e2299BF3b8f964512b041Db4C', - USD0: '0x6A5d5Af0E266a24648a9d7E8D388EAEc7AbD8433', + USD0: '0x224762e69169E425239EeEE0012d1B0e041C123D', + WrappedM: '0xE0663f2372cAa1459b7ade90812Dc737CE587FA6', 'USD0++': '0x2227b6806339906707b43F36a1f07B52FF7Fa776', USDM: '0x57F5E098CaD7A3D1Eed53991D4d66C45C9AF7812', wUSDM: '0x3B95bC951EE0f553ba487327278cAc44f29715E5', + UsualM: '0xE3f7A0c4a44b740328157A5152A85c3bCB54DA09', }, [Network.POLYGON]: { jGBP: '0x02aa0B826c7BA6386DdBE04C0a8715A1c0A16B24',