From b4ea6e0c4306aefe2d51fa8aaebc6c2357efab9e Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 25 Apr 2024 13:04:08 +0100 Subject: [PATCH 1/9] chore: fix build issues --- package.json | 4 +- ...l+transformer-typescript-types+2.8.3.patch | 13 ++ yarn.lock | 112 +++++++++++++++++- 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 patches/@parcel+transformer-typescript-types+2.8.3.patch diff --git a/package.json b/package.json index 84857b8f..77bf89fa 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "format": "lerna run format --stream", "format:check": "lerna run format:check --stream", "lint": "lerna run lint --stream && yarn clean", - "publish:all": "npx lerna publish --no-private" + "publish:all": "npx lerna publish --no-private", + "postinstall": "patch-package" }, "devDependencies": { "@babel/core": "^7.21.4", @@ -73,6 +74,7 @@ "lerna": "^6.6.1", "mocha": "^10.1.0", "parcel": "2.8.3", + "patch-package": "^8.0.0", "prettier-plugin-solidity": "^1.0.0-beta.17", "solhint": "^3.2.2", "solhint-plugin-prettier": "0.0.5", diff --git a/patches/@parcel+transformer-typescript-types+2.8.3.patch b/patches/@parcel+transformer-typescript-types+2.8.3.patch new file mode 100644 index 00000000..732437f1 --- /dev/null +++ b/patches/@parcel+transformer-typescript-types+2.8.3.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@parcel/transformer-typescript-types/lib/collect.js b/node_modules/@parcel/transformer-typescript-types/lib/collect.js +index a2a7ff1..f9638b0 100644 +--- a/node_modules/@parcel/transformer-typescript-types/lib/collect.js ++++ b/node_modules/@parcel/transformer-typescript-types/lib/collect.js +@@ -78,7 +78,7 @@ function collect(moduleGraph, context, sourceFile) { + } + + if (_typescript().default.isExportDeclaration(node)) { +- if (node.exportClause) { ++ if (node.exportClause && node.exportClause.elements) { + for (let element of node.exportClause.elements) { + if (node.moduleSpecifier) { + var _element$propertyName2; diff --git a/yarn.lock b/yarn.lock index 4e626dce..c4915941 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7367,6 +7367,17 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -7569,7 +7580,7 @@ ci-info@^2.0.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0: +ci-info@^3.2.0, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -8533,6 +8544,15 @@ deferred-leveldown@~4.0.0: abstract-leveldown "~5.0.0" inherits "^2.0.3" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" @@ -9042,6 +9062,18 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-module-lexer@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz" @@ -10728,6 +10760,11 @@ function-bind@^1.1.1: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" @@ -10860,6 +10897,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -11581,6 +11629,13 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" @@ -11671,6 +11726,13 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" @@ -13168,6 +13230,16 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "^0.0.1" +json-stable-stringify@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz" @@ -15923,6 +15995,27 @@ patch-package@^6.2.2: tmp "^0.0.33" yaml "^1.10.2" +patch-package@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61" + integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" + find-yarn-workspace-root "^2.0.0" + fs-extra "^9.0.0" + json-stable-stringify "^1.0.2" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + rimraf "^2.6.3" + semver "^7.5.3" + slash "^2.0.0" + tmp "^0.0.33" + yaml "^2.2.2" + path-browserify@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" @@ -17446,6 +17539,18 @@ set-blocking@^2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" @@ -21108,6 +21213,11 @@ yaml@^1.10.0, yaml@^1.10.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.2: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" + integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== + yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz" From 7eb8977e5e37a32d4394074caa11d7f7c8848fce Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 25 Apr 2024 13:07:08 +0100 Subject: [PATCH 2/9] chore: make cli package private --- packages/summer-cli/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/summer-cli/package.json b/packages/summer-cli/package.json index 239e269b..275939ff 100644 --- a/packages/summer-cli/package.json +++ b/packages/summer-cli/package.json @@ -2,6 +2,7 @@ "name": "@oasisdex/cli", "version": "0.0.0", "description": "Minimalistic boilerplate to quick-start Node.js development in TypeScript.", + "private": true, "engines": { "node": ">= 16.20.2 <19" }, From a93a71d747d5074728a060ec2cd60ec22e2a5746 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 25 Apr 2024 13:07:32 +0100 Subject: [PATCH 3/9] Publish - @oasisdex/addresses@0.1.18-automation - @oasisdex/dma-library@0.6.3-automation --- packages/addresses/package.json | 2 +- packages/dma-library/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/addresses/package.json b/packages/addresses/package.json index cae1549a..c0d33d86 100644 --- a/packages/addresses/package.json +++ b/packages/addresses/package.json @@ -1,6 +1,6 @@ { "name": "@oasisdex/addresses", - "version": "0.1.17-automation", + "version": "0.1.18-automation", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", "main": "lib/index.js", diff --git a/packages/dma-library/package.json b/packages/dma-library/package.json index 36c3c6aa..0ef24297 100644 --- a/packages/dma-library/package.json +++ b/packages/dma-library/package.json @@ -1,6 +1,6 @@ { "name": "@oasisdex/dma-library", - "version": "0.5.21-dma-v2-workers.32-auto-withdraw-to-ltv", + "version": "0.6.3-automation", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", "main": "lib/index.js", From 77f2f0c6bf74febd2a7c13a7ff30575c726c9862 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 26 Apr 2024 09:11:49 +0100 Subject: [PATCH 4/9] fix: all inconsistencies following import of latest from oasis-earn-sc library --- .../constants/load-contract-names.ts | 60 +- .../constants/operation-names.ts | 2 + .../common/erc4626/deposit.ts | 38 ++ .../common/erc4626/index.ts | 2 + .../common/erc4626/withdraw.ts | 34 ++ .../operation-definitions/index.ts | 4 + .../deploy-configurations/types/protocol.ts | 2 + packages/dma-common/types/swap.ts | 2 +- .../factories/ajna/eth-usdc-multiply.ts | 8 +- packages/dma-library/src/actions/ajna/ajna.ts | 8 +- packages/dma-library/src/actions/common.ts | 110 +++- packages/dma-library/src/actions/index.ts | 12 +- .../src/actions/morphoblue/morphoblue.ts | 1 + .../dma-library/src/operations/aave/index.ts | 9 +- .../src/operations/common/erc4626/deposit.ts | 83 +++ .../src/operations/common/erc4626/withdraw.ts | 82 +++ .../src/operations/common/index.ts | 12 + packages/dma-library/src/operations/index.ts | 7 + .../morphoblue/borrow/payback-withdraw.ts | 1 + .../src/operations/morphoblue/index.ts | 6 +- .../morphoblue/multiply/adjust-risk-down.ts | 6 +- .../morphoblue/multiply/adjust-risk-up.ts | 8 +- .../operations/morphoblue/multiply/close.ts | 25 +- .../operations/morphoblue/multiply/open.ts | 26 +- .../dma-library/src/protocols/ajna/consts.ts | 6 + .../dma-library/src/protocols/ajna/index.ts | 502 +--------------- .../dma-library/src/protocols/ajna/utils.ts | 538 ++++++++++++++++++ .../aave-like/multiply/close/close.ts | 1 - .../strategies/ajna/borrow/deposit-borrow.ts | 10 +- .../src/strategies/ajna/borrow/open.ts | 12 +- .../ajna/borrow/payback-withdraw.ts | 10 +- .../strategies/ajna/earn/claim-collateral.ts | 4 +- .../strategies/ajna/earn/deposit-adjust.ts | 14 +- .../src/strategies/ajna/earn/open.ts | 11 +- .../src/strategies/ajna/earn/validations.ts | 14 +- .../strategies/ajna/earn/withdraw-adjust.ts | 4 +- .../src/strategies/ajna/multiply/adjust.ts | 18 +- .../src/strategies/ajna/multiply/close.ts | 48 +- .../src/strategies/ajna/multiply/common.ts | 24 +- .../src/strategies/ajna/multiply/open.ts | 7 +- .../ajna/validation/earn/lup-below-htp.ts | 10 +- .../ajna/validation/earn/price-below-htp.ts | 5 +- .../earn/withdraw-more-than-available.ts | 23 +- .../validation/earn/withdraw-not-available.ts | 31 + .../src/strategies/ajna/validation/index.ts | 2 +- .../ajna/validation/multiply/dustLimit.ts | 7 +- .../morphoblue/common/claim-rewards.ts | 42 -- .../src/strategies/morphoblue/index.ts | 7 - packages/dma-library/src/types/actions.ts | 5 + packages/dma-library/src/views/aave/types.ts | 83 ++- .../dma-library/src/views/common/erc4626.ts | 6 +- packages/dma-library/src/views/index.ts | 11 +- 52 files changed, 1265 insertions(+), 738 deletions(-) create mode 100644 packages/deploy-configurations/operation-definitions/common/erc4626/deposit.ts create mode 100644 packages/deploy-configurations/operation-definitions/common/erc4626/index.ts create mode 100644 packages/deploy-configurations/operation-definitions/common/erc4626/withdraw.ts create mode 100644 packages/dma-library/src/operations/common/erc4626/deposit.ts create mode 100644 packages/dma-library/src/operations/common/erc4626/withdraw.ts create mode 100644 packages/dma-library/src/operations/common/index.ts create mode 100644 packages/dma-library/src/protocols/ajna/consts.ts create mode 100644 packages/dma-library/src/protocols/ajna/utils.ts create mode 100644 packages/dma-library/src/strategies/ajna/validation/earn/withdraw-not-available.ts delete mode 100644 packages/dma-library/src/strategies/morphoblue/common/claim-rewards.ts diff --git a/packages/deploy-configurations/constants/load-contract-names.ts b/packages/deploy-configurations/constants/load-contract-names.ts index 54e6cefd..f63a3bf6 100644 --- a/packages/deploy-configurations/constants/load-contract-names.ts +++ b/packages/deploy-configurations/constants/load-contract-names.ts @@ -2,37 +2,6 @@ import { Network } from '@deploy-configurations/types/network' export function loadContractNames(network: Network) { return SERVICE_REGISTRY_NAMES - // - // if (network === Network.MAINNET) { - // console.log('LOADING MAINNET CONFIG') - // loadedConfig = require('./contract-names.mainnet') - // console.log("LOADED CONFIG: ", JSON.stringify(loadedConfig.SERVICE_REGISTRY_NAMES.common)) - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.OPTIMISM) { - // loadedConfig = require('./contract-names.optimism') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.ARBITRUM) { - // loadedConfig = require('./contract-names.arbitrum') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.BASE) { - // loadedConfig = require('./contract-names.base') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.GOERLI) { - // loadedConfig = require('./contract-names.mainnet') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.SEPOLIA) { - // loadedConfig = require('./contract-names.mainnet') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.LOCAL) { - // loadedConfig = require('./contract-names.local') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else if (network === Network.TEST) { - // loadedConfig = require('./contract-names.test') - // return loadedConfig.SERVICE_REGISTRY_NAMES - // } else { - // throw new Error(`Invalid network: ${network}`) - // } - // return loadedConfig.SERVICE_REGISTRY_NAMES } /** @@ -46,6 +15,7 @@ export function loadContractNames(network: Network) { export const SERVICE_REGISTRY_NAMES = { common: { PULL_TOKEN: 'PullToken_7', + PULL_TOKEN_MAX_AMOUNT: 'PullTokenMaxAmount', SEND_TOKEN: 'SendToken_7', SEND_TOKEN_AUTO: 'SendTokenAuto_7', SET_APPROVAL: 'SetApproval_6', @@ -55,8 +25,12 @@ export const SERVICE_REGISTRY_NAMES = { WRAP_ETH: 'WrapEth_6', UNWRAP_ETH: 'UnwrapEth_6', RETURN_FUNDS: 'ReturnFunds_6', + RETURN_MULTIPLE_TOKENS: 'ReturnMultipleTokens', COLLECT_FEE: 'CollectFee_3', POSITION_CREATED: 'PositionCreated', + TOKEN_BALANCE: 'TokenBalance', + ERC4626_DEPOSIT: 'ERC4626Deposit', + ERC4626_WITHDRAW: 'ERC4626Withdraw', ACCOUNT_GUARD: 'AccountGuard', ACCOUNT_FACTORY: 'AccountFactory', OPERATION_EXECUTOR: 'OperationExecutor_5', @@ -101,13 +75,12 @@ export const SERVICE_REGISTRY_NAMES = { L2_ENCODER: 'AaveL2Encoder', }, spark: { - DEPOSIT: 'SparkDeposit_auto_3', - WITHDRAW: 'SparkWithdraw_auto_3', - WITHDRAW_AUTO: 'SparkWithdrawAuto_auto_3', - BORROW: 'SparkBorrow_auto_3', - PAYBACK: 'SparkPayback_auto_3', + DEPOSIT: 'SparkDeposit', + WITHDRAW: 'SparkWithdraw', + BORROW: 'SparkBorrow_2', + PAYBACK: 'SparkPayback_2', LENDING_POOL: 'SparkLendingPool', - SET_EMODE: 'SparkSetEMode_auto_3', + SET_EMODE: 'SparkSetEMode', }, maker: { DEPOSIT: 'MakerDeposit', @@ -125,23 +98,24 @@ export const SERVICE_REGISTRY_NAMES = { CHAINLOG_VIEW: 'ChainLogView', }, ajna: { - DEPOSIT_BORROW: 'AjnaDepositBorrow', - REPAY_WITHDRAW: 'AjnaRepayWithdraw', - ERC20_POOL_FACTORY: 'ERC20PoolFactory', - AJNA_POOL_UTILS_INFO: 'AjnaPoolUtilsInfo', + DEPOSIT_BORROW: 'AjnaDepositBorrow_5', + REPAY_WITHDRAW: 'AjnaRepayWithdraw_5', + ERC20_POOL_FACTORY: 'ERC20PoolFactoryRc14', + AJNA_POOL_UTILS_INFO: 'AjnaPoolUtilsInfoRc14', }, morphoblue: { MORPHO_BLUE: 'MorphoBlue', DEPOSIT: 'MorphoBlueDeposit', WITHDRAW: 'MorphoBlueWithdraw', BORROW: 'MorphoBlueBorrow', - PAYBACK: 'MorphoBluePayback', + PAYBACK: 'MorphoBluePayback_2', + CLAIM_REWARDS: 'MorphoBlueClaimRewards', }, test: { DUMMY_ACTION: 'DummyAction', DUMMY_OPTIONAL_ACTION: 'DummyOptionalAction', DUMMY_SWAP: 'DummySwap', - DUMMY_EXCHANGE: 'DummyExchange', + DUMMY_EXCHANGE: 'MockExchange', SWAP: 'uSwap', }, } as const diff --git a/packages/deploy-configurations/constants/operation-names.ts b/packages/deploy-configurations/constants/operation-names.ts index 3fa180ad..400fb8b5 100644 --- a/packages/deploy-configurations/constants/operation-names.ts +++ b/packages/deploy-configurations/constants/operation-names.ts @@ -75,6 +75,8 @@ export const OPERATION_NAMES = { }, common: { CUSTOM_OPERATION: 'CustomOperation', + ERC4626_DEPOSIT: 'ERC4626Deposit', + ERC4626_WITHDRAW: 'ERC4626Withdraw', }, } as const diff --git a/packages/deploy-configurations/operation-definitions/common/erc4626/deposit.ts b/packages/deploy-configurations/operation-definitions/common/erc4626/deposit.ts new file mode 100644 index 00000000..13de6616 --- /dev/null +++ b/packages/deploy-configurations/operation-definitions/common/erc4626/deposit.ts @@ -0,0 +1,38 @@ +import { loadContractNames, OPERATION_NAMES } from '@deploy-configurations/constants' +import { Network } from '@deploy-configurations/types/network' +import { getActionHash } from '@deploy-configurations/utils/action-hash' + +export function getErc4626DepositOperationDefinition(network: Network) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return { + name: OPERATION_NAMES.common.ERC4626_DEPOSIT, + actions: [ + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.PULL_TOKEN), + optional: true, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.WRAP_ETH), + optional: true, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.SWAP_ACTION), + optional: true, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.SET_APPROVAL), + optional: false, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.ERC4626_DEPOSIT), + optional: false, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.POSITION_CREATED), + optional: true, + }, + ], + log: false, + } +} diff --git a/packages/deploy-configurations/operation-definitions/common/erc4626/index.ts b/packages/deploy-configurations/operation-definitions/common/erc4626/index.ts new file mode 100644 index 00000000..6777116d --- /dev/null +++ b/packages/deploy-configurations/operation-definitions/common/erc4626/index.ts @@ -0,0 +1,2 @@ +export { getErc4626DepositOperationDefinition } from './deposit' +export { getErc4626WithdrawOperationDefinition } from './withdraw' diff --git a/packages/deploy-configurations/operation-definitions/common/erc4626/withdraw.ts b/packages/deploy-configurations/operation-definitions/common/erc4626/withdraw.ts new file mode 100644 index 00000000..b28542cb --- /dev/null +++ b/packages/deploy-configurations/operation-definitions/common/erc4626/withdraw.ts @@ -0,0 +1,34 @@ +import { loadContractNames, OPERATION_NAMES } from '@deploy-configurations/constants' +import { Network } from '@deploy-configurations/types/network' +import { getActionHash } from '@deploy-configurations/utils/action-hash' + +export function getErc4626WithdrawOperationDefinition(network: Network) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return { + name: OPERATION_NAMES.common.ERC4626_WITHDRAW, + actions: [ + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.ERC4626_WITHDRAW), + optional: false, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.SWAP_ACTION), + optional: true, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.UNWRAP_ETH), + optional: true, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.RETURN_FUNDS), + optional: false, + }, + { + hash: getActionHash(SERVICE_REGISTRY_NAMES.common.RETURN_FUNDS), + optional: true, + }, + ], + log: false, + } +} diff --git a/packages/deploy-configurations/operation-definitions/index.ts b/packages/deploy-configurations/operation-definitions/index.ts index 98615d72..57780d45 100644 --- a/packages/deploy-configurations/operation-definitions/index.ts +++ b/packages/deploy-configurations/operation-definitions/index.ts @@ -53,3 +53,7 @@ export { getMorphoBlueOpenOperationDefinition } from './morphoblue/multiply' export { getMorphoBlueCloseOperationDefinition } from './morphoblue/multiply' export { getMorphoBlueAdjustDownOperationDefinition } from './morphoblue/multiply' export { getMorphoBlueAdjustUpOperationDefinition } from './morphoblue/multiply' + +// COMMON +export { getErc4626DepositOperationDefinition } from './common/erc4626' +export { getErc4626WithdrawOperationDefinition } from './common/erc4626' diff --git a/packages/deploy-configurations/types/protocol.ts b/packages/deploy-configurations/types/protocol.ts index 5aab5eae..236c2d8d 100644 --- a/packages/deploy-configurations/types/protocol.ts +++ b/packages/deploy-configurations/types/protocol.ts @@ -4,6 +4,8 @@ export const ProtocolNames = [ 'Maker', 'Compound', 'Ajna', + 'Ajna_rc13', + 'Ajna_rc14', 'Spark', 'MorphoBlue', ] as const diff --git a/packages/dma-common/types/swap.ts b/packages/dma-common/types/swap.ts index 12a65f95..0d7974a7 100644 --- a/packages/dma-common/types/swap.ts +++ b/packages/dma-common/types/swap.ts @@ -10,5 +10,5 @@ export interface Swap { minToTokenAmount: BigNumber exchangeCalldata: string | number collectFeeFrom: Address - fee: BigNumber + tokenFee: BigNumber } diff --git a/packages/dma-contracts/test/fixtures/factories/ajna/eth-usdc-multiply.ts b/packages/dma-contracts/test/fixtures/factories/ajna/eth-usdc-multiply.ts index ed8406ea..9a25053a 100644 --- a/packages/dma-contracts/test/fixtures/factories/ajna/eth-usdc-multiply.ts +++ b/packages/dma-contracts/test/fixtures/factories/ajna/eth-usdc-multiply.ts @@ -11,7 +11,7 @@ import { } from '@dma-contracts/test/fixtures' import { ETH, MULTIPLE, USDC } from '@dma-contracts/test/fixtures/factories/common' import { AjnaPosition, Network, RiskRatio, strategies } from '@dma-library' -import { AjnaPool, AjnaStrategy } from '@dma-library/types/ajna' +import { AjnaPool, SummerStrategy } from '@dma-library/types/ajna' import BigNumber from 'bignumber.js' import { ethers } from 'ethers' @@ -46,7 +46,7 @@ const ethUsdcMultiplyAjnaPosition: EthUsdcMultiplyAjnaPosition = async ({ const pool = ajnaSystem.pools.wethUsdcPool if (!pool) throw new Error('wethUsdcPool is not set') - await addLiquidityToPool(ajnaSystem, pool, config) + await addLiquidityToPool(ajnaSystem, pool) const ajnaPool = await dependencies.getPoolData(pool.address) @@ -179,7 +179,7 @@ async function getEthUsdcMultiplyAjnaPositionPayload( } async function executeTx( - payload: AjnaStrategy, + payload: SummerStrategy, dependencies: StrategyDependenciesAjna, feeRecipient: string, config: RuntimeConfig, @@ -217,7 +217,7 @@ function buildPositionDetails( dependencies: StrategyDependenciesAjna, tokens: ReturnType, getSwapDataFn: AjnaPositionDetails['getSwapData'], - payload: AjnaStrategy, + payload: SummerStrategy, pool: AjnaPool, feesCollected: BigNumber, mockMarketPrice: BigNumber, diff --git a/packages/dma-library/src/actions/ajna/ajna.ts b/packages/dma-library/src/actions/ajna/ajna.ts index d5665483..f2a5c38c 100644 --- a/packages/dma-library/src/actions/ajna/ajna.ts +++ b/packages/dma-library/src/actions/ajna/ajna.ts @@ -6,11 +6,10 @@ import { ActionFactory } from '@dma-library/actions/action-factory' import { ActionCall, calldataTypes } from '@dma-library/types' import BigNumber from 'bignumber.js' -const SERVICE_REGISTRY_NAMES = loadContractNames(Network.MAINNET) - const createAction = ActionFactory.create export type AjnaDepositBorrowAction = ( + network: Network, args: { quoteToken: string collateralToken: string @@ -30,9 +29,11 @@ export type AjnaDepositBorrowAction = ( ) => ActionCall export const ajnaDepositBorrow: AjnaDepositBorrowAction = ( + network: Network, args, paramsMapping = [0, 0, 0, 0, 0, 0], ) => { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) return createAction( getActionHash(SERVICE_REGISTRY_NAMES.ajna.DEPOSIT_BORROW), [calldataTypes.ajna.DepositBorrow], @@ -51,6 +52,7 @@ export const ajnaDepositBorrow: AjnaDepositBorrowAction = ( } export type AjnaPaybackWithdrawAction = ( + network: Network, args: { quoteToken: string collateralToken: string @@ -72,9 +74,11 @@ export type AjnaPaybackWithdrawAction = ( ) => ActionCall export const ajnaPaybackWithdraw: AjnaPaybackWithdrawAction = ( + network: Network, args, paramsMapping = [0, 0, 0, 0, 0, 0, 0], ) => { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) const withdrawAmount = args.withdrawAmount?.toFixed(0) || ZERO.toFixed(0) const paybackAmount = args.paybackAmount?.toFixed(0) || ZERO.toFixed(0) return createAction( diff --git a/packages/dma-library/src/actions/common.ts b/packages/dma-library/src/actions/common.ts index fa0e3256..2f1b5ea1 100644 --- a/packages/dma-library/src/actions/common.ts +++ b/packages/dma-library/src/actions/common.ts @@ -27,6 +27,25 @@ export function pullToken( ) } +export function pullTokenMaxAmount( + network: Network, + args: { amount: BigNumber; asset: string; from: string }, +) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return createAction( + getActionHash(SERVICE_REGISTRY_NAMES.common.PULL_TOKEN_MAX_AMOUNT), + [calldataTypes.common.PullTokenMaxAmount], + [ + { + amount: args.amount.toFixed(0), + asset: args.asset, + from: args.from, + }, + ], + ) +} + export function setApproval( network: Network, args: { amount: BigNumber | 0; asset: string; delegate: string; sumAmounts: boolean }, @@ -234,21 +253,16 @@ export function returnFunds(network: Network, args: { asset: string }) { ) } -export function collectFee( - network: Network, - args: { asset: string }, - paramsMapping: [amount: number] = [0], -) { +export function returnMultipleTokens(network: Network, args: { assets: string[] }) { const SERVICE_REGISTRY_NAMES = loadContractNames(network) return createAction( - getActionHash(SERVICE_REGISTRY_NAMES.common.COLLECT_FEE), - [calldataTypes.common.CollectFee], + getActionHash(SERVICE_REGISTRY_NAMES.common.RETURN_MULTIPLE_TOKENS), + [calldataTypes.common.ReturnMultipleTokens], [ { - asset: args.asset, + assets: args.assets, }, - paramsMapping, ], ) } @@ -277,3 +291,81 @@ export function positionCreated( ], ) } + +export function tokenBalance( + network: Network, + args: { asset: string; owner: string }, + paramsMapping: [asset: number, owner: number] = [0, 0], +) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return createAction( + getActionHash(SERVICE_REGISTRY_NAMES.common.TOKEN_BALANCE), + [calldataTypes.common.TokenBalance], + [ + { + asset: args.asset, + owner: args.owner, + }, + paramsMapping, + ], + ) +} + +export function erc4626Deposit( + network: Network, + args: { vault: string; amount: BigNumber }, + paramsMapping: [vault: number, amount: number] = [0, 0], +) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return createAction( + getActionHash(SERVICE_REGISTRY_NAMES.common.ERC4626_DEPOSIT), + [calldataTypes.common.Erc4626Deposit], + [ + { + vault: args.vault, + amount: args.amount.toFixed(0), + }, + paramsMapping, + ], + ) +} +export function erc4626Withdraw( + network: Network, + args: { vault: string; amount: BigNumber }, + paramsMapping: [vault: number, amount: number] = [0, 0], +) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return createAction( + getActionHash(SERVICE_REGISTRY_NAMES.common.ERC4626_WITHDRAW), + [calldataTypes.common.Erc4626Withdraw], + [ + { + vault: args.vault, + amount: args.amount.toFixed(0), + }, + paramsMapping, + ], + ) +} + +export function collectFee( + network: Network, + args: { asset: string }, + paramsMapping: [amount: number] = [0], +) { + const SERVICE_REGISTRY_NAMES = loadContractNames(network) + + return createAction( + getActionHash(SERVICE_REGISTRY_NAMES.common.COLLECT_FEE), + [calldataTypes.common.CollectFee], + [ + { + asset: args.asset, + }, + paramsMapping, + ], + ) +} diff --git a/packages/dma-library/src/actions/index.ts b/packages/dma-library/src/actions/index.ts index d5f715a3..5879c227 100644 --- a/packages/dma-library/src/actions/index.ts +++ b/packages/dma-library/src/actions/index.ts @@ -18,15 +18,20 @@ import { } from './ajna' import { collectFee, + erc4626Deposit, + erc4626Withdraw, positionCreated, pullToken, + pullTokenMaxAmount, returnFunds, + returnMultipleTokens, sendToken, sendTokenAuto, setApproval, swap, takeAFlashLoan, takeAFlashLoanBalancer, + tokenBalance, unwrapEth, wrapEth, } from './common' @@ -56,7 +61,9 @@ const aave = { } const common = { + collectFee, pullToken, + pullTokenMaxAmount, sendToken, sendTokenAuto, setApproval, @@ -67,7 +74,10 @@ const common = { unwrapEth, takeAFlashLoan, takeAFlashLoanBalancer, - collectFee, + tokenBalance, + erc4626Deposit, + erc4626Withdraw, + returnMultipleTokens, } const ajna: { diff --git a/packages/dma-library/src/actions/morphoblue/morphoblue.ts b/packages/dma-library/src/actions/morphoblue/morphoblue.ts index 51a0e7ab..a98f8e6a 100644 --- a/packages/dma-library/src/actions/morphoblue/morphoblue.ts +++ b/packages/dma-library/src/actions/morphoblue/morphoblue.ts @@ -94,6 +94,7 @@ export function morphoBluePayback( morphoBlueMarket: MorphoBlueMarket amount: BigNumber onBehalf?: Address + paybackAll?: boolean }, paramsMapping: [amount: number] = [0], ): ActionCall { diff --git a/packages/dma-library/src/operations/aave/index.ts b/packages/dma-library/src/operations/aave/index.ts index 86d91590..33e06993 100644 --- a/packages/dma-library/src/operations/aave/index.ts +++ b/packages/dma-library/src/operations/aave/index.ts @@ -1,4 +1,3 @@ -// Borrow // Auto import { type AaveV3WithdrawOperation, withdraw as aaveV3Withdraw } from './auto/withdraw' import { @@ -62,6 +61,8 @@ const borrow = { depositBorrow: aaveV2DepositBorrow, openDepositBorrow: aaveV2OpenDepositBorrow, paybackWithdraw: aaveV2PaybackWithdraw, + withdraw: () => null, + withdrawToDebt: () => null, }, v3: { borrow: aaveV3Borrow, @@ -69,6 +70,8 @@ const borrow = { depositBorrow: aaveV3DepositBorrow, openDepositBorrow: aaveV3OpenDepositBorrow, paybackWithdraw: aaveV3PaybackWithdraw, + withdraw: () => null, + withdrawToDebt: () => null, }, } @@ -94,6 +97,8 @@ export type AaveBorrowOperations = { depositBorrow: AaveV2DepositBorrowOperation openDepositBorrow: AaveV2OpenDepositBorrowOperation paybackWithdraw: AaveV2PaybackWithdrawOperation + withdraw: any + withdrawToDebt: any } v3: { borrow: AaveV3BorrowOperation @@ -101,6 +106,8 @@ export type AaveBorrowOperations = { depositBorrow: AaveV3DepositBorrowOperation openDepositBorrow: AaveV3OpenDepositBorrowOperation paybackWithdraw: AaveV3PaybackWithdrawOperation + withdraw: any + withdrawToDebt: any } } diff --git a/packages/dma-library/src/operations/common/erc4626/deposit.ts b/packages/dma-library/src/operations/common/erc4626/deposit.ts new file mode 100644 index 00000000..12501de1 --- /dev/null +++ b/packages/dma-library/src/operations/common/erc4626/deposit.ts @@ -0,0 +1,83 @@ +import { ADDRESS_ZERO } from '@deploy-configurations/constants' +import { getErc4626DepositOperationDefinition } from '@deploy-configurations/operation-definitions' +import { Network } from '@deploy-configurations/types/network' +import { actions } from '@dma-library/actions' +import { ActionCall, IOperation, WithProxy, WithSwap } from '@dma-library/types' +import BigNumber from 'bignumber.js' + +import { ZERO } from '../../../../../dma-common/constants/numbers' + +export type Erc4626DepositArgs = { + vault: string + depositToken: string + pullToken: string + amountToDeposit: BigNumber + isPullingEth: boolean + isOpen: boolean +} & Partial & + WithProxy + +export type Erc4626DepositOperation = ( + args: Erc4626DepositArgs, + network: Network, +) => Promise + +export const deposit: Erc4626DepositOperation = async ( + { vault, amountToDeposit, depositToken, pullToken, isPullingEth, swap, proxy, isOpen }, + network, +) => { + // Import ActionCall as it assists type generation + const calls: ActionCall[] = [ + actions.common.pullToken(network, { + amount: amountToDeposit, + asset: pullToken, + from: proxy.owner, + }), + actions.common.wrapEth(network, { + amount: amountToDeposit, + }), + actions.common.swap(network, { + fromAsset: swap ? pullToken : ADDRESS_ZERO, + toAsset: swap ? depositToken : ADDRESS_ZERO, + amount: swap ? swap.amount : ZERO, + receiveAtLeast: swap ? swap.receiveAtLeast : ZERO, + fee: swap ? swap.fee : 0, + withData: swap ? swap.data : '0x00', + collectFeeInFromToken: swap ? swap.collectFeeFrom === 'sourceToken' : false, + }), + actions.common.setApproval( + network, + { + amount: swap ? new BigNumber(0) : amountToDeposit, + asset: depositToken, + delegate: vault, + sumAmounts: false, + }, + [0, 0, swap ? 1 : 0, 0], + ), + actions.common.erc4626Deposit( + network, + { + vault, + amount: amountToDeposit, + }, + [0, swap ? 1 : 0], + ), + actions.common.positionCreated(network, { + protocol: `erc4626-${vault.toLowerCase()}`, + positionType: 'Earn', + collateralToken: depositToken, + debtToken: depositToken, + }), + ] + + calls[0].skipped = isPullingEth + calls[1].skipped = !isPullingEth + calls[2].skipped = !swap + calls[5].skipped = !isOpen + + return { + calls, + operationName: getErc4626DepositOperationDefinition(network).name, + } +} diff --git a/packages/dma-library/src/operations/common/erc4626/withdraw.ts b/packages/dma-library/src/operations/common/erc4626/withdraw.ts new file mode 100644 index 00000000..2b2276ee --- /dev/null +++ b/packages/dma-library/src/operations/common/erc4626/withdraw.ts @@ -0,0 +1,82 @@ +import { ADDRESS_ZERO } from '@deploy-configurations/addresses' +import { getErc4626WithdrawOperationDefinition } from '@deploy-configurations/operation-definitions' +import { Network } from '@deploy-configurations/types/network' +import { actions } from '@dma-library/actions' +import { ActionCall, IOperation, WithProxy, WithSwap } from '@dma-library/types' +import BigNumber from 'bignumber.js' + +import { MAX_UINT, ZERO } from '../../../../../dma-common/constants/numbers' +import { Erc4626StrategyAddresses } from '../../../types/common/erc4626-addresses' + +export type Erc4626WithdrawArgs = { + vault: string + withdrawToken: string + returnToken: string + amountToWithdraw: BigNumber + isWithdrawingEth: boolean + isReturningEth: boolean + isClose: boolean +} & Partial & + WithProxy + +export type Erc4626WithdrawOperation = ( + args: Erc4626WithdrawArgs, + addresses: Erc4626StrategyAddresses, + network: Network, +) => Promise + +export const withdraw: Erc4626WithdrawOperation = async ( + { + isClose, + vault, + amountToWithdraw, + withdrawToken, + returnToken, + isWithdrawingEth, + swap, + isReturningEth, + }, + addresses, + network, +) => { + // Import ActionCall as it assists type generation + + const calls: ActionCall[] = [ + actions.common.erc4626Withdraw(network, { + amount: isClose ? new BigNumber(MAX_UINT) : amountToWithdraw, + vault, + }), + actions.common.swap(network, { + fromAsset: swap ? withdrawToken : ADDRESS_ZERO, + toAsset: swap ? returnToken : ADDRESS_ZERO, + amount: swap ? swap.amount : ZERO, + receiveAtLeast: swap ? swap.receiveAtLeast : ZERO, + fee: swap ? swap.fee : 0, + withData: swap ? swap.data : '0x00', + collectFeeInFromToken: swap ? swap.collectFeeFrom === 'sourceToken' : false, + }), + actions.common.unwrapEth(network, { + amount: new BigNumber(MAX_UINT), + }), + actions.common.returnFunds(network, { + asset: isReturningEth ? addresses.tokens.ETH : returnToken, + }), + actions.common.returnFunds(network, { + asset: isWithdrawingEth ? addresses.tokens.ETH : withdrawToken, + }), + ] + /* + we skip unwrapping of WETH if we are not returning ETH or withdrawing ETH + we unwrap WETH if we are returning ETH (after swap or after withdraw) + or withdrawing leftover ETH ( difference between what we withdraw and what we swap) + we skip the second return funds action if there is no swap - we withdraw and return the same token + */ + calls[2].skipped = !isReturningEth && !isWithdrawingEth + calls[1].skipped = !swap + calls[4].skipped = !swap + + return { + calls, + operationName: getErc4626WithdrawOperationDefinition(network).name, + } +} diff --git a/packages/dma-library/src/operations/common/index.ts b/packages/dma-library/src/operations/common/index.ts new file mode 100644 index 00000000..def84785 --- /dev/null +++ b/packages/dma-library/src/operations/common/index.ts @@ -0,0 +1,12 @@ +import { deposit as erc4626Deposit, Erc4626DepositOperation } from './erc4626/deposit' +import { Erc4626WithdrawOperation, withdraw as erc4626Withdraw } from './erc4626/withdraw' + +export type Erc4626Operations = { + deposit: Erc4626DepositOperation + withdraw: Erc4626WithdrawOperation +} + +export const erc4626Operations: Erc4626Operations = { + deposit: erc4626Deposit, + withdraw: erc4626Withdraw, +} diff --git a/packages/dma-library/src/operations/index.ts b/packages/dma-library/src/operations/index.ts index 58b08b19..5006e245 100644 --- a/packages/dma-library/src/operations/index.ts +++ b/packages/dma-library/src/operations/index.ts @@ -1,5 +1,7 @@ import { AaveOperations, aaveOperations } from './aave' import { AjnaOperations, ajnaOperations } from './ajna' +import { Erc4626Operations, erc4626Operations } from './common' +import { MorphoBlueOperations, morphoBlueOperations } from './morphoblue' import { SparkOperations, sparkOperations } from './spark' export { AaveBorrowOperations, AaveMultiplyOperations } from './aave' @@ -8,15 +10,20 @@ export { BorrowArgs, DepositArgs } from './aave-like' const ajna = ajnaOperations const spark = sparkOperations const aave = aaveOperations +const morphoblue = morphoBlueOperations export const operations: { ajna: AjnaOperations aave: AaveOperations spark: SparkOperations + morphoblue: MorphoBlueOperations + erc4626Operations: Erc4626Operations } = { aave, ajna, spark, + morphoblue, + erc4626Operations, } export { diff --git a/packages/dma-library/src/operations/morphoblue/borrow/payback-withdraw.ts b/packages/dma-library/src/operations/morphoblue/borrow/payback-withdraw.ts index 3770b6a5..78b74d63 100644 --- a/packages/dma-library/src/operations/morphoblue/borrow/payback-withdraw.ts +++ b/packages/dma-library/src/operations/morphoblue/borrow/payback-withdraw.ts @@ -11,6 +11,7 @@ export type MorphoBluePaybackWithdrawArgs = { morphoBlueMarket: MorphoBlueMarket amountCollateralToWithdrawInBaseUnit: BigNumber amountDebtToPaybackInBaseUnit: BigNumber + isPaybackAll: boolean proxy: string user: string } diff --git a/packages/dma-library/src/operations/morphoblue/index.ts b/packages/dma-library/src/operations/morphoblue/index.ts index 3d098c15..14508b72 100644 --- a/packages/dma-library/src/operations/morphoblue/index.ts +++ b/packages/dma-library/src/operations/morphoblue/index.ts @@ -23,20 +23,19 @@ import { import { close as morphoBlueClose, MorphoBlueCloseOperation } from './multiply/close' import { MorphoBlueOpenOperation, open as morphoBlueOpen } from './multiply/open' -const borrow = { +const borrow: MorphoBlueBorrowOperations = { borrow: morphoBlueBorrow, deposit: morphoBlueDeposit, depositBorrow: morphoBlueDepositBorrow, openDepositBorrow: morphoBlueOpenDepositBorrow, paybackWithdraw: morphoBluePaybackWithdraw, } -const multiply = { +const multiply: MorphoBlueMultiplyOperations = { open: morphoBlueOpen, close: morphoBlueClose, adjustRiskUp: morphoBlueAdjustRiskUp, adjustRiskDown: morphoBlueAdjustRiskDown, } - export type MorphoBlueBorrowOperations = { borrow: MorphoBlueBorrowOperation deposit: MorphoBlueDepositOperation @@ -51,7 +50,6 @@ export type MorphoBlueMultiplyOperations = { adjustRiskUp: MorphoBlueAdjustUpOperation adjustRiskDown: MorphoBlueAdjustDownOperation } - export type MorphoBlueOperations = { borrow: MorphoBlueBorrowOperations multiply: MorphoBlueMultiplyOperations diff --git a/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-down.ts b/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-down.ts index e64dca1c..543ac98d 100644 --- a/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-down.ts +++ b/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-down.ts @@ -4,11 +4,11 @@ import { actions } from '@dma-library/actions' import { BALANCER_FEE } from '@dma-library/config/flashloan-fees' import { IOperation } from '@dma-library/types' import { - WithAaveLikeStrategyAddresses, WithCollateralAndWithdrawal, WithDebt, WithFlashloan, WithMorphoBlueMarket, + WithMorphpBlueStrategyAddresses, WithNetwork, WithProxy, WithSwap, @@ -21,7 +21,7 @@ export type MorphoBlueAdjustRiskDownArgs = WithMorphoBlueMarket & WithSwap & WithFlashloan & WithProxy & - WithAaveLikeStrategyAddresses & + WithMorphpBlueStrategyAddresses & WithNetwork export type MorphoBlueAdjustDownOperation = ({ @@ -61,7 +61,7 @@ export const adjustRiskDown: MorphoBlueAdjustDownOperation = async ({ // Resulting risk will be same as simulation given that dust amount is transferred to user const setDebtTokenApprovalOnPool = actions.common.setApproval(network, { asset: debt.address, - delegate: addresses.lendingPool, + delegate: addresses.morphoblue, amount: flashloan.token.amount, sumAmounts: false, }) diff --git a/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-up.ts b/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-up.ts index 2cac61a1..c897efd5 100644 --- a/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-up.ts +++ b/packages/dma-library/src/operations/morphoblue/multiply/adjust-risk-up.ts @@ -3,11 +3,11 @@ import { ZERO } from '@dma-common/constants' import { actions } from '@dma-library/actions' import { IOperation } from '@dma-library/types' import { - WithAaveLikeStrategyAddresses, WithCollateral, WithDebtAndBorrow, WithFlashloan, WithMorphoBlueMarket, + WithMorphpBlueStrategyAddresses, WithNetwork, WithOptionalDeposit, WithProxy, @@ -23,7 +23,7 @@ export type MorphoBlueAdjustRiskUpArgs = WithMorphoBlueMarket & WithSwap & WithFlashloan & WithProxy & - WithAaveLikeStrategyAddresses & + WithMorphpBlueStrategyAddresses & WithNetwork export type MorphoBlueAdjustUpOperation = ({ @@ -88,7 +88,7 @@ export const adjustRiskUp: MorphoBlueAdjustUpOperation = async ({ network, { asset: collateral.address, - delegate: addresses.lendingPool, + delegate: addresses.morphoblue, amount: depositAmount, sumAmounts: true, }, @@ -102,7 +102,7 @@ export const adjustRiskUp: MorphoBlueAdjustUpOperation = async ({ amount: depositAmount, sumAmounts: true, }, - [swapActionStorageIndex, 0], + [0, swapActionStorageIndex], ) const borrowDebtToRepayFL = actions.morphoblue.borrow(network, { diff --git a/packages/dma-library/src/operations/morphoblue/multiply/close.ts b/packages/dma-library/src/operations/morphoblue/multiply/close.ts index c5cf0ee3..391d07b7 100644 --- a/packages/dma-library/src/operations/morphoblue/multiply/close.ts +++ b/packages/dma-library/src/operations/morphoblue/multiply/close.ts @@ -1,5 +1,5 @@ import { getMorphoBlueCloseOperationDefinition } from '@deploy-configurations/operation-definitions' -import { FEE_BASE, MAX_UINT, ZERO } from '@dma-common/constants' +import { FEE_BASE, MAX_UINT } from '@dma-common/constants' import { actions } from '@dma-library/actions' import { BALANCER_FEE } from '@dma-library/config/flashloan-fees' import { @@ -8,11 +8,16 @@ import { WithDebt, WithFlashloan, WithNetwork, + WithPaybackDebt, WithPositionAndLockedCollateral, WithProxy, WithSwap, + WithWithdrawCollateral, } from '@dma-library/types' -import { WithAaveLikeStrategyAddresses, WithMorphoBlueMarket } from '@dma-library/types/operations' +import { + WithMorphoBlueMarket, + WithMorphpBlueStrategyAddresses, +} from '@dma-library/types/operations' import BigNumber from 'bignumber.js' export type MorphoBlueCloseArgs = WithMorphoBlueMarket & @@ -22,8 +27,10 @@ export type MorphoBlueCloseArgs = WithMorphoBlueMarket & WithFlashloan & WithProxy & WithPositionAndLockedCollateral & - WithAaveLikeStrategyAddresses & - WithNetwork + WithMorphpBlueStrategyAddresses & + WithNetwork & + WithPaybackDebt & + WithWithdrawCollateral export type MorphoBlueCloseOperation = ({ morphoBlueMarket, @@ -45,6 +52,8 @@ export const close: MorphoBlueCloseOperation = async ({ proxy, addresses, network, + amountDebtToPaybackInBaseUnit, + amountCollateralToWithdrawInBaseUnit, }) => { if (collateral.address !== morphoBlueMarket.collateralToken) { throw new Error('Collateral token must be the same as MorphoBlue market collateral token') @@ -55,19 +64,21 @@ export const close: MorphoBlueCloseOperation = async ({ const setDebtTokenApprovalOnPool = actions.common.setApproval(network, { asset: debt.address, - delegate: addresses.lendingPool, + delegate: addresses.morphoblue, amount: flashloan.token.amount, sumAmounts: false, }) const paybackDebt = actions.morphoblue.payback(network, { morphoBlueMarket: morphoBlueMarket, - amount: ZERO, + amount: amountDebtToPaybackInBaseUnit, + onBehalf: proxy.address, + paybackAll: true, }) const withdrawCollateral = actions.morphoblue.withdraw(network, { morphoBlueMarket: morphoBlueMarket, - amount: new BigNumber(MAX_UINT), + amount: amountCollateralToWithdrawInBaseUnit, to: proxy.address, }) diff --git a/packages/dma-library/src/operations/morphoblue/multiply/open.ts b/packages/dma-library/src/operations/morphoblue/multiply/open.ts index c3effc01..314a893a 100644 --- a/packages/dma-library/src/operations/morphoblue/multiply/open.ts +++ b/packages/dma-library/src/operations/morphoblue/multiply/open.ts @@ -49,7 +49,7 @@ export const open: MorphoBlueOpenOperation = async ({ collateral, debt, deposit, - // swap, + swap, flashloan, proxy, position, @@ -85,15 +85,15 @@ export const open: MorphoBlueOpenOperation = async ({ // No previous actions store values with OpStorage const swapActionStorageIndex = 1 - // const swapDebtTokensForCollateralTokens = actions.common.swap(network, { - // fromAsset: debt.address, - // toAsset: collateral.address, - // amount: swap.amount, - // receiveAtLeast: swap.receiveAtLeast, - // fee: swap.fee, - // withData: swap.data, - // collectFeeInFromToken: swap.collectFeeFrom === 'sourceToken', - // }) + const swapDebtTokensForCollateralTokens = actions.common.swap(network, { + fromAsset: debt.address, + toAsset: collateral.address, + amount: swap.amount, + receiveAtLeast: swap.receiveAtLeast, + fee: swap.fee, + withData: swap.data, + collectFeeInFromToken: swap.collectFeeFrom === 'sourceToken', + }) const setCollateralApproval = actions.common.setApproval( network, @@ -113,7 +113,7 @@ export const open: MorphoBlueOpenOperation = async ({ amount: depositAmount, sumAmounts: true, }, - [swapActionStorageIndex, 0], + [0, swapActionStorageIndex], ) const borrowDebtTokens = actions.morphoblue.borrow(network, { @@ -138,8 +138,8 @@ export const open: MorphoBlueOpenOperation = async ({ const flashloanCalls = [ pullCollateralTokensToProxy, - // wrapEth, - // swapDebtTokensForCollateralTokens, + wrapEth, + swapDebtTokensForCollateralTokens, setCollateralApproval, depositCollateral, borrowDebtTokens, diff --git a/packages/dma-library/src/protocols/ajna/consts.ts b/packages/dma-library/src/protocols/ajna/consts.ts new file mode 100644 index 00000000..f437b19d --- /dev/null +++ b/packages/dma-library/src/protocols/ajna/consts.ts @@ -0,0 +1,6 @@ +export const ajnaCollateralizationFactor = 1.04 + +// This offset is needed for actions like paybackAll and withdrawAll because of the debt that is constantly growing over time +// performing these actions without this buffer would lead to issues with tx since params passed will be already out of date +// while sending tx +export const ajnaPaybackAllWithdrawAllValueOffset = 0.00005 // 0.005% diff --git a/packages/dma-library/src/protocols/ajna/index.ts b/packages/dma-library/src/protocols/ajna/index.ts index a0dacea2..60912fdb 100644 --- a/packages/dma-library/src/protocols/ajna/index.ts +++ b/packages/dma-library/src/protocols/ajna/index.ts @@ -1,500 +1,2 @@ -import { ZERO } from '@dma-common/constants' -import { negativeToZero } from '@dma-common/utils/common' -import { ajnaBuckets } from '@dma-library/strategies' -import { getAjnaEarnValidations } from '@dma-library/strategies/ajna/earn/validations' -import { - getLiquidityInLupBucket, - getPoolLiquidity, - getTotalPoolLiquidity, -} from '@dma-library/strategies/ajna/validation/borrowish/notEnoughLiquidity' -import { SwapData } from '@dma-library/types' -import { - AjnaCommonDependencies, - AjnaCommonDMADependencies, - AjnaEarnActions, - AjnaEarnPayload, - AjnaEarnPosition, - AjnaError, - AjnaNotice, - AjnaPool, - AjnaStrategy, - AjnaSuccess, - AjnaWarning, -} from '@dma-library/types/ajna' -import BigNumber from 'bignumber.js' -import { ethers } from 'ethers' - -export const prepareAjnaDMAPayload = ({ - dependencies, - targetPosition, - errors, - warnings, - data, - txValue, - swaps, -}: { - dependencies: AjnaCommonDMADependencies - targetPosition: T - errors: AjnaError[] - warnings: AjnaWarning[] - notices: AjnaNotice[] - successes: AjnaSuccess[] - data: string - txValue: string - swaps: (SwapData & { collectFeeFrom: 'sourceToken' | 'targetToken'; tokenFee: BigNumber })[] -}): AjnaStrategy => { - return { - simulation: { - swaps: swaps.map(swap => ({ - fromTokenAddress: swap.fromTokenAddress, - toTokenAddress: swap.toTokenAddress, - fromTokenAmount: swap.fromTokenAmount, - toTokenAmount: swap.toTokenAmount, - minToTokenAmount: swap.minToTokenAmount, - exchangeCalldata: swap.exchangeCalldata, - collectFeeFrom: swap.collectFeeFrom, - fee: swap.tokenFee, - })), - errors, - warnings, - notices: [], - successes: [], - targetPosition, - position: targetPosition, - }, - tx: { - to: dependencies.operationExecutor, - data, - value: txValue, - }, - } -} - -export const prepareAjnaPayload = ({ - dependencies, - targetPosition, - errors, - warnings, - notices, - successes, - data, - txValue, -}: { - dependencies: AjnaCommonDependencies - targetPosition: T - errors: AjnaError[] - warnings: AjnaWarning[] - notices: AjnaNotice[] - successes: AjnaSuccess[] - data: string - txValue: string -}): AjnaStrategy => { - return { - simulation: { - swaps: [], - errors, - warnings, - notices, - successes, - targetPosition, - position: targetPosition, - }, - tx: { - to: dependencies.ajnaProxyActions, - data, - value: txValue, - }, - } -} - -export const getAjnaEarnActionOutput = async ({ - targetPosition, - data, - dependencies, - args, - action, - txValue, -}: { - targetPosition: AjnaEarnPosition - data: string - dependencies: AjnaCommonDependencies - args: AjnaEarnPayload - action: AjnaEarnActions - txValue: string -}) => { - const afterLupIndex = ['deposit-earn', 'withdraw-earn'].includes(action) - ? calculateNewLupWhenAdjusting(args.position.pool, args.position, targetPosition).newLupIndex - : undefined - - const { errors, warnings, notices, successes } = getAjnaEarnValidations({ - price: args.price, - quoteAmount: args.quoteAmount, - quoteTokenPrecision: args.quoteTokenPrecision, - position: args.position, - simulation: targetPosition, - afterLupIndex, - action, - }) - - return prepareAjnaPayload({ - dependencies, - targetPosition, - errors, - warnings, - notices, - successes, - data, - txValue, - }) -} - -export const resolveAjnaEthAction = (isUsingEth: boolean, amount: BigNumber) => - isUsingEth ? ethers.utils.parseEther(amount.toString()).toString() : '0' - -export const calculateAjnaApyPerDays = (amount: BigNumber, apy: BigNumber, days: number) => - amount - // converted to numbers because BigNumber doesn't handle power with decimals - .times(new BigNumber(Math.E ** apy.times(days).toNumber())) - .minus(amount) - .div(amount) - -// The origination fee is calculated as the greatest of the current annualized -// borrower interest rate divided by 52 (one week of interest) or 5 bps multiplied by the loan’s new -// debt. -export const getAjnaBorrowOriginationFee = ({ - interestRate, - quoteAmount, -}: { - interestRate: BigNumber - quoteAmount: BigNumber -}) => { - const weeklyInterestRate = interestRate.div(52) - const fiveBasisPoints = new BigNumber(0.0005) - - return BigNumber.max(weeklyInterestRate, fiveBasisPoints).times(quoteAmount) -} - -function getSimulationPoolOutput( - positionCollateral: BigNumber, - positionDebt: BigNumber, - debtChange: BigNumber, - pool: AjnaPool, - newLup: BigNumber, - newLupIndex: BigNumber, -) { - const thresholdPrice = !positionCollateral.eq(0) - ? positionDebt.dividedBy(positionCollateral) - : ZERO - - const newHtp = thresholdPrice.gt(pool.htp) ? thresholdPrice : pool.htp - - return { - ...pool, - lup: newLup, - lowestUtilizedPrice: newLup, - lowestUtilizedPriceIndex: newLupIndex, - htp: newHtp, - highestThresholdPrice: newHtp, - // TODO this is old index, we need to map newHtp to index - highestThresholdPriceIndex: pool.highestThresholdPriceIndex, - - debt: pool.debt.plus(debtChange), - } -} - -function getMaxGenerate( - pool: AjnaPool, - positionDebt: BigNumber, - positionCollateral: BigNumber, - maxDebt: BigNumber = ZERO, -): BigNumber { - const initialMaxDebt = positionCollateral.times(pool.lowestUtilizedPrice).minus(positionDebt) - - const liquidityAvailableInLupBucket = getLiquidityInLupBucket(pool) - - if (initialMaxDebt.lte(liquidityAvailableInLupBucket)) { - return initialMaxDebt.isNegative() ? maxDebt : initialMaxDebt.plus(maxDebt) - } - - const sortedBuckets = [...pool.buckets].sort((a, b) => a.index.minus(b.index).toNumber()) - - const lupBucketArrayIndex = sortedBuckets.findIndex(bucket => - bucket.index.isEqualTo(pool.lowestUtilizedPriceIndex), - ) - - const bucketBelowLup = sortedBuckets[lupBucketArrayIndex + 1] - - if (!bucketBelowLup) { - return maxDebt.plus(liquidityAvailableInLupBucket) - } - - const newPool = getSimulationPoolOutput( - positionCollateral, - positionDebt, - liquidityAvailableInLupBucket, - pool, - bucketBelowLup.price, - bucketBelowLup.index, - ) - - return getMaxGenerate( - newPool, - positionDebt.plus(liquidityAvailableInLupBucket), - positionCollateral, - liquidityAvailableInLupBucket.plus(maxDebt), - ) -} - -export function calculateMaxGenerate( - pool: AjnaPool, - positionDebt: BigNumber, - collateralAmount: BigNumber, -) { - const maxDebtWithoutFee = getMaxGenerate(pool, positionDebt, collateralAmount) - - const originationFee = getAjnaBorrowOriginationFee({ - interestRate: pool.interestRate, - quoteAmount: maxDebtWithoutFee, - }) - - const poolLiquidity = getPoolLiquidity({ - buckets: pool.buckets, - debt: pool.debt, - }) - const poolLiquidityWithFee = poolLiquidity.minus(originationFee) - const maxDebtWithFee = maxDebtWithoutFee.minus(originationFee) - - if (poolLiquidityWithFee.lt(maxDebtWithFee)) { - return negativeToZero(poolLiquidityWithFee) - } - - return negativeToZero(maxDebtWithFee) -} - -export function calculateNewLup(pool: AjnaPool, debtChange: BigNumber): [BigNumber, BigNumber] { - const sortedBuckets = [...pool.buckets].sort((a, b) => a.index.minus(b.index).toNumber()) - const totalPoolLiquidity = getTotalPoolLiquidity(pool.buckets) - - let remainingDebt = pool.debt.plus(debtChange) - let newLup = sortedBuckets[0] ? sortedBuckets[0].price : pool.lowestUtilizedPrice - let newLupIndex = sortedBuckets[0] ? sortedBuckets[0].index : pool.lowestUtilizedPriceIndex - - if (remainingDebt.gt(totalPoolLiquidity)) { - newLup = sortedBuckets[sortedBuckets.length - 1].price - newLupIndex = sortedBuckets[sortedBuckets.length - 1].index - remainingDebt = ZERO - - return [newLup, newLupIndex] - } - - sortedBuckets.forEach(bucket => { - if (remainingDebt.gt(bucket.quoteTokens)) { - remainingDebt = remainingDebt.minus(bucket.quoteTokens) - } else { - if (remainingDebt.gt(0)) { - newLup = bucket.price - newLupIndex = bucket.index - remainingDebt = ZERO - } - } - }) - return [newLup, newLupIndex] -} - -export function calculateNewLupWhenAdjusting( - pool: AjnaPool, - position: AjnaEarnPosition, - simulation?: AjnaEarnPosition, -) { - if (!simulation) { - return { - newLupPrice: pool.lowestUtilizedPrice, - newLupIndex: pool.lowestUtilizedPriceIndex, - } - } - - let newLupPrice = ZERO - let newLupIndex = ZERO - - const oldBucket = pool.buckets.find(bucket => bucket.price.eq(position.price)) - - if (!oldBucket) { - return { - newLupPrice, - newLupIndex, - } - } - - const poolBuckets = [...pool.buckets].filter(bucket => !bucket.index.eq(oldBucket.index)) - - poolBuckets.push({ - ...oldBucket, - quoteTokens: oldBucket.quoteTokens.minus(position.quoteTokenAmount), - }) - - const newBucketIndex = new BigNumber( - ajnaBuckets.findIndex(bucket => new BigNumber(bucket).eq(simulation.price.shiftedBy(18))), - ) - - const existingBucketArrayIndex = poolBuckets.findIndex(bucket => bucket.index.eq(newBucketIndex)) - - if (existingBucketArrayIndex !== -1) { - poolBuckets[existingBucketArrayIndex].quoteTokens = poolBuckets[ - existingBucketArrayIndex - ].quoteTokens.plus(simulation.quoteTokenAmount) - } else { - poolBuckets.push({ - price: simulation.price, - index: newBucketIndex, - quoteTokens: simulation.quoteTokenAmount, - bucketLPs: ZERO, - collateral: ZERO, - }) - } - - const sortedBuckets = [...poolBuckets].sort((a, b) => a.index.minus(b.index).toNumber()) - - let remainingDebt = pool.debt - - for (let i = 0; i < sortedBuckets.length; i++) { - const bucket = sortedBuckets[i] - - if (remainingDebt.gt(bucket.quoteTokens)) { - remainingDebt = remainingDebt.minus(bucket.quoteTokens) - } else { - newLupPrice = bucket.price - newLupIndex = bucket.index - break - } - } - - return { - newLupPrice, - newLupIndex, - } -} - -export function simulatePool( - pool: AjnaPool, - debtChange: BigNumber, - positionDebt: BigNumber, - positionCollateral: BigNumber, -): AjnaPool { - const [newLup, newLupIndex] = calculateNewLup(pool, debtChange) - - return getSimulationPoolOutput( - positionCollateral, - positionDebt, - debtChange, - pool, - newLup, - newLupIndex, - ) -} - -const resolveMaxLiquidityWithdraw = (availableToWithdraw: BigNumber, quoteTokenAmount: BigNumber) => - negativeToZero(availableToWithdraw.gte(quoteTokenAmount) ? quoteTokenAmount : availableToWithdraw) - -export const calculateAjnaMaxLiquidityWithdraw = ({ - availableToWithdraw = ZERO, - pool, - position, - poolCurrentLiquidity, - simulation, -}: { - availableToWithdraw?: BigNumber - pool: AjnaPool - poolCurrentLiquidity: BigNumber - position: AjnaEarnPosition - simulation?: AjnaEarnPosition -}) => { - if (availableToWithdraw.gt(poolCurrentLiquidity)) { - return position.quoteTokenAmount.gt(poolCurrentLiquidity) - ? poolCurrentLiquidity - : position.quoteTokenAmount - } - - if ( - availableToWithdraw.gte(position.quoteTokenAmount) || - pool.lowestUtilizedPriceIndex.isZero() || - position.priceIndex?.gt(pool.lowestUtilizedPriceIndex) - ) { - return position.quoteTokenAmount - } - - const { newLupIndex } = calculateNewLupWhenAdjusting(pool, position, simulation) - - if (newLupIndex.gt(pool.highestThresholdPriceIndex)) { - return resolveMaxLiquidityWithdraw(availableToWithdraw, position.quoteTokenAmount) - } - - const buckets = pool.buckets.filter(bucket => bucket.index.lte(pool.highestThresholdPriceIndex)) - - const lupBucket = buckets.find(bucket => bucket.index.eq(pool.lowestUtilizedPriceIndex)) - const lupBucketIndex = buckets.findIndex(bucket => bucket.index.eq(pool.lowestUtilizedPriceIndex)) - - if (!lupBucket) { - return resolveMaxLiquidityWithdraw(availableToWithdraw, position.quoteTokenAmount) - } - - const liquidityInLupBucket = getLiquidityInLupBucket(pool) - - if (!buckets[lupBucketIndex + 1]) { - return resolveMaxLiquidityWithdraw( - availableToWithdraw.plus(liquidityInLupBucket), - position.quoteTokenAmount, - ) - } - - return calculateAjnaMaxLiquidityWithdraw({ - availableToWithdraw: availableToWithdraw.plus(liquidityInLupBucket), - pool: { - ...pool, - buckets: [ - ...buckets.filter(bucket => !bucket.index.eq(lupBucket.index)), - { ...lupBucket, quoteTokens: lupBucket.quoteTokens.minus(liquidityInLupBucket) }, - ].sort((a, b) => a.index.minus(b.index).toNumber()), - depositSize: pool.depositSize.minus(liquidityInLupBucket), - lowestUtilizedPriceIndex: buckets[lupBucketIndex + 1].index, - lowestUtilizedPrice: buckets[lupBucketIndex + 1].price, - }, - poolCurrentLiquidity, - position, - simulation, - }) -} - -// it's for simulation purposes only, for current value use t0Np from borrowerInfo -export function getNeutralPrice( - positionDebt: BigNumber, - positionCollateral: BigNumber, - interestRate: BigNumber, -) { - const thresholdPrice = - positionCollateral.isZero() || positionDebt.isZero() - ? ZERO - : positionDebt.div(positionCollateral) - - return thresholdPrice.times(new BigNumber(1.04).plus(interestRate.sqrt().div(2))) -} - -export function getAjnaEarnDepositFee({ - interestRate, - positionPrice, - positionQuoteAmount, - simulationPrice, - simulationQuoteAmount, -}: { - interestRate: BigNumber - positionPrice: BigNumber - positionQuoteAmount: BigNumber - simulationPrice?: BigNumber - simulationQuoteAmount?: BigNumber -}) { - // current annualized rate divided by 365 * 3 (8 hours of interest) - const depositFeeRate = interestRate.div(365 * 3) - - return simulationPrice?.lt(positionPrice) || simulationQuoteAmount?.gt(positionQuoteAmount) - ? simulationQuoteAmount?.times(depositFeeRate) - : undefined -} +export * from './consts' +export * from './utils' diff --git a/packages/dma-library/src/protocols/ajna/utils.ts b/packages/dma-library/src/protocols/ajna/utils.ts new file mode 100644 index 00000000..7adf5303 --- /dev/null +++ b/packages/dma-library/src/protocols/ajna/utils.ts @@ -0,0 +1,538 @@ +import { ONE, ZERO } from '@dma-common/constants' +import { negativeToZero } from '@dma-common/utils/common' +import { + ajnaCollateralizationFactor, + ajnaPaybackAllWithdrawAllValueOffset, +} from '@dma-library/protocols/ajna/consts' +import { ajnaBuckets } from '@dma-library/strategies' +import { getAjnaEarnValidations } from '@dma-library/strategies/ajna/earn/validations' +import { + getLiquidityInLupBucket, + getPoolLiquidity, + getTotalPoolLiquidity, +} from '@dma-library/strategies/ajna/validation/borrowish/notEnoughLiquidity' +import { AjnaPosition, CommonDMADependencies, SwapData } from '@dma-library/types' +import { + AjnaCommonDependencies, + AjnaEarnActions, + AjnaEarnPayload, + AjnaEarnPosition, + AjnaError, + AjnaNotice, + AjnaPool, + AjnaSuccess, + AjnaWarning, + SummerStrategy, +} from '@dma-library/types/ajna' +import BigNumber from 'bignumber.js' +import { ethers } from 'ethers' + +export const prepareAjnaDMAPayload = ({ + dependencies, + targetPosition, + errors, + warnings, + data, + txValue, + swaps, +}: { + dependencies: CommonDMADependencies + targetPosition: T + errors: AjnaError[] + warnings: AjnaWarning[] + notices: AjnaNotice[] + successes: AjnaSuccess[] + data: string + txValue: string + swaps: (SwapData & { collectFeeFrom: 'sourceToken' | 'targetToken'; tokenFee: BigNumber })[] +}): SummerStrategy => { + return { + simulation: { + swaps, + errors, + warnings, + notices: [], + successes: [], + targetPosition, + position: targetPosition, + }, + tx: { + to: dependencies.operationExecutor, + data, + value: txValue, + }, + } +} + +export const prepareAjnaPayload = ({ + dependencies, + targetPosition, + errors, + warnings, + notices, + successes, + data, + txValue, +}: { + dependencies: AjnaCommonDependencies + targetPosition: T + errors: AjnaError[] + warnings: AjnaWarning[] + notices: AjnaNotice[] + successes: AjnaSuccess[] + data: string + txValue: string +}): SummerStrategy => { + return { + simulation: { + swaps: [], + errors, + warnings, + notices, + successes, + targetPosition, + position: targetPosition, + }, + tx: { + to: dependencies.ajnaProxyActions, + data, + value: txValue, + }, + } +} + +export const getAjnaEarnActionOutput = async ({ + targetPosition, + data, + dependencies, + args, + action, + txValue, +}: { + targetPosition: AjnaEarnPosition + data: string + dependencies: AjnaCommonDependencies + args: AjnaEarnPayload + action: AjnaEarnActions + txValue: string +}) => { + const afterLupIndex = ['deposit-earn', 'withdraw-earn'].includes(action) + ? calculateNewLupWhenAdjusting(args.position.pool, args.position, targetPosition).newLupIndex + : undefined + + const { errors, warnings, notices, successes } = getAjnaEarnValidations({ + price: args.price, + quoteAmount: args.quoteAmount, + quoteTokenPrecision: args.quoteTokenPrecision, + position: args.position, + simulation: targetPosition, + afterLupIndex, + action, + }) + + return prepareAjnaPayload({ + dependencies, + targetPosition, + errors, + warnings, + notices, + successes, + data, + txValue, + }) +} + +export const resolveTxValue = (isUsingEth: boolean, amount: BigNumber) => + isUsingEth ? ethers.utils.parseEther(amount.toString()).toString() : '0' + +export const calculateAjnaApyPerDays = (amount: BigNumber, apy: BigNumber, days: number) => + amount + // converted to numbers because BigNumber doesn't handle power with decimals + .times(new BigNumber(Math.E ** apy.times(days).toNumber())) + .minus(amount) + .div(amount) + +// The origination fee is calculated as the greatest of the current annualized +// borrower interest rate divided by 52 (one week of interest) or 5 bps multiplied by the loan’s new +// debt. +export const getAjnaBorrowOriginationFee = ({ + interestRate, + quoteAmount, +}: { + interestRate: BigNumber + quoteAmount: BigNumber +}) => { + const weeklyInterestRate = interestRate.div(52) + const fiveBasisPoints = new BigNumber(0.0005) + + return BigNumber.max(weeklyInterestRate, fiveBasisPoints).times(quoteAmount) +} + +function getSimulationPoolOutput( + positionCollateral: BigNumber, + positionDebt: BigNumber, + debtChange: BigNumber, + pool: AjnaPool, + newLup: BigNumber, + newLupIndex: BigNumber, +) { + const thresholdPrice = !positionCollateral.eq(0) + ? positionDebt.dividedBy(positionCollateral) + : ZERO + + const newHtp = thresholdPrice.gt(pool.htp) ? thresholdPrice : pool.htp + + return { + ...pool, + lup: newLup, + lowestUtilizedPrice: newLup, + lowestUtilizedPriceIndex: newLupIndex, + htp: newHtp, + highestThresholdPrice: newHtp, + // TODO this is old index, we need to map newHtp to index + highestThresholdPriceIndex: pool.highestThresholdPriceIndex, + + debt: pool.debt.plus(debtChange), + } +} + +function getMaxGenerateLup( + pool: AjnaPool, + positionDebt: BigNumber, + positionCollateral: BigNumber, + maxDebt: BigNumber = ZERO, +): { lup: BigNumber } { + const initialMaxDebt = positionCollateral.times(pool.lowestUtilizedPrice).minus(positionDebt) + + const liquidityAvailableInLupBucket = getLiquidityInLupBucket(pool) + + if (initialMaxDebt.lte(liquidityAvailableInLupBucket)) { + return { + lup: pool.lowestUtilizedPrice, + } + } + + const sortedBuckets = [...pool.buckets].sort((a, b) => a.index.minus(b.index).toNumber()) + + const lupBucketArrayIndex = sortedBuckets.findIndex(bucket => + bucket.index.isEqualTo(pool.lowestUtilizedPriceIndex), + ) + + const bucketBelowLup = sortedBuckets[lupBucketArrayIndex + 1] + + if (!bucketBelowLup) { + return { + lup: pool.lowestUtilizedPrice, + } + } + + const newPool = getSimulationPoolOutput( + positionCollateral, + positionDebt, + liquidityAvailableInLupBucket, + pool, + bucketBelowLup.price, + bucketBelowLup.index, + ) + + return getMaxGenerateLup( + newPool, + positionDebt.plus(liquidityAvailableInLupBucket), + positionCollateral, + liquidityAvailableInLupBucket.plus(maxDebt), + ) +} + +export function calculateMaxGenerate( + pool: AjnaPool, + positionDebt: BigNumber, + collateralAmount: BigNumber, +) { + const { lup } = getMaxGenerateLup(pool, positionDebt, collateralAmount) + + const maxDebt = collateralAmount.times(lup).div(ajnaCollateralizationFactor).minus(positionDebt) + + // This fee calculated here acts like an offset from calculated maxDebt value. It's important + // because this fee is added to user debt, and we need to take it into account when calculating max + // generate + const originationFee = getAjnaBorrowOriginationFee({ + interestRate: pool.interestRate, + quoteAmount: maxDebt, + }) + + const poolLiquidity = getPoolLiquidity({ + buckets: pool.buckets, + debt: pool.debt, + }) + + if (poolLiquidity.lte(maxDebt)) { + const fee = getAjnaBorrowOriginationFee({ + interestRate: pool.interestRate, + quoteAmount: poolLiquidity, + }) + + return negativeToZero( + poolLiquidity.minus(fee).times(ONE.minus(ajnaPaybackAllWithdrawAllValueOffset)), + ) + } + + return negativeToZero(maxDebt.minus(originationFee)).times( + ONE.minus(ajnaPaybackAllWithdrawAllValueOffset), + ) +} + +export function calculateNewLup(pool: AjnaPool, debtChange: BigNumber): [BigNumber, BigNumber] { + if (pool.buckets.length === 0) { + return [pool.lowestUtilizedPrice, pool.lowestUtilizedPriceIndex] + } + + const sortedBuckets = [...pool.buckets].sort((a, b) => a.index.minus(b.index).toNumber()) + const totalPoolLiquidity = getTotalPoolLiquidity(pool.buckets) + + let remainingDebt = pool.debt.plus(debtChange) + let newLup = sortedBuckets[0] ? sortedBuckets[0].price : pool.lowestUtilizedPrice + let newLupIndex = sortedBuckets[0] ? sortedBuckets[0].index : pool.lowestUtilizedPriceIndex + + if (remainingDebt.gt(totalPoolLiquidity)) { + newLup = sortedBuckets[sortedBuckets.length - 1].price + newLupIndex = sortedBuckets[sortedBuckets.length - 1].index + remainingDebt = ZERO + + return [newLup, newLupIndex] + } + + sortedBuckets.forEach(bucket => { + if (remainingDebt.gt(bucket.quoteTokens)) { + remainingDebt = remainingDebt.minus(bucket.quoteTokens) + } else { + if (remainingDebt.gt(0)) { + newLup = bucket.price + newLupIndex = bucket.index + remainingDebt = ZERO + } + } + }) + return [newLup, newLupIndex] +} + +export function calculateNewLupWhenAdjusting( + pool: AjnaPool, + position: AjnaEarnPosition, + simulation?: AjnaEarnPosition, +) { + if (!simulation) { + return { + newLupPrice: pool.lowestUtilizedPrice, + newLupIndex: pool.lowestUtilizedPriceIndex, + } + } + + let newLupPrice = ZERO + let newLupIndex = ZERO + + const oldBucket = pool.buckets.find(bucket => bucket.price.eq(position.price)) + + if (!oldBucket) { + return { + newLupPrice, + newLupIndex, + } + } + + const poolBuckets = [...pool.buckets].filter(bucket => !bucket.index.eq(oldBucket.index)) + + poolBuckets.push({ + ...oldBucket, + quoteTokens: oldBucket.quoteTokens.minus(position.quoteTokenAmount), + }) + + const newBucketIndex = new BigNumber( + ajnaBuckets.findIndex(bucket => new BigNumber(bucket).eq(simulation.price.shiftedBy(18))), + ) + + const existingBucketArrayIndex = poolBuckets.findIndex(bucket => bucket.index.eq(newBucketIndex)) + + if (existingBucketArrayIndex !== -1) { + poolBuckets[existingBucketArrayIndex].quoteTokens = poolBuckets[ + existingBucketArrayIndex + ].quoteTokens.plus(simulation.quoteTokenAmount) + } else { + poolBuckets.push({ + price: simulation.price, + index: newBucketIndex, + quoteTokens: simulation.quoteTokenAmount, + bucketLPs: ZERO, + collateral: ZERO, + }) + } + + const sortedBuckets = [...poolBuckets].sort((a, b) => a.index.minus(b.index).toNumber()) + + let remainingDebt = pool.debt + + for (let i = 0; i < sortedBuckets.length; i++) { + const bucket = sortedBuckets[i] + + if (remainingDebt.gt(bucket.quoteTokens)) { + remainingDebt = remainingDebt.minus(bucket.quoteTokens) + } else { + newLupPrice = bucket.price + newLupIndex = bucket.index + break + } + } + + return { + newLupPrice, + newLupIndex, + } +} + +export function simulatePool( + pool: AjnaPool, + debtChange: BigNumber, + positionDebt: BigNumber, + positionCollateral: BigNumber, +): AjnaPool { + const [newLup, newLupIndex] = calculateNewLup(pool, debtChange) + + return getSimulationPoolOutput( + positionCollateral, + positionDebt, + debtChange, + pool, + newLup, + newLupIndex, + ) +} + +const resolveMaxLiquidityWithdraw = (availableToWithdraw: BigNumber, quoteTokenAmount: BigNumber) => + negativeToZero(availableToWithdraw.gte(quoteTokenAmount) ? quoteTokenAmount : availableToWithdraw) + +export const calculateAjnaMaxLiquidityWithdraw = ({ + availableToWithdraw = ZERO, + pool, + position, + poolCurrentLiquidity, + simulation, +}: { + availableToWithdraw?: BigNumber + pool: AjnaPool + poolCurrentLiquidity: BigNumber + position: AjnaEarnPosition + simulation?: AjnaEarnPosition +}) => { + if (availableToWithdraw.gt(poolCurrentLiquidity)) { + return position.quoteTokenAmount.gt(poolCurrentLiquidity) + ? poolCurrentLiquidity + : position.quoteTokenAmount + } + + if ( + availableToWithdraw.gte(position.quoteTokenAmount) || + pool.lowestUtilizedPriceIndex.isZero() || + position.priceIndex?.gt(pool.lowestUtilizedPriceIndex) + ) { + return position.quoteTokenAmount + } + + const { newLupIndex } = calculateNewLupWhenAdjusting(pool, position, simulation) + + if (newLupIndex.gt(pool.highestThresholdPriceIndex)) { + return resolveMaxLiquidityWithdraw(availableToWithdraw, position.quoteTokenAmount) + } + + const buckets = pool.buckets.filter(bucket => bucket.index.lte(pool.highestThresholdPriceIndex)) + + const lupBucket = buckets.find(bucket => bucket.index.eq(pool.lowestUtilizedPriceIndex)) + const lupBucketIndex = buckets.findIndex(bucket => bucket.index.eq(pool.lowestUtilizedPriceIndex)) + + if (!lupBucket) { + return resolveMaxLiquidityWithdraw(availableToWithdraw, position.quoteTokenAmount) + } + + const liquidityInLupBucket = getLiquidityInLupBucket(pool) + + if (!buckets[lupBucketIndex + 1]) { + return resolveMaxLiquidityWithdraw( + availableToWithdraw.plus(liquidityInLupBucket), + position.quoteTokenAmount, + ) + } + + return calculateAjnaMaxLiquidityWithdraw({ + availableToWithdraw: availableToWithdraw.plus(liquidityInLupBucket), + pool: { + ...pool, + buckets: [ + ...buckets.filter(bucket => !bucket.index.eq(lupBucket.index)), + { ...lupBucket, quoteTokens: lupBucket.quoteTokens.minus(liquidityInLupBucket) }, + ].sort((a, b) => a.index.minus(b.index).toNumber()), + depositSize: pool.depositSize.minus(liquidityInLupBucket), + lowestUtilizedPriceIndex: buckets[lupBucketIndex + 1].index, + lowestUtilizedPrice: buckets[lupBucketIndex + 1].price, + }, + poolCurrentLiquidity, + position, + simulation, + }) +} + +// it's for simulation purposes only, for current value use t0Np from borrowerInfo +export function getNeutralPrice( + positionDebt: BigNumber, + positionCollateral: BigNumber, + interestRate: BigNumber, + t0NeutralPrice: BigNumber, + thresholdPrice: BigNumber, + generatedDebt: boolean, + withdrawnCollateral: boolean, +) { + if (positionCollateral.isZero()) { + return ZERO + } + + const oldNpTpRatio = t0NeutralPrice.div(thresholdPrice.times(ajnaCollateralizationFactor)) + const oldInterestRate = oldNpTpRatio.times(2).minus(2).pow(2) + + const npToTpRatio = ONE.plus(interestRate.sqrt().div(2)) + + const shouldRestamp = interestRate.lt(oldInterestRate) + + const resolvedNpToTpRatio = + generatedDebt || withdrawnCollateral || shouldRestamp ? npToTpRatio : oldNpTpRatio + + return positionDebt + .times(ajnaCollateralizationFactor) + .div(positionCollateral) + .times(resolvedNpToTpRatio) +} + +export function getAjnaEarnDepositFee({ + interestRate, + positionPrice, + positionQuoteAmount, + simulationPrice, + simulationQuoteAmount, +}: { + interestRate: BigNumber + positionPrice: BigNumber + positionQuoteAmount: BigNumber + simulationPrice?: BigNumber + simulationQuoteAmount?: BigNumber +}) { + // current annualized rate divided by 365 * 3 (8 hours of interest) + const depositFeeRate = interestRate.div(365 * 3) + + return simulationPrice?.lt(positionPrice) || simulationQuoteAmount?.gt(positionQuoteAmount) + ? simulationQuoteAmount?.times(depositFeeRate) + : undefined +} + +export function shouldDisplayAjnaDustLimitValidation(position: AjnaPosition) { + return ( + position.pool.loansCount.gt(10) && + position.debtAmount.lt(position.pool.poolMinDebtAmount) && + position.debtAmount.gt(0) + ) +} diff --git a/packages/dma-library/src/strategies/aave-like/multiply/close/close.ts b/packages/dma-library/src/strategies/aave-like/multiply/close/close.ts index 35a8af41..14f185bf 100644 --- a/packages/dma-library/src/strategies/aave-like/multiply/close/close.ts +++ b/packages/dma-library/src/strategies/aave-like/multiply/close/close.ts @@ -112,7 +112,6 @@ async function getAaveSwapDataToCloseToCollateral( outstandingDebt: dependencies.currentPosition.debt.amount, slippage, // Needs to be WETH for isETH comparison - ETHAddress: addresses.tokens.WETH, getSwapData: dependencies.getSwapData, }) } diff --git a/packages/dma-library/src/strategies/ajna/borrow/deposit-borrow.ts b/packages/dma-library/src/strategies/ajna/borrow/deposit-borrow.ts index a7bdfdd2..a5fddcd8 100644 --- a/packages/dma-library/src/strategies/ajna/borrow/deposit-borrow.ts +++ b/packages/dma-library/src/strategies/ajna/borrow/deposit-borrow.ts @@ -1,27 +1,27 @@ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions.json' -import { prepareAjnaPayload, resolveAjnaEthAction } from '@dma-library/protocols/ajna' +import { prepareAjnaPayload, resolveTxValue } from '@dma-library/protocols/ajna' import { ajnaBuckets } from '@dma-library/strategies' import { validateLiquidationPriceCloseToMarketPrice } from '@dma-library/strategies/ajna/validation/borrowish/liquidationPriceCloseToMarket' import { AjnaBorrowPayload, AjnaCommonDependencies, AjnaPosition, - AjnaStrategy, + SummerStrategy, } from '@dma-library/types/ajna' import BigNumber from 'bignumber.js' import { ethers } from 'ethers' +import { validateGenerateCloseToMaxLtv } from '../../validation/closeToMaxLtv' import { validateBorrowUndercollateralized, validateDustLimit, validateLiquidity, } from '../validation' -import { validateGenerateCloseToMaxLtv } from '../validation/borrowish/closeToMaxLtv' export type AjnaDepositBorrowStrategy = ( args: AjnaBorrowPayload, dependencies: AjnaCommonDependencies, -) => Promise> +) => Promise> export const depositBorrow: AjnaDepositBorrowStrategy = async (args, dependencies) => { const isDepositingEth = @@ -66,6 +66,6 @@ export const depositBorrow: AjnaDepositBorrowStrategy = async (args, dependencie notices: [], successes: [], data, - txValue: resolveAjnaEthAction(isDepositingEth, args.collateralAmount), + txValue: resolveTxValue(isDepositingEth, args.collateralAmount), }) } diff --git a/packages/dma-library/src/strategies/ajna/borrow/open.ts b/packages/dma-library/src/strategies/ajna/borrow/open.ts index 4fd493ae..fcf96264 100644 --- a/packages/dma-library/src/strategies/ajna/borrow/open.ts +++ b/packages/dma-library/src/strategies/ajna/borrow/open.ts @@ -1,9 +1,9 @@ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions.json' -import { prepareAjnaPayload, resolveAjnaEthAction } from '@dma-library/protocols/ajna' +import { prepareAjnaPayload, resolveTxValue } from '@dma-library/protocols/ajna' import { ajnaBuckets } from '@dma-library/strategies' -import { validateGenerateCloseToMaxLtv } from '@dma-library/strategies/ajna/validation/borrowish/closeToMaxLtv' import { validateLiquidationPriceCloseToMarketPrice } from '@dma-library/strategies/ajna/validation/borrowish/liquidationPriceCloseToMarket' -import { AjnaCommonDependencies, AjnaPosition, AjnaStrategy } from '@dma-library/types/ajna' +import { validateGenerateCloseToMaxLtv } from '@dma-library/strategies/validation/closeToMaxLtv' +import { AjnaCommonDependencies, AjnaPosition, SummerStrategy } from '@dma-library/types/ajna' import { AjnaOpenBorrowPayload } from '@dma-library/types/ajna/ajna-dependencies' import { views } from '@dma-library/views' import BigNumber from 'bignumber.js' @@ -18,7 +18,7 @@ import { export type AjnaOpenBorrowStrategy = ( args: AjnaOpenBorrowPayload, dependencies: AjnaCommonDependencies, -) => Promise> +) => Promise> export const open: AjnaOpenBorrowStrategy = async (args, dependencies) => { const getPosition = views.ajna.getPosition @@ -28,6 +28,8 @@ export const open: AjnaOpenBorrowStrategy = async (args, dependencies) => { quotePrice: args.quotePrice, proxyAddress: args.dpmProxyAddress, poolAddress: args.poolAddress, + collateralToken: args.collateralToken, + quoteToken: args.quoteToken, }, { poolInfoAddress: dependencies.poolInfoAddress, @@ -82,6 +84,6 @@ export const open: AjnaOpenBorrowStrategy = async (args, dependencies) => { notices: [], successes: [], warnings, - txValue: resolveAjnaEthAction(isDepositingEth, args.collateralAmount), + txValue: resolveTxValue(isDepositingEth, args.collateralAmount), }) } diff --git a/packages/dma-library/src/strategies/ajna/borrow/payback-withdraw.ts b/packages/dma-library/src/strategies/ajna/borrow/payback-withdraw.ts index 0dab7acf..d1ddedfe 100644 --- a/packages/dma-library/src/strategies/ajna/borrow/payback-withdraw.ts +++ b/packages/dma-library/src/strategies/ajna/borrow/payback-withdraw.ts @@ -1,25 +1,25 @@ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions.json' -import { prepareAjnaPayload, resolveAjnaEthAction } from '@dma-library/protocols/ajna' +import { prepareAjnaPayload, resolveTxValue } from '@dma-library/protocols/ajna' import { validateLiquidationPriceCloseToMarketPrice } from '@dma-library/strategies/ajna/validation/borrowish/liquidationPriceCloseToMarket' import { AjnaBorrowPayload, AjnaCommonDependencies, AjnaPosition, - AjnaStrategy, + SummerStrategy, } from '@dma-library/types/ajna' import { ethers } from 'ethers' +import { validateWithdrawCloseToMaxLtv } from '../../validation/closeToMaxLtv' import { validateDustLimit, validateOverWithdraw, validateWithdrawUndercollateralized, } from '../validation' -import { validateWithdrawCloseToMaxLtv } from '../validation/borrowish/closeToMaxLtv' export type AjnaPaybackWithdrawStrategy = ( args: AjnaBorrowPayload, dependencies: AjnaCommonDependencies, -) => Promise> +) => Promise> export const paybackWithdraw: AjnaPaybackWithdrawStrategy = async (args, dependencies) => { const apa = new ethers.Contract( @@ -61,6 +61,6 @@ export const paybackWithdraw: AjnaPaybackWithdrawStrategy = async (args, depende notices: [], successes: [], data, - txValue: resolveAjnaEthAction(isPayingBackEth, args.quoteAmount), + txValue: resolveTxValue(isPayingBackEth, args.quoteAmount), }) } diff --git a/packages/dma-library/src/strategies/ajna/earn/claim-collateral.ts b/packages/dma-library/src/strategies/ajna/earn/claim-collateral.ts index e69f5993..1a65a6c3 100644 --- a/packages/dma-library/src/strategies/ajna/earn/claim-collateral.ts +++ b/packages/dma-library/src/strategies/ajna/earn/claim-collateral.ts @@ -1,14 +1,14 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions.json' import { getAjnaEarnActionOutput } from '@dma-library/protocols/ajna' -import { AjnaCommonDependencies, AjnaEarnPosition, AjnaStrategy } from '@dma-library/types/ajna' +import { AjnaCommonDependencies, AjnaEarnPosition, SummerStrategy } from '@dma-library/types/ajna' import { AjnaEarnPayload } from '@dma-library/types/ajna/ajna-dependencies' import { ethers } from 'ethers' export type AjnaClaimCollateralStrategy = ( args: AjnaEarnPayload, dependencies: AjnaCommonDependencies, -) => Promise> +) => Promise> export const claimCollateral: AjnaClaimCollateralStrategy = async (args, dependencies) => { const action = 'claim-earn' diff --git a/packages/dma-library/src/strategies/ajna/earn/deposit-adjust.ts b/packages/dma-library/src/strategies/ajna/earn/deposit-adjust.ts index a67260e3..feaae240 100644 --- a/packages/dma-library/src/strategies/ajna/earn/deposit-adjust.ts +++ b/packages/dma-library/src/strategies/ajna/earn/deposit-adjust.ts @@ -2,8 +2,8 @@ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions.json' import poolInfoAbi from '@abis/external/protocols/ajna/poolInfoUtils.json' import { ZERO } from '@dma-common/constants' -import { getAjnaEarnActionOutput, resolveAjnaEthAction } from '@dma-library/protocols/ajna' -import { AjnaCommonDependencies, AjnaEarnPosition, AjnaStrategy } from '@dma-library/types/ajna' +import { getAjnaEarnActionOutput, resolveTxValue } from '@dma-library/protocols/ajna' +import { AjnaCommonDependencies, AjnaEarnPosition, SummerStrategy } from '@dma-library/types/ajna' import { AjnaEarnPayload } from '@dma-library/types/ajna/ajna-dependencies' import BigNumber from 'bignumber.js' import { ethers } from 'ethers' @@ -13,7 +13,7 @@ import bucketPrices from './buckets.json' export type AjnaDepositAndAdjustStrategy = ( args: AjnaEarnPayload, dependencies: AjnaCommonDependencies, -) => Promise> +) => Promise> export const depositAndAdjust: AjnaDepositAndAdjustStrategy = async (args, dependencies) => { const action = 'deposit-earn' @@ -21,6 +21,7 @@ export const depositAndAdjust: AjnaDepositAndAdjustStrategy = async (args, depen args.position.pool.quoteToken.toLowerCase() === dependencies.WETH.toLowerCase() const isDepositing = args.quoteAmount.gt(ZERO) const isAdjusting = !args.price.eq(args.position.price) && args.position.price.gt(ZERO) + const isPositionEmpty = args.position.quoteTokenAmount.isZero() const ajnaProxyActions = new ethers.Contract( dependencies.ajnaProxyActions, @@ -74,7 +75,10 @@ export const depositAndAdjust: AjnaDepositAndAdjustStrategy = async (args, depen ethers.utils.parseUnits(args.quoteAmount.toString(), args.quoteTokenPrecision).toString(), args.price.shiftedBy(18).toString(), ]) - targetPosition = args.position.deposit(args.quoteAmount) + + targetPosition = isPositionEmpty + ? args.position.moveQuote(priceToIndex).deposit(args.quoteAmount) + : args.position.deposit(args.quoteAmount) } if (!data || !targetPosition) throw new Error('Invalid depositAndAdjust params') @@ -84,7 +88,7 @@ export const depositAndAdjust: AjnaDepositAndAdjustStrategy = async (args, depen data, dependencies, args, - txValue: resolveAjnaEthAction(isLendingEth, args.quoteAmount), + txValue: resolveTxValue(isLendingEth, args.quoteAmount), action, }) } diff --git a/packages/dma-library/src/strategies/ajna/earn/open.ts b/packages/dma-library/src/strategies/ajna/earn/open.ts index c58037a8..1fc76ee8 100644 --- a/packages/dma-library/src/strategies/ajna/earn/open.ts +++ b/packages/dma-library/src/strategies/ajna/earn/open.ts @@ -1,12 +1,12 @@ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions.json' import poolInfoAbi from '@abis/external/protocols/ajna/poolInfoUtils.json' import { ZERO } from '@dma-common/constants' -import { getAjnaEarnActionOutput, resolveAjnaEthAction } from '@dma-library/protocols/ajna' +import { getAjnaEarnActionOutput, resolveTxValue } from '@dma-library/protocols/ajna' import { AjnaEarnPosition, AjnaOpenEarnDependencies, AjnaOpenEarnPayload, - AjnaStrategy, + SummerStrategy, } from '@dma-library/types/ajna' import { views } from '@dma-library/views' import BigNumber from 'bignumber.js' @@ -15,7 +15,7 @@ import { ethers } from 'ethers' export type AjnaOpenEarnStrategy = ( args: AjnaOpenEarnPayload, dependencies: AjnaOpenEarnDependencies, -) => Promise> +) => Promise> export const open: AjnaOpenEarnStrategy = async (args, dependencies) => { const action = 'open-earn' @@ -25,6 +25,8 @@ export const open: AjnaOpenEarnStrategy = async (args, dependencies) => { quotePrice: args.quotePrice, proxyAddress: args.dpmProxyAddress, poolAddress: args.poolAddress, + collateralToken: args.collateralToken, + quoteToken: args.quoteToken, }, { getEarnData: dependencies.getEarnData, @@ -70,6 +72,7 @@ export const open: AjnaOpenEarnStrategy = async (args, dependencies) => { position.pnl, position.totalEarnings, false, + position.historicalApy, ) return getAjnaEarnActionOutput({ @@ -81,7 +84,7 @@ export const open: AjnaOpenEarnStrategy = async (args, dependencies) => { collateralAmount: ZERO, ...args, }, - txValue: resolveAjnaEthAction(isLendingEth, args.quoteAmount), + txValue: resolveTxValue(isLendingEth, args.quoteAmount), action, }) } diff --git a/packages/dma-library/src/strategies/ajna/earn/validations.ts b/packages/dma-library/src/strategies/ajna/earn/validations.ts index 04e91140..aa40a1af 100644 --- a/packages/dma-library/src/strategies/ajna/earn/validations.ts +++ b/packages/dma-library/src/strategies/ajna/earn/validations.ts @@ -4,6 +4,7 @@ import { validatePriceBelowHtp, validatePriceBetweenHtpAndLup, validateWithdrawMoreThanAvailable, + validateWithdrawNotAvailable, } from '@dma-library/strategies/ajna/validation' import { AjnaEarnActions, @@ -20,6 +21,7 @@ export const getAjnaEarnValidations = ({ quoteAmount, quoteTokenPrecision, position, + simulation, action, afterLupIndex, }: { @@ -50,13 +52,19 @@ export const getAjnaEarnValidations = ({ break } case 'deposit-earn': { - errors.push(...validateLupBelowHtp(position, action, afterLupIndex)) + errors.push(...validateLupBelowHtp(position, simulation, action, afterLupIndex)) break } case 'withdraw-earn': { errors.push( - ...validateLupBelowHtp(position, action, afterLupIndex), - ...validateWithdrawMoreThanAvailable(position, quoteAmount, quoteTokenPrecision), + ...validateLupBelowHtp(position, simulation, action, afterLupIndex), + ...validateWithdrawMoreThanAvailable( + position, + simulation, + quoteAmount, + quoteTokenPrecision, + ), + ...validateWithdrawNotAvailable(position, simulation, quoteTokenPrecision), ) break } diff --git a/packages/dma-library/src/strategies/ajna/earn/withdraw-adjust.ts b/packages/dma-library/src/strategies/ajna/earn/withdraw-adjust.ts index 610813f3..5315c368 100644 --- a/packages/dma-library/src/strategies/ajna/earn/withdraw-adjust.ts +++ b/packages/dma-library/src/strategies/ajna/earn/withdraw-adjust.ts @@ -3,7 +3,7 @@ import ajnaProxyActionsAbi from '@abis/external/protocols/ajna/ajnaProxyActions. import poolInfoAbi from '@abis/external/protocols/ajna/poolInfoUtils.json' import { ZERO } from '@dma-common/constants' import { getAjnaEarnActionOutput } from '@dma-library/protocols/ajna' -import { AjnaCommonDependencies, AjnaEarnPosition, AjnaStrategy } from '@dma-library/types/ajna' +import { AjnaCommonDependencies, AjnaEarnPosition, SummerStrategy } from '@dma-library/types/ajna' import { AjnaEarnPayload } from '@dma-library/types/ajna/ajna-dependencies' import BigNumber from 'bignumber.js' import { ethers } from 'ethers' @@ -13,7 +13,7 @@ import bucketPrices from './buckets.json' export type AjnaWithdrawAndAdjustStrategy = ( args: AjnaEarnPayload, dependencies: AjnaCommonDependencies, -) => Promise> +) => Promise> export const withdrawAndAdjust: AjnaWithdrawAndAdjustStrategy = async (args, dependencies) => { const action = 'withdraw-earn' diff --git a/packages/dma-library/src/strategies/ajna/multiply/adjust.ts b/packages/dma-library/src/strategies/ajna/multiply/adjust.ts index 5be36fdd..bec613e7 100644 --- a/packages/dma-library/src/strategies/ajna/multiply/adjust.ts +++ b/packages/dma-library/src/strategies/ajna/multiply/adjust.ts @@ -11,9 +11,9 @@ import { import { AjnaMultiplyPayload, AjnaPosition, - AjnaStrategy, FlashloanProvider, PositionType, + SummerStrategy, SwapData, } from '@dma-library/types' import { AjnaCommonDMADependencies } from '@dma-library/types/ajna' @@ -25,7 +25,7 @@ import BigNumber from 'bignumber.js' export type AjnaAdjustRiskStrategy = ( args: AjnaMultiplyPayload, dependencies: AjnaCommonDMADependencies, -) => Promise> +) => Promise> const positionType: PositionType = 'Multiply' @@ -95,8 +95,6 @@ const adjustRiskDown: AjnaAdjustRiskStrategy = async (args, dependencies) => { riskIsIncreasing, oraclePrice, positionType, - // TODO: remove this - ZERO, ) // Get swap data @@ -106,8 +104,6 @@ const adjustRiskDown: AjnaAdjustRiskStrategy = async (args, dependencies) => { simulatedAdjustment, riskIsIncreasing, positionType, - // TODO: remove this - ZERO, ) // Build operation @@ -147,12 +143,10 @@ async function buildOperation( const fromTokenSymbol = riskIsIncreasing ? args.quoteTokenSymbol : args.collateralTokenSymbol const toTokenSymbol = riskIsIncreasing ? args.collateralTokenSymbol : args.quoteTokenSymbol - // TODO: remove this - const fee = ZERO - // const fee = SwapUtils.feeResolver(fromTokenSymbol, toTokenSymbol, { - // isIncreasingRisk: riskIsIncreasing, - // isEarnPosition: SwapUtils.isCorrelatedPosition(fromTokenSymbol, toTokenSymbol), - // }) + const fee = SwapUtils.feeResolver(fromTokenSymbol, toTokenSymbol, { + isIncreasingRisk: riskIsIncreasing, + isEarnPosition: SwapUtils.isCorrelatedPosition(fromTokenSymbol, toTokenSymbol), + }) // When adjusting risk up we need to flashloan the swap amount before deducting fees // Assuming an ETH/USDC position, we'd be Flashloaning USDC to swap for ETH // Once the received ETH is deposited as collateral we can then increase our debt diff --git a/packages/dma-library/src/strategies/ajna/multiply/close.ts b/packages/dma-library/src/strategies/ajna/multiply/close.ts index f294d462..385a6f01 100644 --- a/packages/dma-library/src/strategies/ajna/multiply/close.ts +++ b/packages/dma-library/src/strategies/ajna/multiply/close.ts @@ -4,15 +4,15 @@ import { amountToWei } from '@dma-common/utils/common' import { calculateFee } from '@dma-common/utils/swap' import { areSymbolsEqual } from '@dma-common/utils/symbols' import { operations } from '@dma-library/operations' -import { prepareAjnaDMAPayload, resolveAjnaEthAction } from '@dma-library/protocols/ajna' +import { prepareAjnaDMAPayload, resolveTxValue } from '@dma-library/protocols/ajna' import { ajnaBuckets } from '@dma-library/strategies' import * as StrategiesCommon from '@dma-library/strategies/common' import { AjnaPosition, - AjnaStrategy, FlashloanProvider, IOperation, PositionType, + SummerStrategy, SwapData, } from '@dma-library/types' import { @@ -20,6 +20,7 @@ import { AjnaCommonDMADependencies, } from '@dma-library/types/ajna/ajna-dependencies' import { encodeOperation } from '@dma-library/utils/operation' +import * as SwapUtils from '@dma-library/utils/swap' import * as Domain from '@domain' import { FLASHLOAN_SAFETY_MARGIN } from '@domain/constants' import BigNumber from 'bignumber.js' @@ -27,7 +28,7 @@ import BigNumber from 'bignumber.js' export type AjnaCloseStrategy = ( args: AjnaCloseMultiplyPayload, dependencies: AjnaCommonDMADependencies, -) => Promise> +) => Promise> const positionType: PositionType = 'Multiply' @@ -56,15 +57,13 @@ export const closeMultiply: AjnaCloseStrategy = async (args, dependencies) => { const targetPosition = args.position.close() - // TODO: remove this - const fee = ZERO - // const fee = SwapUtils.feeResolver(args.collateralTokenSymbol, args.quoteTokenSymbol, { - // isEarnPosition: SwapUtils.isCorrelatedPosition( - // args.collateralTokenSymbol, - // args.quoteTokenSymbol, - // ), - // isIncreasingRisk: false, - // }) + const fee = SwapUtils.feeResolver(args.collateralTokenSymbol, args.quoteTokenSymbol, { + isEarnPosition: SwapUtils.isCorrelatedPosition( + args.collateralTokenSymbol, + args.quoteTokenSymbol, + ), + isIncreasingRisk: false, + }) const postSwapFee = collectFeeFrom === 'targetToken' ? calculateFee(swapData.toTokenAmount, fee.toNumber()) : ZERO @@ -92,7 +91,7 @@ export const closeMultiply: AjnaCloseStrategy = async (args, dependencies) => { successes: [], notices: [], // TODO instead of zero we will need data from swap - txValue: resolveAjnaEthAction(isDepositingEth, ZERO), + txValue: resolveTxValue(isDepositingEth, ZERO), }) } @@ -123,8 +122,6 @@ async function getAjnaSwapDataToCloseToDebt( slippage: args.slippage, swapAmountBeforeFees: swapAmountBeforeFees, getSwapData: dependencies.getSwapData, - // TODO: remove this - __feeOverride: ZERO, }) } @@ -158,10 +155,7 @@ async function getAjnaSwapDataToCloseToCollateral( debtPrice, slippage: args.slippage, outstandingDebt, - ETHAddress: dependencies.WETH, getSwapData: dependencies.getSwapData, - // TODO: remove this - __feeOverride: ZERO, }) } @@ -194,15 +188,14 @@ async function buildOperation( address: position.pool.quoteToken, } - // TODO: remove this - const fee = ZERO - // const fee = SwapUtils.feeResolver(args.collateralTokenSymbol, args.quoteTokenSymbol, { - // isEarnPosition: SwapUtils.isCorrelatedPosition( - // args.collateralTokenSymbol, - // args.quoteTokenSymbol, - // ), - // isIncreasingRisk: false, - // }) + const fee = SwapUtils.feeResolver(args.collateralTokenSymbol, args.quoteTokenSymbol, { + isEarnPosition: SwapUtils.isCorrelatedPosition( + args.collateralTokenSymbol, + args.quoteTokenSymbol, + ), + isIncreasingRisk: false, + }) + const collateralAmountToBeSwapped = args.shouldCloseToCollateral ? swapData.fromTokenAmount.plus(preSwapFee) : lockedCollateralAmount @@ -249,6 +242,7 @@ async function buildOperation( pool: args.poolAddress, }, price: new BigNumber(ajnaBuckets[ajnaBuckets.length - 1]), + network: dependencies.network, } if (args.shouldCloseToCollateral) { diff --git a/packages/dma-library/src/strategies/ajna/multiply/common.ts b/packages/dma-library/src/strategies/ajna/multiply/common.ts index c3e97335..f9fa9b6c 100644 --- a/packages/dma-library/src/strategies/ajna/multiply/common.ts +++ b/packages/dma-library/src/strategies/ajna/multiply/common.ts @@ -4,21 +4,22 @@ import { areAddressesEqual } from '@dma-common/utils/addresses/index' import { amountFromWei, amountToWei } from '@dma-common/utils/common' import { calculateFee } from '@dma-common/utils/swap' import { BALANCER_FEE } from '@dma-library/config/flashloan-fees' -import { prepareAjnaDMAPayload, resolveAjnaEthAction } from '@dma-library/protocols/ajna' +import { getNeutralPrice, prepareAjnaDMAPayload, resolveTxValue } from '@dma-library/protocols/ajna' import { validateBorrowUndercollateralized, validateLiquidity, } from '@dma-library/strategies/ajna/validation' -import { validateGenerateCloseToMaxLtv } from '@dma-library/strategies/ajna/validation/borrowish/closeToMaxLtv' import { validateLiquidationPriceCloseToMarketPrice } from '@dma-library/strategies/ajna/validation/borrowish/liquidationPriceCloseToMarket' import { validateDustLimitMultiply } from '@dma-library/strategies/ajna/validation/multiply/dustLimit' +import { validateGenerateCloseToMaxLtv } from '@dma-library/strategies/validation/closeToMaxLtv' import { AjnaMultiplyPayload, - AjnaPosition, IOperation, PositionType, SwapData, -} from '@dma-library/types' + // eslint-disable-next-line import/no-unresolved +} from '../../../types' +import { AjnaPosition } from '../../../types/ajna/ajna-position' import { AjnaCommonDMADependencies } from '@dma-library/types/ajna' import { encodeOperation } from '@dma-library/utils/operation' import * as SwapUtils from '@dma-library/utils/swap' @@ -104,6 +105,7 @@ export async function simulateAdjustment( options: { collectSwapFeeFrom: collectFeeFrom, }, + network: dependencies.network, } // TODO: Refactor AjnaPosition to extend IPositionV2 (eventually) @@ -192,7 +194,17 @@ export function prepareAjnaMultiplyDMAPayload( debtAmount, args.collateralPrice, args.quotePrice, - args.position.t0NeutralPrice, + getNeutralPrice( + debtAmount, + collateralAmount, + args.position.pool.interestRate, + args.position.t0NeutralPrice, + args.position.thresholdPrice, + // this doesn't matter for multiply since we always + // either generate or withdraw, so re-stamp will happen + true, + true, + ), args.position.pnl, ) @@ -235,7 +247,7 @@ export function prepareAjnaMultiplyDMAPayload( warnings, successes: [], notices: [], - txValue: resolveAjnaEthAction(isDepositingEth, txAmount), + txValue: resolveTxValue(isDepositingEth, txAmount), }) } diff --git a/packages/dma-library/src/strategies/ajna/multiply/open.ts b/packages/dma-library/src/strategies/ajna/multiply/open.ts index fc254f7c..6205b13a 100644 --- a/packages/dma-library/src/strategies/ajna/multiply/open.ts +++ b/packages/dma-library/src/strategies/ajna/multiply/open.ts @@ -17,7 +17,7 @@ import { PositionType, SwapData, } from '@dma-library/types' -import { AjnaCommonDMADependencies, AjnaPosition, AjnaStrategy } from '@dma-library/types/ajna' +import { AjnaCommonDMADependencies, AjnaPosition, SummerStrategy } from '@dma-library/types/ajna' import * as SwapUtils from '@dma-library/utils/swap' import { views } from '@dma-library/views' import * as Domain from '@domain' @@ -27,7 +27,7 @@ import BigNumber from 'bignumber.js' export type AjnaOpenMultiplyStrategy = ( args: AjnaOpenMultiplyPayload, dependencies: AjnaCommonDMADependencies, -) => Promise> +) => Promise> const positionType: PositionType = 'Multiply' @@ -84,6 +84,8 @@ async function getPosition(args: AjnaOpenMultiplyPayload, dependencies: AjnaComm quotePrice: args.quotePrice, proxyAddress: args.dpmProxyAddress, poolAddress: args.poolAddress, + collateralToken: args.collateralToken, + quoteToken: args.quoteToken, }, { poolInfoAddress: dependencies.poolInfoAddress, @@ -176,6 +178,7 @@ async function simulateAdjustment( options: { collectSwapFeeFrom: collectFeeFrom, }, + network: dependencies.network, } // TODO: Refactor AjnaPosition to extend IPositionV2 (eventually) diff --git a/packages/dma-library/src/strategies/ajna/validation/earn/lup-below-htp.ts b/packages/dma-library/src/strategies/ajna/validation/earn/lup-below-htp.ts index 3b27ff24..c7eb2c7d 100644 --- a/packages/dma-library/src/strategies/ajna/validation/earn/lup-below-htp.ts +++ b/packages/dma-library/src/strategies/ajna/validation/earn/lup-below-htp.ts @@ -4,13 +4,19 @@ import BigNumber from 'bignumber.js' export const validateLupBelowHtp = ( position: AjnaEarnPosition, + simulation: AjnaEarnPosition, action: AjnaEarnActions, afterLupIndex?: BigNumber, ): AjnaError[] => { if ( - afterLupIndex && - new BigNumber(afterLupIndex.toString()).gt(position.pool.highestThresholdPriceIndex) + action === 'deposit-earn' && + position.pool.lowestUtilizedPriceIndex.gt(position.pool.highestThresholdPriceIndex) && + simulation.price.gte(position.price) ) { + return [] + } + + if (afterLupIndex?.gt(position.pool.highestThresholdPriceIndex)) { return [ { name: diff --git a/packages/dma-library/src/strategies/ajna/validation/earn/price-below-htp.ts b/packages/dma-library/src/strategies/ajna/validation/earn/price-below-htp.ts index 7bfe127c..65e2f0f5 100644 --- a/packages/dma-library/src/strategies/ajna/validation/earn/price-below-htp.ts +++ b/packages/dma-library/src/strategies/ajna/validation/earn/price-below-htp.ts @@ -6,7 +6,10 @@ export const validatePriceBelowHtp = ( position: AjnaEarnPosition, price: BigNumber, ): AjnaNotice[] => { - if (price.lt(position.pool.highestThresholdPrice)) { + if ( + price.lt(position.pool.highestThresholdPrice) && + position.pool.lowestUtilizedPriceIndex.lte(position.pool.highestThresholdPriceIndex) + ) { return [ { name: 'price-below-htp', diff --git a/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-more-than-available.ts b/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-more-than-available.ts index f04ea89e..772dd1b7 100644 --- a/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-more-than-available.ts +++ b/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-more-than-available.ts @@ -1,18 +1,35 @@ -import { formatCryptoBalance } from '@dma-common/utils/common' +import { ZERO } from '@dma-common/constants' +import { formatCryptoBalance, negativeToZero } from '@dma-common/utils/common' +import { protocols } from '@dma-library/protocols' +import { getPoolLiquidity } from '@dma-library/strategies/ajna/validation' import { AjnaEarnPosition, AjnaError } from '@dma-library/types' import BigNumber from 'bignumber.js' export const validateWithdrawMoreThanAvailable = ( position: AjnaEarnPosition, + simulation: AjnaEarnPosition, quoteAmount: BigNumber, quoteTokenPrecision: number, ): AjnaError[] => { - if (position.quoteTokenAmount.decimalPlaces(quoteTokenPrecision).lt(quoteAmount)) { + const availableToWithdraw = negativeToZero( + protocols.ajna + .calculateAjnaMaxLiquidityWithdraw({ + pool: position.pool, + poolCurrentLiquidity: getPoolLiquidity(position.pool), + position, + simulation, + }) + .decimalPlaces(quoteTokenPrecision), + ) + + if (availableToWithdraw.lt(quoteAmount) && availableToWithdraw.gt(ZERO)) { return [ { name: 'withdraw-more-than-available', data: { - amount: formatCryptoBalance(position.quoteTokenAmount), + amount: formatCryptoBalance( + BigNumber.min(position.quoteTokenAmount, availableToWithdraw), + ), }, }, ] diff --git a/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-not-available.ts b/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-not-available.ts new file mode 100644 index 00000000..07e85f4a --- /dev/null +++ b/packages/dma-library/src/strategies/ajna/validation/earn/withdraw-not-available.ts @@ -0,0 +1,31 @@ +import { negativeToZero } from '@dma-common/utils/common' +import { protocols } from '@dma-library/protocols' +import { getPoolLiquidity } from '@dma-library/strategies/ajna/validation' +import { AjnaEarnPosition, AjnaError } from '@dma-library/types' + +export const validateWithdrawNotAvailable = ( + position: AjnaEarnPosition, + simulation: AjnaEarnPosition, + quoteTokenPrecision: number, +): AjnaError[] => { + const availableToWithdraw = negativeToZero( + protocols.ajna + .calculateAjnaMaxLiquidityWithdraw({ + pool: position.pool, + poolCurrentLiquidity: getPoolLiquidity(position.pool), + position, + simulation, + }) + .decimalPlaces(quoteTokenPrecision), + ) + + if (availableToWithdraw.isZero()) { + return [ + { + name: 'withdraw-not-available', + }, + ] + } else { + return [] + } +} diff --git a/packages/dma-library/src/strategies/ajna/validation/index.ts b/packages/dma-library/src/strategies/ajna/validation/index.ts index d0475dc6..5a2319e6 100644 --- a/packages/dma-library/src/strategies/ajna/validation/index.ts +++ b/packages/dma-library/src/strategies/ajna/validation/index.ts @@ -1,7 +1,6 @@ export { validateDustLimit } from './borrowish/dustLimit' export { validateLiquidity } from './borrowish/notEnoughLiquidity' export { getPoolLiquidity } from './borrowish/notEnoughLiquidity' -export { validateOverRepay } from './borrowish/overRepay' export { validateOverWithdraw } from './borrowish/overWithdraw' export { validateBorrowUndercollateralized, @@ -12,3 +11,4 @@ export { validatePriceAboveLup } from './earn/price-above-lup' export { validatePriceBelowHtp } from './earn/price-below-htp' export { validatePriceBetweenHtpAndLup } from './earn/price-between-htp-and-lup' export { validateWithdrawMoreThanAvailable } from './earn/withdraw-more-than-available' +export { validateWithdrawNotAvailable } from './earn/withdraw-not-available' diff --git a/packages/dma-library/src/strategies/ajna/validation/multiply/dustLimit.ts b/packages/dma-library/src/strategies/ajna/validation/multiply/dustLimit.ts index de4fb810..5049efb7 100644 --- a/packages/dma-library/src/strategies/ajna/validation/multiply/dustLimit.ts +++ b/packages/dma-library/src/strategies/ajna/validation/multiply/dustLimit.ts @@ -1,14 +1,11 @@ -import { formatCryptoBalance } from '@dma-common/utils/common/formaters' +import { shouldDisplayAjnaDustLimitValidation } from '@dma-library/protocols/ajna' import { AjnaError, AjnaPosition } from '@dma-library/types/ajna' export function validateDustLimitMultiply(position: AjnaPosition): AjnaError[] { - if (position.debtAmount.lt(position.pool.poolMinDebtAmount) && position.debtAmount.gt(0)) { + if (shouldDisplayAjnaDustLimitValidation(position)) { return [ { name: 'debt-less-then-dust-limit-multiply', - data: { - minDebtAmount: formatCryptoBalance(position.pool.poolMinDebtAmount), - }, }, ] } else { diff --git a/packages/dma-library/src/strategies/morphoblue/common/claim-rewards.ts b/packages/dma-library/src/strategies/morphoblue/common/claim-rewards.ts deleted file mode 100644 index 2a857ad4..00000000 --- a/packages/dma-library/src/strategies/morphoblue/common/claim-rewards.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Address, Tx } from '@dma-common/types' -import { Network } from '@dma-library/index' -import { operations } from '@dma-library/operations' -import { CommonDMADependencies } from '@dma-library/types' -import { encodeOperation } from '@dma-library/utils/operation' -import BigNumber from 'bignumber.js' - -import { ZERO } from '../../../../../dma-common/constants/numbers' - -export interface MorphoClaimRewardsDependencies extends CommonDMADependencies { - network: Network -} - -export interface MorphoCloseClaimRewardsPayload { - urds: Address[] - rewards: Address[] - claimable: BigNumber[] - proofs: string[][] -} - -export type MorphoClaimRewardsStrategy = ( - args: MorphoCloseClaimRewardsPayload, - dependencies: MorphoClaimRewardsDependencies, -) => Promise - -export const claimRewards: MorphoClaimRewardsStrategy = async (args, dependencies) => { - const operation = await operations.morphoblue.common.claimRewards( - { - urds: args.urds, - rewards: args.rewards, - claimable: args.claimable.map(item => item.toString()), - proofs: args.proofs, - }, - dependencies.network, - ) - - return { - to: dependencies.operationExecutor, - data: encodeOperation(operation, dependencies), - value: ZERO.toString(), - } -} diff --git a/packages/dma-library/src/strategies/morphoblue/index.ts b/packages/dma-library/src/strategies/morphoblue/index.ts index c9d0e269..0bf5ced1 100644 --- a/packages/dma-library/src/strategies/morphoblue/index.ts +++ b/packages/dma-library/src/strategies/morphoblue/index.ts @@ -7,7 +7,6 @@ import { MorphoPaybackWithdrawStrategy, paybackWithdraw as morphoPaybackWithdraw, } from './borrow/payback-withdraw' -import { claimRewards, MorphoClaimRewardsStrategy } from './common/claim-rewards' import { adjustMultiply, MorphoAdjustRiskStrategy } from './multiply/adjust' import { closeMultiply, MorphoCloseStrategy } from './multiply/close' import { MorphoOpenMultiplyStrategy, openMultiply } from './multiply/open' @@ -23,9 +22,6 @@ export const morphoblue: { close: MorphoCloseStrategy adjust: MorphoAdjustRiskStrategy } - common: { - claimRewards: MorphoClaimRewardsStrategy - } } = { borrow: { openDepositBorrow: morphoblueOpenDepositBorrow, @@ -37,7 +33,4 @@ export const morphoblue: { adjust: adjustMultiply, close: closeMultiply, }, - common: { - claimRewards: claimRewards, - }, } diff --git a/packages/dma-library/src/types/actions.ts b/packages/dma-library/src/types/actions.ts index c3bf7711..61d526a4 100644 --- a/packages/dma-library/src/types/actions.ts +++ b/packages/dma-library/src/types/actions.ts @@ -22,10 +22,15 @@ export const calldataTypes = { WrapEth: `tuple(uint256 amount)`, UnwrapEth: `tuple(uint256 amount)`, ReturnFunds: `tuple(address asset)`, + ReturnMultipleTokens: `tuple(address[] assets)`, CollectFee: `tuple(address asset)`, PullToken: `tuple(address asset, address from, uint256 amount)`, + PullTokenMaxAmount: `tuple(address asset, address from, uint256 amount)`, PositionCreated: `tuple(string protocol, string positionType, address collateralToken, address debtToken)`, TakeAFlashLoan: `tuple(uint256 amount, address asset, bool isDPMProxy, uint8 provider, (bytes32 targetHash, bytes callData)[] calls)`, + TokenBalance: `tuple(address asset, address owner)`, + Erc4626Deposit: `tuple(address vault, uint256 amount)`, + Erc4626Withdraw: `tuple(address vault, uint256 amount)`, }, maker: { Open: `tuple(address joinAddress)`, diff --git a/packages/dma-library/src/views/aave/types.ts b/packages/dma-library/src/views/aave/types.ts index cd90a52f..9c1136f0 100644 --- a/packages/dma-library/src/views/aave/types.ts +++ b/packages/dma-library/src/views/aave/types.ts @@ -2,13 +2,90 @@ import { AaveLikeStrategyAddresses } from '@dma-library/operations/aave-like' import { AaveLikeTokens, IViewPositionParams } from '@dma-library/types' import { AaveVersion } from '@dma-library/types/aave' import * as StrategyParams from '@dma-library/types/strategy-params' +import BigNumber from 'bignumber.js' export type AaveGetCurrentPositionArgs = IViewPositionParams export type AaveV2GetCurrentPositionDependencies = StrategyParams.WithViewPositionDependencies & { - protocolVersion: AaveVersion.v2 - } + protocolVersion: AaveVersion.v2 +} export type AaveV3GetCurrentPositionDependencies = StrategyParams.WithViewPositionDependencies & { - protocolVersion: AaveVersion.v3 + protocolVersion: AaveVersion.v3 +} + +type AaveV2ReserveDataReply = { + availableLiquidity: BigNumber + totalStableDebt: BigNumber + totalVariableDebt: BigNumber + liquidityRate: BigNumber + variableBorrowRate: BigNumber + stableBorrowRate: BigNumber + averageStableBorrowRate: BigNumber + liquidityIndex: BigNumber + variableBorrowIndex: BigNumber + lastUpdateTimestamp: BigNumber +} + +type AaveV3ReserveDataReply = { + availableLiquidity: BigNumber + unbacked: BigNumber + accruedToTreasuryScaled: BigNumber + totalAToken: BigNumber + totalToken: BigNumber + totalStableDebt: BigNumber + totalVariableDebt: BigNumber + liquidityRate: BigNumber + variableBorrowRate: BigNumber + stableBorrowRate: BigNumber + averageStableBorrowRate: BigNumber + liquidityIndex: BigNumber + variableBorrowIndex: BigNumber + lastUpdateTimestamp: BigNumber +} + +type SparkV3ReserveDataReply = AaveV3ReserveDataReply + +export type ReserveDataReply = + | AaveV2ReserveDataReply + | AaveV3ReserveDataReply + | SparkV3ReserveDataReply + +export type AaveLikeCumulativeData = { + cumulativeDepositUSD: BigNumber + cumulativeDepositInQuoteToken: BigNumber + cumulativeDepositInCollateralToken: BigNumber + cumulativeWithdrawUSD: BigNumber + cumulativeWithdrawInQuoteToken: BigNumber + cumulativeWithdrawInCollateralToken: BigNumber + cumulativeFeesUSD: BigNumber + cumulativeFeesInQuoteToken: BigNumber + cumulativeFeesInCollateralToken: BigNumber +} + +export interface AaveLikeReserveConfigurationData { + ltv: BigNumber + liquidationThreshold: BigNumber + liquidationBonus: BigNumber +} + +export interface AaveLikeReserveData { + tokenAddress: string + variableDebtAddress: string + availableLiquidity: BigNumber + variableBorrowRate: BigNumber + liquidityRate: BigNumber + caps: { + borrow: BigNumber + supply: BigNumber } + totalDebt: BigNumber + totalSupply: BigNumber + availableToBorrow: BigNumber + availableToSupply: BigNumber +} + +export type ReserveData = { + collateral: AaveLikeReserveData + debt: AaveLikeReserveData +} diff --git a/packages/dma-library/src/views/common/erc4626.ts b/packages/dma-library/src/views/common/erc4626.ts index 7cec5e14..7335fef1 100644 --- a/packages/dma-library/src/views/common/erc4626.ts +++ b/packages/dma-library/src/views/common/erc4626.ts @@ -24,7 +24,11 @@ export async function getErc4626Position( ): Promise { const { precision } = underlyingAsset - const vaultContractInstance = new ethers.Contract(vaultAddress, erc4626abi, provider) as IERC4626 + const vaultContractInstance = new ethers.Contract( + vaultAddress, + erc4626abi, + provider, + ) as unknown as IERC4626 const [vaultParameters, subgraphResponse] = await Promise.all([ getVaultApyParameters(vaultAddress), getLazyVaultSubgraphResponse(vaultAddress, proxyAddress), diff --git a/packages/dma-library/src/views/index.ts b/packages/dma-library/src/views/index.ts index b5f43d69..53371a82 100644 --- a/packages/dma-library/src/views/index.ts +++ b/packages/dma-library/src/views/index.ts @@ -3,8 +3,11 @@ import { AaveVersion } from '@dma-library/types/aave' import { AaveView, getCurrentPositionAaveV2, getCurrentPositionAaveV3 } from './aave' import type { GetCumulativesData, GetEarnData } from './ajna' import { getEarnPosition, getPosition } from './ajna' +import { getErc4626Position } from './common' import { getMorphoPosition } from './morpho' -import { getCurrentSparkPosition, SparkView } from './spark' +import { + getCurrentSparkPosition, SparkView +} from "./spark"; const aave: AaveView = { v2: (args, dependencies) => @@ -18,6 +21,7 @@ const aave: AaveView = { protocolVersion: AaveVersion.v3, }), } + const spark: SparkView = getCurrentSparkPosition const ajna = { getPosition, @@ -27,12 +31,15 @@ const ajna = { const morpho = { getPosition: getMorphoPosition, } - +const common = { + getErc4626Position: getErc4626Position, +} const views = { ajna, aave, spark, morpho, + common, } export { GetCumulativesData, GetEarnData } export { views } From 019cae259c06e272ffa4212b2f195d287be3d091 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 26 Apr 2024 09:34:20 +0100 Subject: [PATCH 5/9] Publish - @oasisdex/dma-library@0.6.4-automation --- packages/dma-library/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dma-library/package.json b/packages/dma-library/package.json index 0ef24297..d894a660 100644 --- a/packages/dma-library/package.json +++ b/packages/dma-library/package.json @@ -1,6 +1,6 @@ { "name": "@oasisdex/dma-library", - "version": "0.6.3-automation", + "version": "0.6.4-automation", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", "main": "lib/index.js", From 2f16834bb2cf845f6b99812a307cf50c80f71dbe Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 26 Apr 2024 09:47:07 +0100 Subject: [PATCH 6/9] v0.1.19-automation --- packages/addresses/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/addresses/package.json b/packages/addresses/package.json index c0d33d86..d2496b19 100644 --- a/packages/addresses/package.json +++ b/packages/addresses/package.json @@ -1,6 +1,6 @@ { "name": "@oasisdex/addresses", - "version": "0.1.18-automation", + "version": "0.1.19-automation", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", "main": "lib/index.js", From 622787ce487f3aa004ba3a5bc9e58bd95398d6fd Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 26 Apr 2024 14:53:18 +0100 Subject: [PATCH 7/9] v0.1.20-automation --- packages/addresses/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/addresses/package.json b/packages/addresses/package.json index d2496b19..6e78fcdb 100644 --- a/packages/addresses/package.json +++ b/packages/addresses/package.json @@ -1,6 +1,6 @@ { "name": "@oasisdex/addresses", - "version": "0.1.19-automation", + "version": "0.1.20-automation", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", "main": "lib/index.js", From efdc0a3f4970cc5fe7090fb58dac04983b0908c5 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Mon, 29 Apr 2024 13:10:55 +0100 Subject: [PATCH 8/9] v0.1.20-automation.0 --- packages/addresses/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/addresses/package.json b/packages/addresses/package.json index 6e78fcdb..810c807d 100644 --- a/packages/addresses/package.json +++ b/packages/addresses/package.json @@ -1,6 +1,6 @@ { "name": "@oasisdex/addresses", - "version": "0.1.20-automation", + "version": "0.1.20-automation.0", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", "main": "lib/index.js", From 5c7c32e6fdba9d6d2ef282a0aeb756b2ce8ff990 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 30 Apr 2024 12:07:00 +0100 Subject: [PATCH 9/9] feat: update addresses to include morpho addresses --- .../deploy-configurations/addresses/index.ts | 20 ++++++++++--------- .../configs/mainnet.conf.ts | 6 +++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/deploy-configurations/addresses/index.ts b/packages/deploy-configurations/addresses/index.ts index 3be44b20..75147734 100644 --- a/packages/deploy-configurations/addresses/index.ts +++ b/packages/deploy-configurations/addresses/index.ts @@ -29,6 +29,7 @@ import { MakerProtocolJoins, MakerProtocolPips, } from '@deploy-configurations/types/deployment-config/maker-protocol' +import { MorphoBlueProtocol } from '@deploy-configurations/types/deployment-config/morpho-blue' import { OptionalSparkProtocolContracts, SparkProtocol, @@ -45,7 +46,7 @@ enum AaveKeys { V3 = 'v3', } -type DefaultDeployment = { +export type DefaultDeployment = { [SystemKeys.MPA]: { [MpaKeys.CORE]: Record [MpaKeys.ACTIONS]: Record @@ -63,6 +64,7 @@ type DefaultDeployment = { } [SystemKeys.AUTOMATION]: Record [SystemKeys.AJNA]: Record + [SystemKeys.MORPHO_BLUE]: Record } export type Addresses = { @@ -78,10 +80,7 @@ export type Addresses = { if (!mainnetConfig.aave.v2) throw new Error('Missing aave v2 config on mainnet') if (!optimismConfig.aave.v3.L2Encoder) throw new Error('Missing L2Encoder config on optimism') -const createAddressesStructure = ( - networkConfig: SystemConfig, - ajnaConfig?: SystemConfig, -): DefaultDeployment => ({ +const createAddressesStructure = (networkConfig: SystemConfig): DefaultDeployment => ({ mpa: { core: { ...extractAddressesFromConfig(networkConfig.mpa.core), @@ -119,7 +118,10 @@ const createAddressesStructure = ( ...extractAddressesFromConfig(networkConfig.automation), }, ajna: { - ...extractAddressesFromConfig(ajnaConfig?.ajna || networkConfig.ajna), + ...extractAddressesFromConfig(networkConfig.ajna), + }, + morphoblue: { + ...extractAddressesFromConfig(networkConfig.morphoblue), }, }) @@ -149,9 +151,9 @@ function extractAddressesFromConfig( export const ADDRESSES: Addresses = { [Network.MAINNET]: createAddressesStructure(mainnetConfig), [Network.OPTIMISM]: createAddressesStructure(optimismConfig), - [Network.GOERLI]: createAddressesStructure(goerliConfig, goerliConfig), - [Network.ARBITRUM]: createAddressesStructure(arbitrumConfig, mainnetConfig), - [Network.BASE]: createAddressesStructure(baseConfig, mainnetConfig), + [Network.GOERLI]: createAddressesStructure(goerliConfig), + [Network.ARBITRUM]: createAddressesStructure(arbitrumConfig), + [Network.BASE]: createAddressesStructure(baseConfig), [Network.TEST]: createAddressesStructure(testConfig), [Network.SEPOLIA]: createAddressesStructure(sepoliaConfig), } diff --git a/packages/deploy-configurations/configs/mainnet.conf.ts b/packages/deploy-configurations/configs/mainnet.conf.ts index 3d9ce366..0bd0e0b8 100644 --- a/packages/deploy-configurations/configs/mainnet.conf.ts +++ b/packages/deploy-configurations/configs/mainnet.conf.ts @@ -1476,8 +1476,12 @@ export const config: SystemConfig = { morphoblue: { MorphoBlue: { name: 'MorphoBlue', - address: '0x0000000000000000000000000000000000000000', + address: '0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb', serviceRegistryName: SERVICE_REGISTRY_NAMES.morphoblue.MORPHO_BLUE, }, + AdaptiveCurveIrm: { + name: 'AdaptiveCurveIrm', + address: '0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC', + }, }, }