From 7a007a357d1ba1ba1b65f4b0614c4b431245f3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Brand=C3=A3o?= Date: Wed, 13 Dec 2023 22:30:23 +0000 Subject: [PATCH] Initial Yearn implementation --- core/src/abis/Yearn/Vault.json | 869 ++++++++++++++++++ core/src/abis/index.ts | 2 + .../asset-strategies/YearnDepositStrategy.ts | 190 ++++ .../asset-strategies/asset-type-strategies.ts | 2 + core/src/transaction/types.ts | 1 + core/tests/protocols/utils.ts | 2 +- core/tests/protocols/yearnDeposit.spec.ts | 41 + 7 files changed, 1106 insertions(+), 1 deletion(-) create mode 100644 core/src/abis/Yearn/Vault.json create mode 100644 core/src/asset-strategies/YearnDepositStrategy.ts create mode 100644 core/tests/protocols/yearnDeposit.spec.ts diff --git a/core/src/abis/Yearn/Vault.json b/core/src/abis/Yearn/Vault.json new file mode 100644 index 0000000..5f171de --- /dev/null +++ b/core/src/abis/Yearn/Vault.json @@ -0,0 +1,869 @@ +[ + { + "name": "Transfer", + "inputs": [ + { "name": "sender", "type": "address", "indexed": true }, + { "name": "receiver", "type": "address", "indexed": true }, + { "name": "value", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "Approval", + "inputs": [ + { "name": "owner", "type": "address", "indexed": true }, + { "name": "spender", "type": "address", "indexed": true }, + { "name": "value", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyAdded", + "inputs": [ + { "name": "strategy", "type": "address", "indexed": true }, + { "name": "debtRatio", "type": "uint256", "indexed": false }, + { "name": "minDebtPerHarvest", "type": "uint256", "indexed": false }, + { "name": "maxDebtPerHarvest", "type": "uint256", "indexed": false }, + { "name": "performanceFee", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyReported", + "inputs": [ + { "name": "strategy", "type": "address", "indexed": true }, + { "name": "gain", "type": "uint256", "indexed": false }, + { "name": "loss", "type": "uint256", "indexed": false }, + { "name": "debtPaid", "type": "uint256", "indexed": false }, + { "name": "totalGain", "type": "uint256", "indexed": false }, + { "name": "totalLoss", "type": "uint256", "indexed": false }, + { "name": "totalDebt", "type": "uint256", "indexed": false }, + { "name": "debtAdded", "type": "uint256", "indexed": false }, + { "name": "debtRatio", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateGovernance", + "inputs": [{ "name": "governance", "type": "address", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateManagement", + "inputs": [{ "name": "management", "type": "address", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateRewards", + "inputs": [{ "name": "rewards", "type": "address", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateDepositLimit", + "inputs": [{ "name": "depositLimit", "type": "uint256", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdatePerformanceFee", + "inputs": [ + { "name": "performanceFee", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateManagementFee", + "inputs": [ + { "name": "managementFee", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateGuardian", + "inputs": [{ "name": "guardian", "type": "address", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "EmergencyShutdown", + "inputs": [{ "name": "active", "type": "bool", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "UpdateWithdrawalQueue", + "inputs": [{ "name": "queue", "type": "address[20]", "indexed": false }], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdateDebtRatio", + "inputs": [ + { "name": "strategy", "type": "address", "indexed": true }, + { "name": "debtRatio", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdateMinDebtPerHarvest", + "inputs": [ + { "name": "strategy", "type": "address", "indexed": true }, + { "name": "minDebtPerHarvest", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdateMaxDebtPerHarvest", + "inputs": [ + { "name": "strategy", "type": "address", "indexed": true }, + { "name": "maxDebtPerHarvest", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyUpdatePerformanceFee", + "inputs": [ + { "name": "strategy", "type": "address", "indexed": true }, + { "name": "performanceFee", "type": "uint256", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyMigrated", + "inputs": [ + { "name": "oldVersion", "type": "address", "indexed": true }, + { "name": "newVersion", "type": "address", "indexed": true } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyRevoked", + "inputs": [{ "name": "strategy", "type": "address", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyRemovedFromQueue", + "inputs": [{ "name": "strategy", "type": "address", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "StrategyAddedToQueue", + "inputs": [{ "name": "strategy", "type": "address", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "initialize", + "inputs": [ + { "name": "token", "type": "address" }, + { "name": "governance", "type": "address" }, + { "name": "rewards", "type": "address" }, + { "name": "nameOverride", "type": "string" }, + { "name": "symbolOverride", "type": "string" } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "initialize", + "inputs": [ + { "name": "token", "type": "address" }, + { "name": "governance", "type": "address" }, + { "name": "rewards", "type": "address" }, + { "name": "nameOverride", "type": "string" }, + { "name": "symbolOverride", "type": "string" }, + { "name": "guardian", "type": "address" } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "initialize", + "inputs": [ + { "name": "token", "type": "address" }, + { "name": "governance", "type": "address" }, + { "name": "rewards", "type": "address" }, + { "name": "nameOverride", "type": "string" }, + { "name": "symbolOverride", "type": "string" }, + { "name": "guardian", "type": "address" }, + { "name": "management", "type": "address" } + ], + "outputs": [] + }, + { + "stateMutability": "pure", + "type": "function", + "name": "apiVersion", + "inputs": [], + "outputs": [{ "name": "", "type": "string" }], + "gas": "5946" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setName", + "inputs": [{ "name": "name", "type": "string" }], + "outputs": [], + "gas": "108344" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setSymbol", + "inputs": [{ "name": "symbol", "type": "string" }], + "outputs": [], + "gas": "73194" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setGovernance", + "inputs": [{ "name": "governance", "type": "address" }], + "outputs": [], + "gas": "37665" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "acceptGovernance", + "inputs": [], + "outputs": [], + "gas": "38937" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setManagement", + "inputs": [{ "name": "management", "type": "address" }], + "outputs": [], + "gas": "39075" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setRewards", + "inputs": [{ "name": "rewards", "type": "address" }], + "outputs": [], + "gas": "39626" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setLockedProfitDegradation", + "inputs": [{ "name": "degradation", "type": "uint256" }], + "outputs": [], + "gas": "37789" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setDepositLimit", + "inputs": [{ "name": "limit", "type": "uint256" }], + "outputs": [], + "gas": "39065" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setPerformanceFee", + "inputs": [{ "name": "fee", "type": "uint256" }], + "outputs": [], + "gas": "39199" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setManagementFee", + "inputs": [{ "name": "fee", "type": "uint256" }], + "outputs": [], + "gas": "39229" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setGuardian", + "inputs": [{ "name": "guardian", "type": "address" }], + "outputs": [], + "gas": "41773" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setEmergencyShutdown", + "inputs": [{ "name": "active", "type": "bool" }], + "outputs": [], + "gas": "41844" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "setWithdrawalQueue", + "inputs": [{ "name": "queue", "type": "address[20]" }], + "outputs": [], + "gas": "1090134" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "transfer", + "inputs": [ + { "name": "receiver", "type": "address" }, + { "name": "amount", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "79308" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "transferFrom", + "inputs": [ + { "name": "sender", "type": "address" }, + { "name": "receiver", "type": "address" }, + { "name": "amount", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "121671" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "approve", + "inputs": [ + { "name": "spender", "type": "address" }, + { "name": "amount", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "38241" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "increaseAllowance", + "inputs": [ + { "name": "spender", "type": "address" }, + { "name": "amount", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "42882" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "decreaseAllowance", + "inputs": [ + { "name": "spender", "type": "address" }, + { "name": "amount", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "42906" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "permit", + "inputs": [ + { "name": "owner", "type": "address" }, + { "name": "spender", "type": "address" }, + { "name": "amount", "type": "uint256" }, + { "name": "expiry", "type": "uint256" }, + { "name": "signature", "type": "bytes" } + ], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "91494" + }, + { + "stateMutability": "view", + "type": "function", + "name": "totalAssets", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "8698" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deposit", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deposit", + "inputs": [{ "name": "_amount", "type": "uint256" }], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deposit", + "inputs": [ + { "name": "_amount", "type": "uint256" }, + { "name": "recipient", "type": "address" } + ], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "maxAvailableShares", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "830798" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "withdraw", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "withdraw", + "inputs": [{ "name": "maxShares", "type": "uint256" }], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "withdraw", + "inputs": [ + { "name": "maxShares", "type": "uint256" }, + { "name": "recipient", "type": "address" } + ], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "withdraw", + "inputs": [ + { "name": "maxShares", "type": "uint256" }, + { "name": "recipient", "type": "address" }, + { "name": "maxLoss", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "pricePerShare", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "43519" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "addStrategy", + "inputs": [ + { "name": "strategy", "type": "address" }, + { "name": "debtRatio", "type": "uint256" }, + { "name": "minDebtPerHarvest", "type": "uint256" }, + { "name": "maxDebtPerHarvest", "type": "uint256" }, + { "name": "performanceFee", "type": "uint256" } + ], + "outputs": [], + "gas": "1523989" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "updateStrategyDebtRatio", + "inputs": [ + { "name": "strategy", "type": "address" }, + { "name": "debtRatio", "type": "uint256" } + ], + "outputs": [], + "gas": "124263" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "updateStrategyMinDebtPerHarvest", + "inputs": [ + { "name": "strategy", "type": "address" }, + { "name": "minDebtPerHarvest", "type": "uint256" } + ], + "outputs": [], + "gas": "47611" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "updateStrategyMaxDebtPerHarvest", + "inputs": [ + { "name": "strategy", "type": "address" }, + { "name": "maxDebtPerHarvest", "type": "uint256" } + ], + "outputs": [], + "gas": "47641" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "updateStrategyPerformanceFee", + "inputs": [ + { "name": "strategy", "type": "address" }, + { "name": "performanceFee", "type": "uint256" } + ], + "outputs": [], + "gas": "42854" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "migrateStrategy", + "inputs": [ + { "name": "oldVersion", "type": "address" }, + { "name": "newVersion", "type": "address" } + ], + "outputs": [], + "gas": "1190208" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "revokeStrategy", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "revokeStrategy", + "inputs": [{ "name": "strategy", "type": "address" }], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "addStrategyToQueue", + "inputs": [{ "name": "strategy", "type": "address" }], + "outputs": [], + "gas": "1255644" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "removeStrategyFromQueue", + "inputs": [{ "name": "strategy", "type": "address" }], + "outputs": [], + "gas": "23636673" + }, + { + "stateMutability": "view", + "type": "function", + "name": "debtOutstanding", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "debtOutstanding", + "inputs": [{ "name": "strategy", "type": "address" }], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "creditAvailable", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "creditAvailable", + "inputs": [{ "name": "strategy", "type": "address" }], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "availableDepositLimit", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "21381" + }, + { + "stateMutability": "view", + "type": "function", + "name": "expectedReturn", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "expectedReturn", + "inputs": [{ "name": "strategy", "type": "address" }], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "report", + "inputs": [ + { "name": "gain", "type": "uint256" }, + { "name": "loss", "type": "uint256" }, + { "name": "_debtPayment", "type": "uint256" } + ], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "1170904" + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "sweep", + "inputs": [{ "name": "token", "type": "address" }], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "sweep", + "inputs": [ + { "name": "token", "type": "address" }, + { "name": "amount", "type": "uint256" } + ], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "name", + "inputs": [], + "outputs": [{ "name": "", "type": "string" }], + "gas": "13920" + }, + { + "stateMutability": "view", + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [{ "name": "", "type": "string" }], + "gas": "11673" + }, + { + "stateMutability": "view", + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "3678" + }, + { + "stateMutability": "view", + "type": "function", + "name": "balanceOf", + "inputs": [{ "name": "arg0", "type": "address" }], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "3923" + }, + { + "stateMutability": "view", + "type": "function", + "name": "allowance", + "inputs": [ + { "name": "arg0", "type": "address" }, + { "name": "arg1", "type": "address" } + ], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4168" + }, + { + "stateMutability": "view", + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "3768" + }, + { + "stateMutability": "view", + "type": "function", + "name": "token", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }], + "gas": "3798" + }, + { + "stateMutability": "view", + "type": "function", + "name": "governance", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }], + "gas": "3828" + }, + { + "stateMutability": "view", + "type": "function", + "name": "management", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }], + "gas": "3858" + }, + { + "stateMutability": "view", + "type": "function", + "name": "guardian", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }], + "gas": "3888" + }, + { + "stateMutability": "view", + "type": "function", + "name": "strategies", + "inputs": [{ "name": "arg0", "type": "address" }], + "outputs": [ + { "name": "performanceFee", "type": "uint256" }, + { "name": "activation", "type": "uint256" }, + { "name": "debtRatio", "type": "uint256" }, + { "name": "minDebtPerHarvest", "type": "uint256" }, + { "name": "maxDebtPerHarvest", "type": "uint256" }, + { "name": "lastReport", "type": "uint256" }, + { "name": "totalDebt", "type": "uint256" }, + { "name": "totalGain", "type": "uint256" }, + { "name": "totalLoss", "type": "uint256" } + ], + "gas": "22641" + }, + { + "stateMutability": "view", + "type": "function", + "name": "withdrawalQueue", + "inputs": [{ "name": "arg0", "type": "uint256" }], + "outputs": [{ "name": "", "type": "address" }], + "gas": "4057" + }, + { + "stateMutability": "view", + "type": "function", + "name": "emergencyShutdown", + "inputs": [], + "outputs": [{ "name": "", "type": "bool" }], + "gas": "3978" + }, + { + "stateMutability": "view", + "type": "function", + "name": "depositLimit", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4008" + }, + { + "stateMutability": "view", + "type": "function", + "name": "debtRatio", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4038" + }, + { + "stateMutability": "view", + "type": "function", + "name": "totalDebt", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4068" + }, + { + "stateMutability": "view", + "type": "function", + "name": "lastReport", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4098" + }, + { + "stateMutability": "view", + "type": "function", + "name": "activation", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4128" + }, + { + "stateMutability": "view", + "type": "function", + "name": "lockedProfit", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4158" + }, + { + "stateMutability": "view", + "type": "function", + "name": "lockedProfitDegradation", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4188" + }, + { + "stateMutability": "view", + "type": "function", + "name": "rewards", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }], + "gas": "4218" + }, + { + "stateMutability": "view", + "type": "function", + "name": "managementFee", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4248" + }, + { + "stateMutability": "view", + "type": "function", + "name": "performanceFee", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4278" + }, + { + "stateMutability": "view", + "type": "function", + "name": "nonces", + "inputs": [{ "name": "arg0", "type": "address" }], + "outputs": [{ "name": "", "type": "uint256" }], + "gas": "4523" + }, + { + "stateMutability": "view", + "type": "function", + "name": "DOMAIN_SEPARATOR", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32" }], + "gas": "4338" + } +] diff --git a/core/src/abis/index.ts b/core/src/abis/index.ts index f49b610..f23ad10 100644 --- a/core/src/abis/index.ts +++ b/core/src/abis/index.ts @@ -17,6 +17,7 @@ import CompoundCometRewardsABI from "./Compound/CometRewards.json"; import UniswapV2FactoryABI from "./UniswapV2/UniswapV2Factory.json"; import UniswapV2PairABI from "./UniswapV2/UniswapV2Pair.json"; import UniswapV2Router02ABI from "./UniswapV2/UniswapV2Router02.json"; +import YearnVaultABI from "./Yearn/Vault.json"; import ZeroXERC20ABI from "./ZeroX/ZeroXERC20.json"; import SavingsDaiABI from "./Maker/SavingsDai.json"; import RouterABI from "./Router.json"; @@ -43,6 +44,7 @@ export const CompoundCometRewards = new Interface(CompoundCometRewardsABI); export const UniswapV2Factory = new Interface(UniswapV2FactoryABI); export const UniswapV2Pair = new Interface(UniswapV2PairABI); export const UniswapV2Router02 = new Interface(UniswapV2Router02ABI); +export const YearnVault = new Interface(YearnVaultABI); export const ZeroXERC20 = new Interface(ZeroXERC20ABI.abi); export const SavingsDai = new Interface(SavingsDaiABI); export const Router = new Interface(RouterABI.abi); diff --git a/core/src/asset-strategies/YearnDepositStrategy.ts b/core/src/asset-strategies/YearnDepositStrategy.ts new file mode 100644 index 0000000..d10dbec --- /dev/null +++ b/core/src/asset-strategies/YearnDepositStrategy.ts @@ -0,0 +1,190 @@ +import { RequestTree } from "../transaction/get-prices-and-linked-assets"; +import { StoreOpType } from "../transaction/types"; +import { + fetchPriceData, + getPrice, +} from "../transaction/asset-type-strategies-helpers"; +import { IERC20, YearnVault } from "core/src/abis"; +import { + FRACTION_MULTIPLIER, + MAGIC_REPLACERS, +} from "core/src/utils/get-magic-offset"; +import { + FetchPriceDataParams, + GetPriceParams, + GenerateStepParams, +} from "./InterfaceStrategy"; +import { InterfaceStrategy } from "./InterfaceStrategy"; + +export class YearnDepositStrategy extends InterfaceStrategy { + fetchPriceData({ provider, assetStore, asset }: FetchPriceDataParams) { + const linkedAsset = assetStore.getAssetById(asset.linkedAssets[0].assetId); + + let requestTree: RequestTree = {}; + + requestTree[asset.address] = {}; + + const fetchedData = fetchPriceData({ + provider, + assetStore, + asset: linkedAsset, + }); + requestTree = { + ...requestTree, + ...fetchedData, + }; + return requestTree; + } + + getPrice({ assetStore, asset, requestTree }: GetPriceParams) { + const linkedAsset = assetStore.getAssetById(asset.linkedAssets[0].assetId); + + return getPrice({ assetStore, asset: linkedAsset, requestTree }); + } + + async generateStep({ + assetAllocation, + assetStore, + walletAddress, + chainId, + value, + currentAllocation, + routerOperation, + }: GenerateStepParams) { + const asset = assetStore.getAssetById(assetAllocation.assetId); + if (asset.linkedAssets.length != 1) { + throw new Error( + `YearnDepositStrategy: asset ${asset.id} should have exactly one linked asset` + ); + } + const linkedAsset = assetStore.getAssetById(asset.linkedAssets[0].assetId); + + const storeNumberToken = routerOperation.stores.findOrInitializeStoreIdx({ + assetId: linkedAsset.id, + }); + const storeNumberYVault = routerOperation.stores.findOrInitializeStoreIdx({ + assetId: asset.id, + }); + + if (assetAllocation.fraction > 0) { + const currentFraction = currentAllocation.getAssetById({ + assetId: linkedAsset.id, + }).fraction; + const newFraction = asset.linkedAssets[0].fraction / currentFraction; + const variation = currentFraction * newFraction; + + currentAllocation.updateFraction({ + assetId: linkedAsset.id, + delta: -variation, + }); + currentAllocation.updateFraction({ + assetId: asset.id, + delta: variation, + }); + + routerOperation.addStep({ + stepAddress: linkedAsset.address, + encodedFunctionData: IERC20.encodeFunctionData("approve", [ + asset.address, + MAGIC_REPLACERS[0], + ]), + storeOperations: [ + { + storeOpType: StoreOpType.RetrieveStoreAssignCall, + storeNumber: storeNumberToken, + offsetReplacer: { + replacer: MAGIC_REPLACERS[0], + occurrence: 0, + }, + fraction: Math.round(FRACTION_MULTIPLIER * newFraction), + }, + ], + }); + + routerOperation.addStep({ + stepAddress: asset.address, + encodedFunctionData: YearnVault.encodeFunctionData( + "deposit(uint256,address)", + [ + MAGIC_REPLACERS[0], // assets + walletAddress, // receiver + ] + ), + encodedFunctionResult: YearnVault.encodeFunctionResult( + "deposit(uint256,address)", + [ + MAGIC_REPLACERS[0], // shares + ] + ), + storeOperations: [ + { + storeOpType: StoreOpType.RetrieveStoreAssignCallSubtract, + storeNumber: storeNumberToken, + offsetReplacer: { replacer: MAGIC_REPLACERS[0], occurrence: 0 }, + fraction: Math.round(newFraction * FRACTION_MULTIPLIER), + }, + { + storeOpType: StoreOpType.RetrieveResultAddStore, + storeNumber: storeNumberYVault, + offsetReplacer: { replacer: MAGIC_REPLACERS[0], occurrence: 0 }, + fraction: FRACTION_MULTIPLIER, + }, + ], + }); + } else if (assetAllocation.fraction < 0) { + const currentFraction = currentAllocation.getAssetById({ + assetId: asset.id, + }).fraction; + const newFraction = -assetAllocation.fraction / currentFraction; + const variation = newFraction * currentFraction; + + asset.linkedAssets.map((la, i) => { + currentAllocation.updateFraction({ + assetId: la.assetId, + delta: variation * la.fraction, + }); + currentAllocation.updateFraction({ + assetId: asset.id, + delta: -variation * la.fraction, + }); + }); + + routerOperation.addStep({ + stepAddress: asset.address, + encodedFunctionData: YearnVault.encodeFunctionData( + "withdraw(uint256,address)", + [ + MAGIC_REPLACERS[0], // maxShares + walletAddress, // recipient + ] + ), + encodedFunctionResult: YearnVault.encodeFunctionResult( + "withdraw(uint256,address)", + [MAGIC_REPLACERS[0]] + ), + storeOperations: [ + { + storeOpType: StoreOpType.RetrieveStoreAssignCallSubtract, + storeNumber: storeNumberYVault, + offsetReplacer: { + replacer: MAGIC_REPLACERS[0], + occurrence: 0, + }, + fraction: Math.round(newFraction * FRACTION_MULTIPLIER), + }, + { + storeOpType: StoreOpType.RetrieveResultAddStore, + storeNumber: storeNumberToken, + offsetReplacer: { + replacer: MAGIC_REPLACERS[0], + occurrence: 0, + }, + fraction: FRACTION_MULTIPLIER, + }, + ], + }); + } + + return routerOperation; + } +} diff --git a/core/src/asset-strategies/asset-type-strategies.ts b/core/src/asset-strategies/asset-type-strategies.ts index 982cf20..12bcdfa 100644 --- a/core/src/asset-strategies/asset-type-strategies.ts +++ b/core/src/asset-strategies/asset-type-strategies.ts @@ -11,6 +11,7 @@ import { InterfaceStrategy } from "./InterfaceStrategy"; import { BalancerDepositStrategy } from "./BalancerDepositStrategy"; import { SavingsDaiDepositStrategy } from "./SavingsDaiDepositStrategy"; import { CompoundDepositStrategy } from "./CompoundV3DepositStrategy"; +import { YearnDepositStrategy } from "./YearnDepositStrategy"; export const assetTypeStrategies: { [interfaceName in AssetType]: InterfaceStrategy; @@ -26,4 +27,5 @@ export const assetTypeStrategies: { compoundV3Deposit: new CompoundDepositStrategy(), stargateDeposit: new StargateDepositStrategy(), savingsDaiDeposit: new SavingsDaiDepositStrategy(), + yearnDeposit: new YearnDepositStrategy(), }; diff --git a/core/src/transaction/types.ts b/core/src/transaction/types.ts index 84d5546..ed5ffa6 100644 --- a/core/src/transaction/types.ts +++ b/core/src/transaction/types.ts @@ -294,6 +294,7 @@ export type AssetType = | "compoundV3Deposit" | "stargateDeposit" | "savingsDaiDeposit" + | "yearnDeposit" | "balancerDeposit" | "uniswapV2Liquidity"; diff --git a/core/tests/protocols/utils.ts b/core/tests/protocols/utils.ts index bbe1145..a208958 100644 --- a/core/tests/protocols/utils.ts +++ b/core/tests/protocols/utils.ts @@ -35,7 +35,7 @@ export async function simulateRouterOperationHelper({ const result = await simulateRouterOperation({ chainId, routerOperation, - provider, + provider: actualProvider, sellAssets: inputAllocation.map((i) => assetStore.getAssetById(i.assetId)), amountsIn: inputAllocation.map((i) => i.amountStr), buyAssets: outputAllocation.map((a) => assetStore.getAssetById(a.assetId)), diff --git a/core/tests/protocols/yearnDeposit.spec.ts b/core/tests/protocols/yearnDeposit.spec.ts new file mode 100644 index 0000000..08e67da --- /dev/null +++ b/core/tests/protocols/yearnDeposit.spec.ts @@ -0,0 +1,41 @@ +import { test } from "vitest"; +import { simulateRouterOperationHelper } from "./utils"; +import { JsonRpcProvider } from "ethers"; + +test("generateTransaction: WETH to yvWETH (yearnDeposit)", async () => { + const provider = new JsonRpcProvider( + "https://rpc.tenderly.co/fork/5e9c3441-0528-4cc0-b9ef-809076a0df43" + ); + await simulateRouterOperationHelper({ + chainId: 1, + inputAllocation: [ + { + assetId: "e9ac450f-efa3-4af1-929d-476c078e9ac6", + amountStr: "1000000000000000000000", + }, + ], + provider, + outputAllocation: [ + { assetId: "e6062600-14a4-46e1-b3a3-62bbf90df7a8", fraction: 1.0 }, + ], + }); +}); + +test.skip("generateTransaction: yvWETH (yearnDeposit) to WETH", async () => { + const provider = new JsonRpcProvider( + "https://rpc.tenderly.co/fork/5e9c3441-0528-4cc0-b9ef-809076a0df43" + ); + await simulateRouterOperationHelper({ + chainId: 1, + inputAllocation: [ + { + assetId: "e6062600-14a4-46e1-b3a3-62bbf90df7a8", + amountStr: "1000000000000000000000", + }, + ], + provider, + outputAllocation: [ + { assetId: "e9ac450f-efa3-4af1-929d-476c078e9ac6", fraction: 1.0 }, + ], + }); +});