diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..a9371d0ef --- /dev/null +++ b/biome.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.6.3/schema.json", + "linter": { + "enabled": true, + "rules": { + "suspicious": { + "noAssignInExpressions": "warn", + "noConfusingVoidType": "warn", + "noExplicitAny": "warn", + "noFallthroughSwitchClause": "warn", + "noImplicitAnyLet": "warn", + "noPrototypeBuiltins": "warn", + "recommended": true + }, + "correctness": { + "recommended": true, + "noUnusedImports": "error", + "noUnusedVariables": "warn" + }, + "style": { + "recommended": true, + "noUnusedTemplateLiteral": "warn", + "noNonNullAssertion": "off", + "useNodejsImportProtocol": "warn", + "noParameterAssign": "off", + "useLiteralEnumMembers": "off", + "useImportType": "off" + }, + "complexity": { + "noStaticOnlyClass": "off", + "noBannedTypes": "warn", + "noExcessiveCognitiveComplexity": "off", + "noExtraBooleanCast": "off", + "noForEach": "off", + "noThisInStatic": "error", + "noUselessCatch": "warn", + "noUselessConstructor": "error", + "noUselessEmptyExport": "warn", + "noUselessFragments": "warn", + "noUselessLabel": "off", + "noUselessRename": "error", + "noUselessSwitchCase": "error", + "noUselessThisAlias": "off", + "noUselessTypeConstraint": "warn", + "noVoid": "error", + "useLiteralKeys": "warn" + } + } + }, + "organizeImports": { + "enabled": true + }, + "javascript": { + "formatter": { + "enabled": true, + "indentWidth": 2, + "indentStyle": "space", + "lineEnding": "lf", + "semicolons": "always", + "trailingComma": "es5", + "quoteStyle": "double", + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "arrowParentheses": "asNeeded", + "lineWidth": 120, + "bracketSameLine": true, + "bracketSpacing": true + } + }, + "formatter": { + "indentStyle": "space", + "enabled": true, + "formatWithErrors": true, + "indentWidth": 2, + "lineWidth": 120, + "lineEnding": "lf" + } +} diff --git a/package.json b/package.json index 726f543a0..8d84a85c4 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "checks": "yarn check:pq && yarn check:tsc && yarn check:es" }, "dependencies": { + "@biomejs/biome": "^1.6.3", "@0x/utils": "^4.5.2", "@bgd-labs/aave-address-book": "2.21.1", "@ethersproject/abi": "^5.7.0", diff --git a/src/abi/angle-transmuter/Transmuter.json b/src/abi/angle-transmuter/Transmuter.json index 8fa454b64..fffed6243 100644 --- a/src/abi/angle-transmuter/Transmuter.json +++ b/src/abi/angle-transmuter/Transmuter.json @@ -4,7 +4,7 @@ "name": "accessControlManager", "outputs": [ { - "internalType": "address", + "internalType": "contract IAccessControlManager", "name": "", "type": "address" } @@ -53,7 +53,7 @@ "name": "agToken", "outputs": [ { - "internalType": "address", + "internalType": "contract IAgToken", "name": "", "type": "address" } @@ -64,7 +64,7 @@ { "inputs": [ { - "internalType": "address", + "internalType": "contract IERC20", "name": "token", "type": "address" }, @@ -94,7 +94,7 @@ "type": "address" }, { - "internalType": "uint8", + "internalType": "enum FacetCutAction", "name": "action", "type": "uint8" }, @@ -104,7 +104,7 @@ "type": "bytes4[]" } ], - "internalType": "struct DummyDiamondImplementation.Tuple6871229[]", + "internalType": "struct FacetCut[]", "name": "_diamondCut", "type": "tuple[]" }, @@ -168,7 +168,7 @@ "outputs": [ { "internalType": "bytes4[]", - "name": "_facetFunctionSelectors", + "name": "facetFunctionSelectors_", "type": "bytes4[]" } ], @@ -192,7 +192,7 @@ "type": "bytes4[]" } ], - "internalType": "struct DummyDiamondImplementation.Tuple1236461[]", + "internalType": "struct Facet[]", "name": "facets_", "type": "tuple[]" } @@ -318,7 +318,7 @@ { "components": [ { - "internalType": "address[]", + "internalType": "contract IERC20[]", "name": "subCollaterals", "type": "address[]" }, @@ -328,12 +328,12 @@ "type": "bytes" } ], - "internalType": "struct DummyDiamondImplementation.Tuple5479340", + "internalType": "struct ManagerStorage", "name": "managerData", "type": "tuple" } ], - "internalType": "struct DummyDiamondImplementation.Tuple3550792", + "internalType": "struct Collateral", "name": "", "type": "tuple" } @@ -451,17 +451,17 @@ "outputs": [ { "internalType": "bool", - "name": "", + "name": "isManaged", "type": "bool" }, { - "internalType": "address[]", - "name": "", + "internalType": "contract IERC20[]", + "name": "subCollaterals", "type": "address[]" }, { "internalType": "bytes", - "name": "", + "name": "config", "type": "bytes" } ], @@ -479,12 +479,12 @@ "name": "getOracle", "outputs": [ { - "internalType": "uint8", + "internalType": "enum OracleReadType", "name": "oracleType", "type": "uint8" }, { - "internalType": "uint8", + "internalType": "enum OracleTargetType", "name": "targetType", "type": "uint8" }, @@ -497,6 +497,11 @@ "internalType": "bytes", "name": "targetData", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "hyperparameters", + "type": "bytes" } ], "stateMutability": "view", @@ -565,26 +570,13 @@ "outputs": [ { "internalType": "uint256", - "name": "", + "name": "stablecoinsIssued", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "implementation", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -593,7 +585,7 @@ "type": "address" }, { - "internalType": "uint8", + "internalType": "enum ActionType", "name": "action", "type": "uint8" } @@ -712,7 +704,7 @@ { "inputs": [ { - "internalType": "uint8", + "internalType": "enum WhitelistType", "name": "whitelistType", "type": "uint8" }, @@ -823,7 +815,7 @@ "type": "address" }, { - "internalType": "address", + "internalType": "contract IERC20", "name": "token", "type": "address" }, @@ -986,7 +978,7 @@ { "components": [ { - "internalType": "address[]", + "internalType": "contract IERC20[]", "name": "subCollaterals", "type": "address[]" }, @@ -996,7 +988,7 @@ "type": "bytes" } ], - "internalType": "struct DummyDiamondImplementation.Tuple5479340", + "internalType": "struct ManagerStorage", "name": "managerData", "type": "tuple" } @@ -1006,19 +998,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "_implementation", - "type": "address" - } - ], - "name": "setDummyImplementation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -1308,7 +1287,7 @@ "type": "address" }, { - "internalType": "uint8", + "internalType": "enum TrustedType", "name": "t", "type": "uint8" } @@ -1319,9 +1298,22 @@ "type": "function" }, { + "type": "function", + "name": "updateOracle", "inputs": [ { - "internalType": "uint8", + "name": "collateral", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "inputs": [ + { + "internalType": "enum WhitelistType", "name": "whitelistType", "type": "uint8" }, @@ -1359,361 +1351,5 @@ ], "stateMutability": "nonpayable", "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenIn", - "type": "address", - "indexed": true - }, - { - "internalType": "address", - "name": "tokenOut", - "type": "address", - "indexed": true - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256", - "indexed": false - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256", - "indexed": false - }, - { - "internalType": "address", - "name": "from", - "type": "address", - "indexed": true - }, - { - "internalType": "address", - "name": "to", - "type": "address", - "indexed": false - } - ], - "type": "event", - "name": "Swap", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256", - "indexed": false - }, - { - "internalType": "address[]", - "name": "tokens", - "type": "address[]", - "indexed": false - }, - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]", - "indexed": false - }, - { - "internalType": "address[]", - "name": "forfeitTokens", - "type": "address[]", - "indexed": false - }, - { - "internalType": "address", - "name": "from", - "type": "address", - "indexed": true - }, - { - "internalType": "address", - "name": "to", - "type": "address", - "indexed": true - } - ], - "type": "event", - "name": "Redeemed", - "anonymous": false - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - } - ], - "name": "CollateralAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20[]", - "name": "subCollaterals", - "type": "address[]" - }, - { - "internalType": "bytes", - "name": "config", - "type": "bytes" - } - ], - "indexed": false, - "internalType": "struct ManagerStorage", - "name": "managerData", - "type": "tuple" - } - ], - "name": "CollateralManagerSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - } - ], - "name": "CollateralRevoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "whitelistData", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "whitelistStatus", - "type": "uint8" - } - ], - "name": "CollateralWhitelistStatusUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint64[]", - "name": "xFee", - "type": "uint64[]" - }, - { - "indexed": false, - "internalType": "int64[]", - "name": "yFee", - "type": "int64[]" - }, - { - "indexed": false, - "internalType": "bool", - "name": "mint", - "type": "bool" - } - ], - "name": "FeesSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "oracleConfig", - "type": "bytes" - } - ], - "name": "OracleSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "pausedType", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isPaused", - "type": "bool" - } - ], - "name": "PauseToggled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64[]", - "name": "xFee", - "type": "uint64[]" - }, - { - "indexed": false, - "internalType": "int64[]", - "name": "yFee", - "type": "int64[]" - } - ], - "name": "RedemptionCurveParamsSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collateral", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "increase", - "type": "bool" - } - ], - "name": "ReservesAdjusted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isTrusted", - "type": "bool" - }, - { - "indexed": false, - "internalType": "enum TrustedType", - "name": "trustedType", - "type": "uint8" - } - ], - "name": "TrustedToggled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "enum WhitelistType", - "name": "whitelistType", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "address", - "name": "who", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "whitelistStatus", - "type": "uint256" - } - ], - "name": "WhitelistStatusToggled", - "type": "event" } ] diff --git a/src/dex/angle-transmuter/angle-transmuter-events.test.ts b/src/dex/angle-transmuter/angle-transmuter-events.test.ts index a434a2e9f..ee85fe45c 100644 --- a/src/dex/angle-transmuter/angle-transmuter-events.test.ts +++ b/src/dex/angle-transmuter/angle-transmuter-events.test.ts @@ -23,7 +23,7 @@ async function fetchPoolState( // eventName -> blockNumbers type EventMappings = Record; -describe('AngleTransmuter EventPool Mainnet', function () { +describe('AngleTransmuter EventPool Mainnet', () => { const dexKey = 'AngleTransmuter'; const network = Network.MAINNET; const dexHelper = new DummyDexHelper(network); @@ -77,7 +77,7 @@ describe('AngleTransmuter EventPool Mainnet', function () { dexHelper, logger, { - agEUR: { + EURA: { address: '0x1a7e4e63778B4f12a199C062f3eFdD288afCBce8', decimals: 18, }, @@ -121,7 +121,7 @@ describe('AngleTransmuter EventPool Mainnet', function () { ([eventName, blockNumbers]: [string, number[]]) => { describe(`${eventName}`, () => { blockNumbers.forEach((blockNumber: number) => { - it(`State after ${blockNumber}`, async function () { + it(`State after ${blockNumber}`, async () => { await testEventSubscriber( angleTransmuterPool, angleTransmuterPool.addressesSubscribed, diff --git a/src/dex/angle-transmuter/angle-transmuter-integration.test.ts b/src/dex/angle-transmuter/angle-transmuter-integration.test.ts index 595f9e3d5..91b217280 100644 --- a/src/dex/angle-transmuter/angle-transmuter-integration.test.ts +++ b/src/dex/angle-transmuter/angle-transmuter-integration.test.ts @@ -159,7 +159,7 @@ async function testPricingOnNetwork( ); } -describe('AngleTransmuter', function () { +describe('AngleTransmuter', () => { const dexKey = 'AngleTransmuter'; let blockNumber: number; let angleTransmuter: AngleTransmuter; @@ -209,7 +209,7 @@ describe('AngleTransmuter', function () { } }); - it('getPoolIdentifiers and getPricesVolume SELL', async function () { + it('getPoolIdentifiers and getPricesVolume SELL', async () => { await testPricingOnNetwork( angleTransmuter, network, @@ -223,7 +223,7 @@ describe('AngleTransmuter', function () { ); }); - it('getPoolIdentifiers and getPricesVolume BUY', async function () { + it('getPoolIdentifiers and getPricesVolume BUY', async () => { await testPricingOnNetwork( angleTransmuter, network, @@ -237,7 +237,7 @@ describe('AngleTransmuter', function () { ); }); - it('getTopPoolsForToken', async function () { + it('getTopPoolsForToken', async () => { // We have to check without calling initializePricing, because // pool-tracker is not calling that function const newAngleTransmuter = new AngleTransmuter( diff --git a/src/dex/angle-transmuter/angle-transmuter-pool.ts b/src/dex/angle-transmuter/angle-transmuter-pool.ts index ba6fe4d7e..f75f65bf5 100644 --- a/src/dex/angle-transmuter/angle-transmuter-pool.ts +++ b/src/dex/angle-transmuter/angle-transmuter-pool.ts @@ -10,6 +10,7 @@ import { DecodedOracleConfig, DexParams, OracleFeed, + OracleHyperparameter, OracleQuoteType, OracleReadType, PoolConfig, @@ -26,11 +27,13 @@ import { _quoteBurnExactOutput, _quoteMintExactInput, _quoteMintExactOutput, + filterDictionaryOnly, } from './utils'; import { RedstoneSubscriber } from './redstone'; -import { formatUnits } from 'ethers/lib/utils'; +import { formatEther, formatUnits } from 'ethers/lib/utils'; import { BackedSubscriber } from './backedOracle'; import { SwapSide } from '../../constants'; +import { ethers } from 'ethers'; export class AngleTransmuterEventPool extends ComposedEventSubscriber { public transmuter: Contract; @@ -151,9 +154,9 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber blockNumber: number, ): Promise { const state = await this.getStateOrGenerate(blockNumber); - const isMint = _tokenOut == state.stablecoin.address; + const isMint = _tokenOut === state.stablecoin.address; let oracleValue: number; - let minRatio: number = 0; + let minRatio = 0; let collateral = _tokenIn; if (isMint) oracleValue = await this._readMint(this.config, state, _tokenIn); @@ -173,7 +176,7 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber const fees = state.transmuter.collaterals[collateral].fees; return _amounts.map(_amount => { - if (isMint && side == SwapSide.SELL) + if (isMint && side === SwapSide.SELL) return _quoteMintExactInput( oracleValue, _amount, @@ -181,7 +184,7 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber collatStablecoinIssued, otherStablecoinIssued, ); - else if (isMint && side == SwapSide.BUY) + if (isMint && side === SwapSide.BUY) return _quoteMintExactOutput( oracleValue, _amount, @@ -189,7 +192,7 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber collatStablecoinIssued, otherStablecoinIssued, ); - else if (!isMint && side == SwapSide.SELL) + if (!isMint && side === SwapSide.SELL) return _quoteBurnExactInput( oracleValue, minRatio, @@ -198,15 +201,14 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber collatStablecoinIssued, otherStablecoinIssued, ); - else - return _quoteBurnExactOutput( - oracleValue, - minRatio, - _amount, - fees, - collatStablecoinIssued, - otherStablecoinIssued, - ); + return _quoteBurnExactOutput( + oracleValue, + minRatio, + _amount, + fees, + collatStablecoinIssued, + otherStablecoinIssued, + ); }); } @@ -360,15 +362,15 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber if (oracleConfigDecoded.oracleType !== OracleReadType.EXTERNAL) { // add all the feed oracles used to their respective channels if ( - oracleConfigDecoded.oracleType == OracleReadType.CHAINLINK_FEEDS || - oracleConfigDecoded.oracleType == OracleReadType.PYTH + oracleConfigDecoded.oracleType === OracleReadType.CHAINLINK_FEEDS || + oracleConfigDecoded.oracleType === OracleReadType.PYTH ) { const oracleFeed = TransmuterSubscriber._decodeOracleFeed( oracleConfigDecoded.oracleType, oracleConfigDecoded.oracleData, ); ({ chainlinkMap, backedMap, redstoneMap, pythIds } = - await this._fillMap( + await AngleTransmuterEventPool._fillMap( chainlinkMap, backedMap, redstoneMap, @@ -379,15 +381,15 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber )); } if ( - oracleConfigDecoded.targetType == OracleReadType.CHAINLINK_FEEDS || - oracleConfigDecoded.targetType == OracleReadType.PYTH + oracleConfigDecoded.targetType === OracleReadType.CHAINLINK_FEEDS || + oracleConfigDecoded.targetType === OracleReadType.PYTH ) { const oracleFeed = TransmuterSubscriber._decodeOracleFeed( oracleConfigDecoded.targetType, oracleConfigDecoded.targetData, ); ({ chainlinkMap, backedMap, redstoneMap, pythIds } = - await this._fillMap( + await AngleTransmuterEventPool._fillMap( chainlinkMap, backedMap, redstoneMap, @@ -413,14 +415,14 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber blockNumber: number | 'latest', multiContract: Contract, ): Promise { - const collaterals = await this.getCollateralsList( + const collaterals = await AngleTransmuterEventPool.getCollateralsList( dexParams.transmuter, blockNumber, multiContract, ); // get all oracles feed - const oracles = await this.getOraclesConfig( + const oracles = await AngleTransmuterEventPool.getOraclesConfig( dexParams.transmuter, dexParams.pyth, collaterals, @@ -429,7 +431,7 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber ); return { - agEUR: dexParams.agEUR, + agEUR: dexParams.EURA, transmuter: dexParams.transmuter, collaterals: collaterals, oracles: oracles, @@ -442,26 +444,14 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber _readMint(config: PoolConfig, state: PoolState, collateral: Address): number { const configOracle = state.transmuter.collaterals[collateral].config; - if (configOracle.oracleType == OracleReadType.EXTERNAL) { + if (configOracle.oracleType === OracleReadType.EXTERNAL) { return 1; - } else { - const targetPrice = this._read( - config, - state, - configOracle.targetType, - configOracle.targetFeed, - 1, - ); - let oracleValue = this._read( - config, - state, - configOracle.oracleType, - configOracle.oracleFeed, - targetPrice, - ); - if (targetPrice < oracleValue) oracleValue = targetPrice; - return oracleValue; } + let target: number; + let spot: number; + ({ spot, target } = this._readSpotAndTarget(config, state, collateral)); + if (target < spot) spot = target; + return spot; } _getBurnOracle( @@ -498,32 +488,77 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber collateral: Address, ): { oracleValue: number; ratio: number } { const configOracle = state.transmuter.collaterals[collateral].config; - if (configOracle.oracleType == OracleReadType.EXTERNAL) { + if (configOracle.oracleType === OracleReadType.EXTERNAL) { return { oracleValue: 1, ratio: 1 }; - } else { - const targetPrice = this._read( - config, - state, - configOracle.targetType, - configOracle.targetFeed, - 1, - ); - const oracleValue = this._read( - config, - state, - configOracle.oracleType, - configOracle.oracleFeed, - targetPrice, - ); - let ratio = 1; - if (oracleValue < targetPrice) ratio = oracleValue / targetPrice; - return { oracleValue, ratio }; } + let spot: number; + let target: number; + let burnRatioDeviation: number; + ({ spot, target, burnRatioDeviation } = this._readSpotAndTarget( + config, + state, + collateral, + )); + + let ratio = 1; + if (spot < target * (1 - burnRatioDeviation)) ratio = spot / target; + else if (spot < target) spot = target; + return { oracleValue: spot, ratio }; + } + + _readSpotAndTarget( + config: PoolConfig, + state: PoolState, + collateral: Address, + ): { + spot: number; + target: number; + userDeviation: number; + burnRatioDeviation: number; + } { + const configOracle = state.transmuter.collaterals[collateral].config; + const hyperparameters = filterDictionaryOnly( + ethers.utils.defaultAbiCoder.decode( + ['uint128 userDeviation', 'uint128 burnRatioDeviation'], + configOracle.hyperparameters, + ), + ) as unknown as OracleHyperparameter; + const targetPrice = this._read( + config, + state, + configOracle.targetType, + configOracle.targetFeed, + 1, + ); + let oracleValue = this._read( + config, + state, + configOracle.oracleType, + configOracle.oracleFeed, + targetPrice, + ); + const userDeviation = Number.parseFloat( + formatEther(hyperparameters.userDeviation.toString()), + ); + const burnRatioDeviation = Number.parseFloat( + formatEther(hyperparameters.burnRatioDeviation.toString()), + ); + if ( + targetPrice * (1 - userDeviation) < oracleValue || + oracleValue < targetPrice * (1 + userDeviation) + ) + oracleValue = targetPrice; + return { + spot: oracleValue, + target: targetPrice, + userDeviation, + burnRatioDeviation, + }; } _quoteAmount(quoteType: OracleQuoteType, baseValue: number): number { if (quoteType === OracleQuoteType.UNIT) return 1; - else return baseValue; + return baseValue; } _read( @@ -534,33 +569,33 @@ export class AngleTransmuterEventPool extends ComposedEventSubscriber baseValue: number, ): number { let price = 1; - if (oracleType == OracleReadType.CHAINLINK_FEEDS) { + if (oracleType === OracleReadType.CHAINLINK_FEEDS) { price = this._quoteAmount(feed.chainlink!.quoteType, baseValue); for (let i = 0; i < feed.chainlink!.circuitChainlink.length; i++) { const id = feed.chainlink!.circuitChainlink[i]; - let decimals; + let decimals: number; if (Object.keys(config.oracles.chainlink).includes(id)) decimals = config.oracles.chainlink[id].decimals; else if (Object.keys(config.oracles.redstone).includes(id)) decimals = config.oracles.redstone[id].decimals; else decimals = config.oracles.backed[id].decimals; - const rate = parseFloat( + const rate = Number.parseFloat( formatUnits(state.oracles.chainlink[id].answer.toString(), decimals), ); - if (feed.chainlink!.circuitChainIsMultiplied[i] == 1) price *= rate; + if (feed.chainlink!.circuitChainIsMultiplied[i] === 1) price *= rate; else price /= rate; } - } else if (oracleType == OracleReadType.PYTH) { + } else if (oracleType === OracleReadType.PYTH) { price = this._quoteAmount(feed.pyth!.quoteType, baseValue); for (let i = 0; i < feed.pyth!.feedIds.length; i++) { const id = feed.pyth!.feedIds[i]; const rate = state.oracles.pyth[id].answer; - if (feed.pyth!.isMultiplied[i] == 1) price *= rate; + if (feed.pyth!.isMultiplied[i] === 1) price *= rate; else price /= rate; } - } else if (oracleType == OracleReadType.STABLE) { + } else if (oracleType === OracleReadType.STABLE) { price = 1; - } else if (oracleType == OracleReadType.NO_ORACLE) { + } else if (oracleType === OracleReadType.NO_ORACLE) { price = baseValue; } return price; diff --git a/src/dex/angle-transmuter/angle-transmuter.ts b/src/dex/angle-transmuter/angle-transmuter.ts index ffdfb76cb..7b5c40b3b 100644 --- a/src/dex/angle-transmuter/angle-transmuter.ts +++ b/src/dex/angle-transmuter/angle-transmuter.ts @@ -21,7 +21,6 @@ import { AngleTransmuterEventPool } from './angle-transmuter-pool'; import { Interface, formatUnits, parseUnits } from 'ethers/lib/utils'; import { TransmuterSubscriber } from './transmuter'; import ERC20ABI from '../../abi/erc20.json'; -import _ from 'lodash'; const TransmuterGasCost = 0; @@ -33,7 +32,7 @@ export class AngleTransmuter protected supportedTokensMap: { [address: string]: boolean } = {}; // supportedTokens is only used by the pooltracker protected supportedTokens: Token[] = []; - transmuterUSDLiquidity: number = 0; + transmuterUSDLiquidity = 0; public static erc20Interface = new Interface(ERC20ABI); @@ -56,7 +55,7 @@ export class AngleTransmuter ) { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); - this.supportedTokensMap[params.agEUR.address.toLowerCase()] = true; + this.supportedTokensMap[params.EURA.address.toLowerCase()] = true; } // Initialize pricing is called once in the start of @@ -99,8 +98,8 @@ export class AngleTransmuter blockNumber: number, ): Promise { if (this._knownAddress(srcToken, destToken)) - return [`${this.dexKey}_${this.params.agEUR.address.toLowerCase()}`]; - else return []; + return [`${this.dexKey}_${this.params.EURA.address.toLowerCase()}`]; + return []; } // Returns pool prices for amounts. @@ -117,7 +116,7 @@ export class AngleTransmuter ): Promise> { const uniquePool = `${ this.dexKey - }_${this.params.agEUR.address.toLowerCase()}`; + }_${this.params.EURA.address.toLowerCase()}`; if ( !this._knownAddress(srcToken, destToken) || (limitPools && limitPools.length > 0 && !limitPools.includes(uniquePool)) @@ -125,12 +124,12 @@ export class AngleTransmuter return null; const preProcessDecimals = - side == SwapSide.SELL ? srcToken.decimals : destToken.decimals; + side === SwapSide.SELL ? srcToken.decimals : destToken.decimals; const postProcessDecimals = - side == SwapSide.SELL ? destToken.decimals : srcToken.decimals; + side === SwapSide.SELL ? destToken.decimals : srcToken.decimals; const unitVolume = 1; const amountsFloat = amounts.map(amount => - parseFloat(formatUnits(amount.toString(), preProcessDecimals)), + Number.parseFloat(formatUnits(amount.toString(), preProcessDecimals)), ); const prices = await this.eventPools!.getAmountOut( @@ -210,10 +209,10 @@ export class AngleTransmuter // Encode here the transaction arguments const swapData = TransmuterSubscriber.interface.encodeFunctionData( - side == SwapSide.SELL ? 'swapExactInput' : 'swapExactOutput', + side === SwapSide.SELL ? 'swapExactInput' : 'swapExactOutput', [ - side == SwapSide.SELL ? srcAmount : destAmount, - side == SwapSide.SELL ? destAmount : srcAmount, + side === SwapSide.SELL ? srcAmount : destAmount, + side === SwapSide.SELL ? destAmount : srcAmount, srcToken, destToken, this.augustusAddress, @@ -243,7 +242,7 @@ export class AngleTransmuter 'latest', this.dexHelper.multiContract, ); - tokenAddresses = tokenAddresses.concat([this.params.agEUR.address]); + tokenAddresses = tokenAddresses.concat([this.params.EURA.address]); const decimalsCallData = AngleTransmuter.erc20Interface.encodeFunctionData('decimals'); @@ -258,7 +257,7 @@ export class AngleTransmuter ).returnData; const tokenDecimals = res.map((r: any) => - parseInt( + Number.parseInt( AngleTransmuter.erc20Interface .decodeFunctionResult('decimals', r)[0] .toString(), @@ -313,11 +312,11 @@ export class AngleTransmuter if (!this.supportedTokens.some(t => t.address === tokenAddress)) return []; const connectorTokens = - tokenAddress === this.params.agEUR.address + tokenAddress === this.params.EURA.address ? this.supportedTokens.filter( - token => token.address !== this.params.agEUR.address, + token => token.address !== this.params.EURA.address, ) - : [this.params.agEUR]; + : [this.params.EURA]; return [ { exchange: this.dexKey, @@ -325,7 +324,7 @@ export class AngleTransmuter connectorTokens: connectorTokens.slice(0, limit), // liquidity is potentially infinite if swapping for agXXX, otherwise at most reserves value liquidityUSD: - tokenAddress == this.params.agEUR.address + tokenAddress === this.params.EURA.address ? this.transmuterUSDLiquidity : 1e9, }, @@ -348,8 +347,8 @@ export class AngleTransmuter this.supportedTokensMap[srcAddress] && this.supportedTokensMap[destAddress] && // check that at least one of the tokens is agEUR - (srcAddress == this.params.agEUR.address.toLowerCase() || - destAddress == this.params.agEUR.address.toLowerCase()) + (srcAddress === this.params.EURA.address.toLowerCase() || + destAddress === this.params.EURA.address.toLowerCase()) ) { return true; } diff --git a/src/dex/angle-transmuter/config.ts b/src/dex/angle-transmuter/config.ts index 1426cf245..4cac17261 100644 --- a/src/dex/angle-transmuter/config.ts +++ b/src/dex/angle-transmuter/config.ts @@ -5,7 +5,7 @@ import { Network, SwapSide } from '../../constants'; export const AngleTransmuterConfig: DexConfigMap = { AngleTransmuter: { [Network.MAINNET]: { - agEUR: { + EURA: { address: '0x1a7e4e63778B4f12a199C062f3eFdD288afCBce8', decimals: 18, }, diff --git a/src/dex/angle-transmuter/pyth.ts b/src/dex/angle-transmuter/pyth.ts index e8b71965d..ba565e9da 100644 --- a/src/dex/angle-transmuter/pyth.ts +++ b/src/dex/angle-transmuter/pyth.ts @@ -44,7 +44,7 @@ export class PythSubscriber extends PartialEventSubscriber< log.data, log.topics, ); - if (this.oracleIds.indexOf(decoded.id) == -1) return null; + if (this.oracleIds.indexOf(decoded.id) === -1) return null; const _state = _.cloneDeep(state) as PythState; const expo = _state[decoded.id].expo; _state[decoded.id] = { @@ -100,6 +100,6 @@ export class PythSubscriber extends PartialEventSubscriber< public _processPrice(price: BigNumber, expo: number): number { const isNormalizerExpoNeg = expo < 0; if (isNormalizerExpoNeg) return Number(formatUnits(price, -expo)); - else return Number(parseUnits(price.toString(), expo)); + return Number(parseUnits(price.toString(), expo)); } } diff --git a/src/dex/angle-transmuter/transmuter.ts b/src/dex/angle-transmuter/transmuter.ts index 92e4fa96b..451c181c9 100644 --- a/src/dex/angle-transmuter/transmuter.ts +++ b/src/dex/angle-transmuter/transmuter.ts @@ -145,7 +145,7 @@ export class TransmuterSubscriber extends PartialEventSubscriber< multicallOutputs: MultiCallOutput[], blockNumber?: number | 'latest', ): DeepReadonly { - let transmuterState = { + const transmuterState = { collaterals: {} as { [token: string]: CollateralState; }, @@ -167,6 +167,7 @@ export class TransmuterSubscriber extends PartialEventSubscriber< this.collaterals.forEach( (collat: Address, i: number) => + // biome-ignore lint/suspicious/noAssignInExpressions: (transmuterState.collaterals[collat] = { fees: { xFeeMint: ( @@ -174,27 +175,27 @@ export class TransmuterSubscriber extends PartialEventSubscriber< 'getCollateralMintFees', multicallOutputs[indexMintFees * nbrCollaterals + i], )[0] as BigNumber[] - ).map(f => parseFloat(formatUnits(f, 9))), + ).map(f => Number.parseFloat(formatUnits(f, 9))), yFeeMint: ( TransmuterSubscriber.interface.decodeFunctionResult( 'getCollateralMintFees', multicallOutputs[indexMintFees * nbrCollaterals + i], )[1] as BigNumber[] - ).map(f => parseFloat(formatUnits(f, 9))), + ).map(f => Number.parseFloat(formatUnits(f, 9))), xFeeBurn: ( TransmuterSubscriber.interface.decodeFunctionResult( 'getCollateralBurnFees', multicallOutputs[indexBurnFees * nbrCollaterals + i], )[0] as BigNumber[] - ).map(f => parseFloat(formatUnits(f, 9))), + ).map(f => Number.parseFloat(formatUnits(f, 9))), yFeeBurn: ( TransmuterSubscriber.interface.decodeFunctionResult( 'getCollateralBurnFees', multicallOutputs[indexBurnFees * nbrCollaterals + i], )[1] as BigNumber[] - ).map(f => parseFloat(formatUnits(f, 9))), + ).map(f => Number.parseFloat(formatUnits(f, 9))), } as Fees, - stablecoinsIssued: parseFloat( + stablecoinsIssued: Number.parseFloat( formatUnits( TransmuterSubscriber.interface.decodeFunctionResult( 'getIssuedByCollateral', @@ -223,14 +224,14 @@ export class TransmuterSubscriber extends PartialEventSubscriber< 'getRedemptionFees', multicallOutputs[multicallOutputs.length - 2], )[0] as BigNumber[] - ).map(f => parseFloat(formatUnits(f, 9))); + ).map(f => Number.parseFloat(formatUnits(f, 9))); transmuterState.yRedemptionCurve = ( TransmuterSubscriber.interface.decodeFunctionResult( 'getRedemptionFees', multicallOutputs[multicallOutputs.length - 2], )[1] as BigNumber[] - ).map(f => parseFloat(formatUnits(f, 9))); - transmuterState.totalStablecoinIssued = parseFloat( + ).map(f => Number.parseFloat(formatUnits(f, 9))); + transmuterState.totalStablecoinIssued = Number.parseFloat( formatUnits( TransmuterSubscriber.interface.decodeFunctionResult( 'getTotalIssued', @@ -255,17 +256,17 @@ export class TransmuterSubscriber extends PartialEventSubscriber< const yFee: BigNumber[] = event.args.yFee; if (isMint) { state.collaterals[collateral].fees.xFeeMint = xFee.map(f => - parseFloat(formatUnits(f, 9)), + Number.parseFloat(formatUnits(f, 9)), ); state.collaterals[collateral].fees.yFeeMint = yFee.map(f => - parseFloat(formatUnits(f, 9)), + Number.parseFloat(formatUnits(f, 9)), ); } else { state.collaterals[collateral].fees.xFeeBurn = xFee.map(f => - parseFloat(formatUnits(f, 9)), + Number.parseFloat(formatUnits(f, 9)), ); state.collaterals[collateral].fees.yFeeBurn = yFee.map(f => - parseFloat(formatUnits(f, 9)), + Number.parseFloat(formatUnits(f, 9)), ); } return state; @@ -280,8 +281,12 @@ export class TransmuterSubscriber extends PartialEventSubscriber< ): Readonly | null { const xFee: BigNumber[] = event.args.xFee; const yFee: BigNumber[] = event.args.yFee; - state.xRedemptionCurve = xFee.map(f => parseFloat(formatUnits(f, 9))); - state.yRedemptionCurve = yFee.map(f => parseFloat(formatUnits(f, 9))); + state.xRedemptionCurve = xFee.map(f => + Number.parseFloat(formatUnits(f, 9)), + ); + state.yRedemptionCurve = yFee.map(f => + Number.parseFloat(formatUnits(f, 9)), + ); return state; } @@ -295,12 +300,16 @@ export class TransmuterSubscriber extends PartialEventSubscriber< const tokenIn: string = event.args.tokenIn; const tokenOut: string = event.args.tokenOut; // in case of a burn - if (tokenIn.toLowerCase() == this.agEUR.toLowerCase()) { - const amount: number = parseFloat(formatUnits(event.args.amountIn, 18)); + if (tokenIn.toLowerCase() === this.agEUR.toLowerCase()) { + const amount: number = Number.parseFloat( + formatUnits(event.args.amountIn, 18), + ); state.collaterals[tokenOut].stablecoinsIssued -= amount; state.totalStablecoinIssued -= amount; } else { - const amount: number = parseFloat(formatUnits(event.args.amountOut, 18)); + const amount: number = Number.parseFloat( + formatUnits(event.args.amountOut, 18), + ); state.collaterals[tokenIn].stablecoinsIssued += amount; state.totalStablecoinIssued += amount; } @@ -314,7 +323,9 @@ export class TransmuterSubscriber extends PartialEventSubscriber< event: ethers.utils.LogDescription, state: TransmuterState, ): Readonly | null { - const amount: number = parseFloat(formatUnits(event.args.amount, 18)); + const amount: number = Number.parseFloat( + formatUnits(event.args.amount, 18), + ); const currentStablecoinEmission = state.totalStablecoinIssued; for (const collat of Object.keys(state.collaterals)) { state.collaterals[collat].stablecoinsIssued -= @@ -353,7 +364,8 @@ export class TransmuterSubscriber extends PartialEventSubscriber< const collateral = event.args.collateral; const isIncrease: boolean = event.args.increase; const amount: number = - parseFloat(formatUnits(event.args.amount, 18)) * Number(isIncrease); + Number.parseFloat(formatUnits(event.args.amount, 18)) * + Number(isIncrease); state.totalStablecoinIssued += amount; state.collaterals[collateral].stablecoinsIssued += amount; return state; @@ -368,8 +380,8 @@ export class TransmuterSubscriber extends PartialEventSubscriber< const data: string = event.args.whitelistData; if (!state.collaterals[collateral]) state.collaterals[collateral] = {} as CollateralState; - if (status == 1) state.collaterals[collateral].whitelist.data = data; - state.collaterals[collateral].whitelist.status = status > 0 ? true : false; + if (status === 1) state.collaterals[collateral].whitelist.data = data; + state.collaterals[collateral].whitelist.status = status > 0; return state; } @@ -382,7 +394,7 @@ export class TransmuterSubscriber extends PartialEventSubscriber< const whitelistType: number = event.args.whitelistType; if (!state.isWhitelisted[whitelistType]) state.isWhitelisted[whitelistType] = new Set(); - if (status == 0 && state.isWhitelisted[whitelistType].has(who)) + if (status === 0 && state.isWhitelisted[whitelistType].has(who)) state.isWhitelisted[whitelistType].delete(who); else if (status !== 0 && !state.isWhitelisted[whitelistType].has(who)) state.isWhitelisted[whitelistType].add(who); @@ -407,29 +419,30 @@ export class TransmuterSubscriber extends PartialEventSubscriber< * Keep track of used oracles for each collaterals */ _setOracleConfig(oracleConfig: string): Oracle { - const conifgOracle = {} as Oracle; + const configOracle = {} as Oracle; const oracleConfigDecoded = TransmuterSubscriber._decodeOracleConfig(oracleConfig); - conifgOracle.oracleType = oracleConfigDecoded.oracleType; - conifgOracle.targetType = oracleConfigDecoded.targetType; - if (oracleConfigDecoded.oracleType == OracleReadType.EXTERNAL) { + configOracle.oracleType = oracleConfigDecoded.oracleType; + configOracle.targetType = oracleConfigDecoded.targetType; + configOracle.hyperparameters = oracleConfigDecoded.hyperparameters; + if (oracleConfigDecoded.oracleType === OracleReadType.EXTERNAL) { const externalOracle: Address = ethers.utils.defaultAbiCoder.decode( [`address externalOracle`], oracleConfigDecoded.oracleData, )[0]; - conifgOracle.externalOracle = externalOracle; + configOracle.externalOracle = externalOracle; } else { - conifgOracle.oracleFeed = TransmuterSubscriber._decodeOracleFeed( + configOracle.oracleFeed = TransmuterSubscriber._decodeOracleFeed( oracleConfigDecoded.oracleType, oracleConfigDecoded.oracleData, ); - conifgOracle.targetFeed = TransmuterSubscriber._decodeOracleFeed( + configOracle.targetFeed = TransmuterSubscriber._decodeOracleFeed( oracleConfigDecoded.targetType, oracleConfigDecoded.targetData, ); } - return conifgOracle; + return configOracle; } static _decodeOracleConfig(oracleConfig: string): DecodedOracleConfig { @@ -440,6 +453,7 @@ export class TransmuterSubscriber extends PartialEventSubscriber< 'uint8 targetType', 'bytes oracleData', 'bytes targetData', + 'bytes hyperparameters', ], oracleConfig, ), @@ -452,27 +466,27 @@ export class TransmuterSubscriber extends PartialEventSubscriber< readType: OracleReadType, oracleData: string, ): OracleFeed { - if (readType == OracleReadType.CHAINLINK_FEEDS) + if (readType === OracleReadType.CHAINLINK_FEEDS) return { isChainlink: true, isPyth: false, chainlink: TransmuterSubscriber._decodeChainlinkOracle(oracleData), }; - else if (readType == OracleReadType.PYTH) + if (readType === OracleReadType.PYTH) return { isChainlink: false, isPyth: true, pyth: TransmuterSubscriber._decodePythOracle(oracleData), }; - else if (readType == OracleReadType.WSTETH) + if (readType === OracleReadType.WSTETH) return { isChainlink: false, isPyth: false, otherContract: STETH }; - else if (readType == OracleReadType.CBETH) + if (readType === OracleReadType.CBETH) return { isChainlink: false, isPyth: false, otherContract: CBETH }; - else if (readType == OracleReadType.RETH) + if (readType === OracleReadType.RETH) return { isChainlink: false, isPyth: false, otherContract: RETH }; - else if (readType == OracleReadType.SFRXETH) + if (readType === OracleReadType.SFRXETH) return { isChainlink: false, isPyth: false, otherContract: SFRXETH }; - else return { isChainlink: false, isPyth: false }; + return { isChainlink: false, isPyth: false }; } static _decodeChainlinkOracle(oracleData: string): Chainlink { diff --git a/src/dex/angle-transmuter/types.ts b/src/dex/angle-transmuter/types.ts index cd2285160..e5f0afd95 100644 --- a/src/dex/angle-transmuter/types.ts +++ b/src/dex/angle-transmuter/types.ts @@ -66,33 +66,35 @@ export type AngleTransmuterData = { }; export type DexParams = { - agEUR: Token; + EURA: Token; transmuter: Address; pyth: Address; }; export enum QuoteType { - MintExactInput, - MintExactOutput, - BurnExactInput, - BurnExactOutput, + MintExactInput = 0, + MintExactOutput = 1, + BurnExactInput = 2, + BurnExactOutput = 3, } export enum OracleReadType { - CHAINLINK_FEEDS, - EXTERNAL, - NO_ORACLE, - STABLE, - WSTETH, - CBETH, - RETH, - SFRXETH, - PYTH, + CHAINLINK_FEEDS = 0, + EXTERNAL = 1, + NO_ORACLE = 2, + STABLE = 3, + WSTETH = 4, + CBETH = 5, + RETH = 6, + SFRXETH = 7, + PYTH = 8, + MAX = 9, + MORPHO_ORACLE = 10, } export enum OracleQuoteType { - UNIT, - TARGET, + UNIT = 0, + TARGET = 1, } export type Fees = { @@ -132,6 +134,7 @@ export type Oracle = { externalOracle?: Address; oracleFeed: OracleFeed; targetFeed: OracleFeed; + hyperparameters: string; }; export type DecodedOracleConfig = { @@ -139,6 +142,12 @@ export type DecodedOracleConfig = { targetType: OracleReadType; oracleData: string; targetData: string; + hyperparameters: string; +}; + +export type OracleHyperparameter = { + userDeviation: BigNumber; + burnRatioDeviation: BigNumber; }; export type DecodedStateMultiCallResultPythOracle = { diff --git a/src/dex/angle-transmuter/utils.ts b/src/dex/angle-transmuter/utils.ts index 65f9a7948..ab08c3e52 100644 --- a/src/dex/angle-transmuter/utils.ts +++ b/src/dex/angle-transmuter/utils.ts @@ -89,7 +89,7 @@ export function _quoteFees( let currentExposure = (stablecoinsIssued * BASE_9) / (otherStablecoinSupply + stablecoinsIssued); - let amount: number = 0; + let amount = 0; // Finding in which segment the current exposure to the collateral is let i = findLowerBound( @@ -149,7 +149,7 @@ export function _quoteFees( (amountToNextBreakPoint + amountFromPrevBreakPoint); } { - let amountToNextBreakPointNormalizer: number = isExact + const amountToNextBreakPointNormalizer: number = isExact ? amountToNextBreakPoint : isMint ? _invertFeeMint(amountToNextBreakPoint, (upperFees + currentFees) / 2) @@ -204,26 +204,19 @@ export function _quoteFees( } } return amount + _computeFee(quoteType, amountStable, midFee); - } else { - amountStable -= amountToNextBreakPointNormalizer; - amount += !isExact - ? amountToNextBreakPoint - : isMint - ? _invertFeeMint( - amountToNextBreakPoint, - (upperFees + currentFees) / 2, - ) - : _applyFeeBurn( - amountToNextBreakPoint, - (upperFees + currentFees) / 2, - ); - currentExposure = upperExposure * BASE_9; - ++i; - // Update for the rest of the swaps the stablecoins issued from the asset - stablecoinsIssued = isMint - ? stablecoinsIssued + amountToNextBreakPoint - : stablecoinsIssued - amountToNextBreakPoint; } + amountStable -= amountToNextBreakPointNormalizer; + amount += !isExact + ? amountToNextBreakPoint + : isMint + ? _invertFeeMint(amountToNextBreakPoint, (upperFees + currentFees) / 2) + : _applyFeeBurn(amountToNextBreakPoint, (upperFees + currentFees) / 2); + currentExposure = upperExposure * BASE_9; + ++i; + // Update for the rest of the swaps the stablecoins issued from the asset + stablecoinsIssued = isMint + ? stablecoinsIssued + amountToNextBreakPoint + : stablecoinsIssued - amountToNextBreakPoint; } } @@ -293,9 +286,8 @@ function _applyFeeMint(amountIn: number, fees: number): number { // Consider that if fees are above `BASE_12` this is equivalent to infinite fees if (castedFees >= BASE_12) throw new Error('InvalidSwap'); return (amountIn * BASE_9) / (BASE_9 + castedFees); - } else { - return (amountIn * BASE_9) / (BASE_9 - Math.abs(-fees)); } + return (amountIn * BASE_9) / (BASE_9 - Math.abs(-fees)); } function _invertFeeMint(amountOut: number, fees: number): number { @@ -304,9 +296,8 @@ function _invertFeeMint(amountOut: number, fees: number): number { // Consider that if fees are above `BASE_12` this is equivalent to infinite fees if (castedFees >= BASE_12) throw new Error('InvalidSwap'); return (amountOut * (BASE_9 + castedFees)) / BASE_9; - } else { - return (amountOut * (BASE_9 - Math.abs(-fees))) / BASE_9; } + return (amountOut * (BASE_9 - Math.abs(-fees))) / BASE_9; } function _applyFeeBurn(amountIn: number, fees: number): number { @@ -314,9 +305,8 @@ function _applyFeeBurn(amountIn: number, fees: number): number { const castedFees = fees; if (castedFees >= MAX_BURN_FEE) throw new Error('InvalidSwap'); return ((BASE_9 - castedFees) * amountIn) / BASE_9; - } else { - return ((BASE_9 + Math.abs(-fees)) * amountIn) / BASE_9; } + return ((BASE_9 + Math.abs(-fees)) * amountIn) / BASE_9; } function _invertFeeBurn(amountOut: number, fees: number): number { @@ -324,9 +314,8 @@ function _invertFeeBurn(amountOut: number, fees: number): number { const castedFees = fees; if (castedFees >= MAX_BURN_FEE) throw new Error('InvalidSwap'); return (amountOut * BASE_9) / (BASE_9 - castedFees); - } else { - return (amountOut * BASE_9) / (BASE_9 + Math.abs(-fees)); } + return (amountOut * BASE_9) / (BASE_9 + Math.abs(-fees)); } function _computeFee( @@ -336,13 +325,14 @@ function _computeFee( ): number { if (quoteType === QuoteType.MintExactInput) { return _applyFeeMint(amount, fees); - } else if (quoteType === QuoteType.MintExactOutput) { + } + if (quoteType === QuoteType.MintExactOutput) { return _invertFeeMint(amount, fees); - } else if (quoteType === QuoteType.BurnExactInput) { + } + if (quoteType === QuoteType.BurnExactInput) { return _applyFeeBurn(amount, fees); - } else { - return _invertFeeBurn(amount, fees); } + return _invertFeeBurn(amount, fees); } export function filterDictionaryOnly( @@ -352,7 +342,7 @@ export function filterDictionaryOnly( Object.keys(obj).map(key => { if (Object.prototype.hasOwnProperty.call(obj, key)) { // Skip numeric keys to exclude array-like properties - if (isNaN(Number(key))) { + if (Number.isNaN(Number(key))) { result[key as keyof T] = obj[key]; } } diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index 639677bc4..1f3e050b2 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -315,7 +315,7 @@ export const Tokens: { address: '0xc411db5f5eb3f7d552f9b8454b2d74097ccde6e3', decimals: 6, }, - agEUR: { + EURA: { address: '0x1a7e4e63778B4f12a199C062f3eFdD288afCBce8', decimals: 18, }, @@ -323,10 +323,26 @@ export const Tokens: { address: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c', decimals: 6, }, + bERNX: { + address: '0x3f95AA88dDbB7D9D484aa3D482bf0a80009c52c9', + decimals: 18, + }, bC3M: { address: '0x2F123cF3F37CE3328CC9B5b8415f9EC5109b45e7', decimals: 18, }, + USDA: { + address: '0x0000206329b97DB379d5E1Bf586BbDB969C63274', + decimals: 18, + }, + bIB01: { + address: '0xCA30c93B02514f86d5C86a6e375E3A330B435Fb5', + decimals: 18, + }, + steakUSDC: { + address: '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB', + decimals: 18, + }, GHO: { address: '0x40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f', decimals: 18, @@ -1088,6 +1104,9 @@ export const Holders: { PSP: '0xE5E5440a1CE69C5cf67BFFA74d185e57c31b43E5', EUROC: '0x64AE5802620398143FC7113037769175F74825Ea', bC3M: '0x5f9F41497f9e11fd7D4c4B067413199682eE2CFF', + bERNX: '0x5F7A4c11bde4f218f0025Ef444c369d838ffa2aD', + bIB01: '0x5F7A4c11bde4f218f0025Ef444c369d838ffa2aD', + steakUSDC: '0xC977d218Fde6A39c7aCE71C8243545c276B48931', crvUSD: '0xA920De414eA4Ab66b97dA1bFE9e6EcA7d4219635', GHO: '0x844Dc85EdD8492A56228D293cfEbb823EF3E10EC', wibBTC: '0xFbdCA68601f835b27790D98bbb8eC7f05FDEaA9B', @@ -1095,7 +1114,8 @@ export const Holders: { POL: '0x57B6Ad484ccdd902C4419424bA648ba6Ed45dc68', SDEX: '0xB0470cF15B22a6A32c49a7C20E3821B944A76058', frxETH: '0x9df2322bdAEC46627100C999E6dDdD27837fec6e', - agEUR: '0xa116f421ff82A9704428259fd8CC63347127B777', + EURA: '0xa116f421ff82A9704428259fd8CC63347127B777', + USDA: '0xd9Da13DE745bfa50FFAAFD0a531B92f0511B72Cf', }, [Network.ROPSTEN]: { ETH: '0x43262A12d8610AA70C15DbaeAC321d51613c9071', diff --git a/yarn.lock b/yarn.lock index 6bef98c33..a76c222a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -346,6 +346,60 @@ resolved "https://registry.yarnpkg.com/@bgd-labs/aave-address-book/-/aave-address-book-2.21.1.tgz#004aa244d715d785079029f6b61d5ece6bcff563" integrity sha512-q22AThlSRgEgRkwWiK1ts13oO3epZkARmdJzhezv6Fv3PNa5M95uoqABjNldWmpUyadhy2UtujGzXdOQ6MSS9Q== +"@biomejs/biome@^1.6.3": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.7.0.tgz#b12973c41b0a9c62374807b988cf984b20d85f44" + integrity sha512-mejiRhnAq6UrXtYvjWJUKdstcT58n0/FfKemFf3d2Ou0HxOdS88HQmWtQ/UgyZvOEPD572YbFTb6IheyROpqkw== + optionalDependencies: + "@biomejs/cli-darwin-arm64" "1.7.0" + "@biomejs/cli-darwin-x64" "1.7.0" + "@biomejs/cli-linux-arm64" "1.7.0" + "@biomejs/cli-linux-arm64-musl" "1.7.0" + "@biomejs/cli-linux-x64" "1.7.0" + "@biomejs/cli-linux-x64-musl" "1.7.0" + "@biomejs/cli-win32-arm64" "1.7.0" + "@biomejs/cli-win32-x64" "1.7.0" + +"@biomejs/cli-darwin-arm64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.7.0.tgz#a247f1c4cf3f0366e1f0ccbfc8cb226f1237b354" + integrity sha512-12TaeaKHU4SAZt0fQJ2bYk1jUb4foope7LmgDE5p3c0uMxd3mFkg1k7G721T+K6UHYULcSOQDsNNM8DhYi8Irg== + +"@biomejs/cli-darwin-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.7.0.tgz#ab4b3826ca76229e5e6cda468855c24efbc85dc5" + integrity sha512-6Qq1BSIB0cpp0cQNqO/+EiUV7FE3jMpF6w7+AgIBXp0oJxUWb2Ff0RDZdO9bfzkimXD58j0vGpNHMGnCcjDV2Q== + +"@biomejs/cli-linux-arm64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.7.0.tgz#8a479f3a1be968dc685b8c0e60e32521732e9867" + integrity sha512-pwIY80nU7SAxrVVZ6HD9ah1pruwh9ZqlSR0Nvbg4ZJqQa0POhiB+RJx7+/1Ml2mTZdrl8kb/YiwQpD16uwb5wg== + +"@biomejs/cli-linux-arm64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.7.0.tgz#c1edaa4ddc071400bddec6cf180f9248958b66cb" + integrity sha512-GwSci7xBJ2j1CrdDXDUVXnUtrvypEz/xmiYPpFeVdlX5p95eXx+7FekPPbJfhGGw5WKSsKZ+V8AAlbN+kUwJWw== + +"@biomejs/cli-linux-x64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.7.0.tgz#ff37c3306d719120139cefd4ab2e10912c0414f2" + integrity sha512-KzCA0mW4LSbCd7XZWaEJvTOTTBjfJoVEXkfq1fsXxww1HB+ww5PGMbhbIcbYCsj2CTJUifeD5hOkyuBVppU1xQ== + +"@biomejs/cli-linux-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.7.0.tgz#c2de603cbfeaa1db268b396adc280df1b540386f" + integrity sha512-1y+odKQsyHcw0JCGRuqhbx7Y6jxOVSh4lGIVDdJxW1b55yD22DY1kcMEfhUte6f95OIc2uqfkwtiI6xQAiZJdw== + +"@biomejs/cli-win32-arm64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.7.0.tgz#feb48c1a8a3522083b4924f194b5febe06fd94fe" + integrity sha512-AvLDUYZBpOUFgS/mni4VruIoVV3uSGbKSkZQBPXsHgL0w4KttLll3NBrVanmWxOHsom6C6ocHLyfAY8HUc8TXg== + +"@biomejs/cli-win32-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.7.0.tgz#cc95cde067bd692339e6b7b92d049c25c023c2b4" + integrity sha512-Pylm00BAAuLVb40IH9PC17432BTsY8K4pSUvhvgR1eaalnMaD6ug9SYJTTzKDbT6r24MPAGCTiSZERyhGkGzFQ== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"