From c5f4f87d5d9bb8a4618f2059328268b41c5a93e2 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Mon, 25 Mar 2024 15:07:19 +0000 Subject: [PATCH 01/26] feat: add OneInch protocols to quote response and map to QuoteData routes field --- pnpm-lock.yaml | 3 + sdk/sdk-common/package.json | 3 +- sdk/sdk-common/src/simulation/Simulation.ts | 3 + sdk/sdk-common/src/simulation/Steps.ts | 4 ++ .../src/implementation/helpers/index.ts | 65 ------------------- .../reducer/depositBorrowReducer.ts | 2 +- .../simulator-engine/reducer/swapReducer.ts | 14 +++- .../simulator-engine/simulator.ts | 15 ++--- .../implementation/strategies/Refinance.ts | 9 +-- .../src/implementation/utils/BalanceUtils.ts | 29 +++++++++ .../implementation/utils/SimulatorUtils.ts | 38 +++++++++++ .../src/implementation/utils/index.ts | 2 + .../src/interfaces/simulation.ts | 1 + sdk/simulator-service/tests/simulator.test.ts | 6 +- sdk/swap-common/src/types/QuoteData.ts | 15 ++++- sdk/swap-common/src/types/SwapData.ts | 1 + sdk/swap-common/src/types/index.ts | 2 +- .../oneinch/OneInchSwapProvider.ts | 16 ++++- .../src/implementation/oneinch/types.ts | 14 +++- 19 files changed, 151 insertions(+), 91 deletions(-) delete mode 100644 sdk/simulator-service/src/implementation/helpers/index.ts create mode 100644 sdk/simulator-service/src/implementation/utils/BalanceUtils.ts create mode 100644 sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts create mode 100644 sdk/simulator-service/src/implementation/utils/index.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aedd5303ed..b4e044fb01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -850,6 +850,9 @@ importers: '@summerfi/common': specifier: workspace:* version: link:../../packages/common + '@summerfi/swap-common': + specifier: workspace:* + version: link:../swap-common bignumber.js: specifier: 9.0.1 version: 9.0.1 diff --git a/sdk/sdk-common/package.json b/sdk/sdk-common/package.json index fef5a4cf61..a984dddc0e 100644 --- a/sdk/sdk-common/package.json +++ b/sdk/sdk-common/package.json @@ -21,7 +21,8 @@ "bignumber.js": "9.0.1", "superjson": "^1.13.3", "viem": "^2.2.0", - "@summerfi/common": "workspace:*" + "@summerfi/common": "workspace:*", + "@summerfi/swap-common": "workspace:*" }, "devDependencies": { "@summerfi/eslint-config": "workspace:*", diff --git a/sdk/sdk-common/src/simulation/Simulation.ts b/sdk/sdk-common/src/simulation/Simulation.ts index 5bc82fc9a5..0650ac7904 100644 --- a/sdk/sdk-common/src/simulation/Simulation.ts +++ b/sdk/sdk-common/src/simulation/Simulation.ts @@ -9,7 +9,10 @@ import type { Steps } from './Steps' export interface Simulation { simulationType: T sourcePosition?: Position // TODO figure what do to when opening position (empty position or optional) + /* The output of the simulation. The simulated position is the target position */ targetPosition: Position + /* The details of any swaps required as part of the simulation */ + swaps: any steps: Steps[] // TODO: OPEN QUESTION: where errors and warnings and info messages? } diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index d6304616e3..55e329fdf1 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -4,6 +4,8 @@ import { Token } from '../common/implementation/Token' import { TokenAmount } from '../common/implementation/TokenAmount' import { FlashloanProvider, SimulationSteps } from './Enums' import { ReferenceableField, ValueReference } from './ValueReference' +import { SwapProviderType } from '@summerfi/swap-common/enums' +import { SwapRoute } from "@summerfi/swap-common/types" export interface Step { type: T @@ -58,6 +60,8 @@ export interface SwapStep extends Step< SimulationSteps.Swap, { + provider: SwapProviderType + routes: SwapRoute[] fromTokenAmount: TokenAmount toTokenAmount: TokenAmount slippage: Percentage diff --git a/sdk/simulator-service/src/implementation/helpers/index.ts b/sdk/simulator-service/src/implementation/helpers/index.ts deleted file mode 100644 index 863f1943d1..0000000000 --- a/sdk/simulator-service/src/implementation/helpers/index.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { TokenAmount, type Token } from '@summerfi/sdk-common/common' -import type { - ReferenceableField, - SimulationStrategy, - ValueReference, -} from '@summerfi/sdk-common/simulation' -import type { Tail } from '../../interfaces/helperTypes' - -export function makeStrategy(strategy: T): T { - return strategy -} - -export function isValueReference(value: ReferenceableField): value is ValueReference { - return ( - (value as ValueReference).path !== undefined && - (value as ValueReference).estimatedValue !== undefined - ) -} - -export function getReferencedValue(referencableValue: ReferenceableField): T { - if (isValueReference(referencableValue)) { - return referencableValue.estimatedValue - } - return referencableValue -} - -export function getTokenBalance(token: Token, balances: Record): TokenAmount { - return balances[token.address.value] || TokenAmount.createFrom({ amount: '0', token }) -} - -export function addBalance( - amount: TokenAmount, - balance: Record, -): Record { - return { - ...balance, - [amount.token.address.value]: balance[amount.token.address.value] - ? balance[amount.token.address.value].add(amount) - : amount, - } -} - -export function subtractBalance( - amount: TokenAmount, - balance: Record, -): Record { - return { - ...balance, - [amount.token.address.value]: balance[amount.token.address.value] - ? balance[amount.token.address.value].subtract(amount) - : TokenAmount.createFrom({ amount: amount.toBN().negated().toString(), token: amount.token }), - } -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function tail(arr: T): Tail { - const [, ...rest] = arr - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return rest as any as Tail -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function head(arr: T): T[0] { - return arr[0] -} diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts index 428025d4ba..ebb8f7eb4a 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts @@ -1,6 +1,6 @@ import { borrowFromPosition, depositToPosition } from '@summerfi/sdk-common/common/utils' import { steps } from '@summerfi/sdk-common/simulation' -import { addBalance, getReferencedValue, subtractBalance } from '../../helpers' +import { addBalance, subtractBalance, getReferencedValue } from '../../utils' import { SimulationState } from '../../../interfaces/simulation' export function depositBorrowReducer( diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 2c0fd80529..2a4cbca449 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -5,12 +5,24 @@ import { SimulationState } from '../../../interfaces/simulation' export function swapReducer(step: steps.SwapStep, state: SimulationState): SimulationState { const balanceWithoutFromToken = subtractBalance(step.inputs.fromTokenAmount, state.balances) const balanceWithToToken = addBalance(step.outputs.receivedAmount, balanceWithoutFromToken) + step.inputs.fromTokenAmount return { ...state, steps: { ...state.steps, [step.name]: step, }, + swaps: { + ...state.swaps, + [step.name]: { + provider: step.inputs.provider, + routes: step.inputs.routes, + fromTokenAmount: step.inputs.fromTokenAmount, + toTokenAmount: step.inputs.toTokenAmount, + slippage: step.inputs.slippage, + fee: step.inputs.fee + } + }, balances: balanceWithToToken, } -} +} \ No newline at end of file diff --git a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts index eeb8dad7a7..65f63f8a18 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts @@ -1,7 +1,7 @@ import type { SimulationState } from '../../interfaces/simulation' import type { Tail } from '../../interfaces/helperTypes' import type { NextFunction } from '../../interfaces' -import { head, tail } from '../helpers' +import { head, tail } from '../utils' import { processStepOutput } from './stepProcessor/stepOutputProcessors' import { stateReducer } from './reducer/stateReducers' import type { SimulationStrategy } from '@summerfi/sdk-common/simulation' @@ -20,7 +20,7 @@ export class Simulator< private constructor( schema: Strategy, originalSchema: SimulationStrategy, - state: SimulationState = { balances: {}, positions: {}, steps: {} }, + state: SimulationState = { swaps: [], balances: {}, positions: {}, steps: {} }, nextArray: Readonly = [] as unknown as NextArray, ) { this.schema = schema @@ -30,14 +30,14 @@ export class Simulator< } static create(schema: S) { - // The second argument is the same as from the first schema we will substract steps - // with each next step added and we also need to keep the original schema for future reference + // The second argument is the same as from the first schema we will subtract steps + // with each next step added we also need to keep the original schema for future reference return new Simulator(schema, schema) } public async run(): Promise { for (let i = 0; i < this.nextArray.length; i++) { - const proccesesedStepSchema = this.originalSchema[i] + const processedStepSchema = this.originalSchema[i] const getReference = (path: [string, string]) => { const [stepName, output] = path const step: Maybe = this.state.steps[stepName] @@ -76,13 +76,12 @@ export class Simulator< this.state = stateReducer(fullStep, this.state) } - if (nextStep.skip === true && proccesesedStepSchema.optional === false) { + if (nextStep.skip === true && processedStepSchema.optional === false) { throw new Error(`Step is required: ${nextStep.type}`) } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return this.state as any + return this.state } public next( diff --git a/sdk/simulator-service/src/implementation/strategies/Refinance.ts b/sdk/simulator-service/src/implementation/strategies/Refinance.ts index da7e5d9ded..5e7b733a67 100644 --- a/sdk/simulator-service/src/implementation/strategies/Refinance.ts +++ b/sdk/simulator-service/src/implementation/strategies/Refinance.ts @@ -4,7 +4,7 @@ import { SimulationSteps, SimulationType, } from '@summerfi/sdk-common/simulation' -import { getReferencedValue, makeStrategy } from '../helpers' +import { getReferencedValue, makeStrategy } from '../utils' import { Simulator } from '../simulator-engine' import { Percentage, TokenAmount } from '@summerfi/sdk-common/common' import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' @@ -52,7 +52,7 @@ export interface RefinanceDependencies { getSummerFee: () => Percentage } -export async function refinaceLendingToLending( +export async function refinanceLendingToLending( args: RefinanceParameters, dependencies: RefinanceDependencies, ): Promise> { @@ -146,9 +146,6 @@ export async function refinaceLendingToLending( .next(async (ctx) => ({ name: 'DebtSwap', type: SimulationSteps.Swap, - // inputs: await dependencies.getQuote({ - // from: getReferencedValue(ctx.getReference(['DepositBorrow', 'borrowAmount'])), - // to: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, inputs: { ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: args.position.pool.protocol.chainInfo, @@ -180,7 +177,6 @@ export async function refinaceLendingToLending( })) .run() - // TODO: I think simulation should return the simulation position as a preperty targetPosition for easy discoverability const targetPosition = Object.values(simulation.positions).find( (p) => p.pool.protocol === args.targetPool.protocol, ) @@ -193,6 +189,7 @@ export async function refinaceLendingToLending( simulationType: SimulationType.Refinance, sourcePosition: args.position, targetPosition, + swaps: [], steps: Object.values(simulation.steps), } } diff --git a/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts b/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts new file mode 100644 index 0000000000..2290281a0c --- /dev/null +++ b/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts @@ -0,0 +1,29 @@ +import { TokenAmount, type Token } from '@summerfi/sdk-common/common' + +export function getTokenBalance(token: Token, balances: Record): TokenAmount { + return balances[token.address.value] || TokenAmount.createFrom({ amount: '0', token }) +} + +export function addBalance( + amount: TokenAmount, + balance: Record, +): Record { + return { + ...balance, + [amount.token.address.value]: balance[amount.token.address.value] + ? balance[amount.token.address.value].add(amount) + : amount, + } +} + +export function subtractBalance( + amount: TokenAmount, + balance: Record, +): Record { + return { + ...balance, + [amount.token.address.value]: balance[amount.token.address.value] + ? balance[amount.token.address.value].subtract(amount) + : TokenAmount.createFrom({ amount: amount.toBN().negated().toString(), token: amount.token }), + } +} \ No newline at end of file diff --git a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts new file mode 100644 index 0000000000..0b23815a10 --- /dev/null +++ b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts @@ -0,0 +1,38 @@ +import type { + ReferenceableField, + SimulationStrategy, + ValueReference, +} from '@summerfi/sdk-common/simulation' +import type { Tail } from '../../interfaces/helperTypes' + +export function makeStrategy(strategy: T): T { + return strategy +} + +export function isValueReference(value: ReferenceableField): value is ValueReference { + return ( + (value as ValueReference).path !== undefined && + (value as ValueReference).estimatedValue !== undefined + ) +} + +export function getReferencedValue(referenceableValue: ReferenceableField): T { + if (isValueReference(referenceableValue)) { + return referenceableValue.estimatedValue + } + return referenceableValue +} + + + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function tail(arr: T): Tail { + const [, ...rest] = arr + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return rest as any as Tail +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function head(arr: T): T[0] { + return arr[0] +} diff --git a/sdk/simulator-service/src/implementation/utils/index.ts b/sdk/simulator-service/src/implementation/utils/index.ts new file mode 100644 index 0000000000..e4f8e159b9 --- /dev/null +++ b/sdk/simulator-service/src/implementation/utils/index.ts @@ -0,0 +1,2 @@ +export { getTokenBalance, addBalance, subtractBalance } from './BalanceUtils' +export { makeStrategy, isValueReference, getReferencedValue, tail, head } from './SimulatorUtils' \ No newline at end of file diff --git a/sdk/simulator-service/src/interfaces/simulation.ts b/sdk/simulator-service/src/interfaces/simulation.ts index 3346eaceae..2e24ba5d56 100644 --- a/sdk/simulator-service/src/interfaces/simulation.ts +++ b/sdk/simulator-service/src/interfaces/simulation.ts @@ -2,6 +2,7 @@ import type { steps } from '@summerfi/sdk-common/simulation' import type { TokenAmount, Position } from '@summerfi/sdk-common/common' export interface SimulationState { + swaps: any balances: Record positions: Record steps: Record diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index e188b7c160..bda396a7c4 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -1,5 +1,5 @@ import { Percentage } from '@summerfi/sdk-common/common' -import { refinaceLendingToLending } from '../src/implementation/strategies' +import { refinanceLendingToLending } from '../src/implementation/strategies' import { Simulation, SimulationSteps, SimulationType } from '@summerfi/sdk-common/simulation' import { otherTestCollateral, @@ -14,7 +14,7 @@ describe('Refinance', () => { describe('to the position with the same collateral and debt (no swaps)', () => { let simulation: Simulation beforeAll(async () => { - simulation = await refinaceLendingToLending( + simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPool, @@ -60,7 +60,7 @@ describe('Refinance', () => { describe.skip('to the position with the different collateral and debt (with swaps)', () => { let simulation: Simulation beforeAll(async () => { - simulation = await refinaceLendingToLending( + simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, diff --git a/sdk/swap-common/src/types/QuoteData.ts b/sdk/swap-common/src/types/QuoteData.ts index 2a0679e459..7541323f7d 100644 --- a/sdk/swap-common/src/types/QuoteData.ts +++ b/sdk/swap-common/src/types/QuoteData.ts @@ -1,4 +1,4 @@ -import { TokenAmount } from '@summerfi/sdk-common/common' +import { TokenAmount, Percentage, Address } from '@summerfi/sdk-common/common' import type { SwapProviderType } from '../enums/SwapProviderType' /** @@ -11,4 +11,17 @@ export type QuoteData = { fromTokenAmount: TokenAmount toTokenAmount: TokenAmount estimatedGas: string + /* Providers can provide multiple routes */ + routes: SwapRoute[] +} + +export type SwapRoute = SwapHop[] + +type SwapHop = SwapHopPart[] + +type SwapHopPart = { + name: string, + part: Percentage + fromTokenAddress: Address + toTokenAddress: Address } diff --git a/sdk/swap-common/src/types/SwapData.ts b/sdk/swap-common/src/types/SwapData.ts index 61b1e5ed57..5d98fe6ef8 100644 --- a/sdk/swap-common/src/types/SwapData.ts +++ b/sdk/swap-common/src/types/SwapData.ts @@ -13,5 +13,6 @@ export type SwapData = { calldata: HexData targetContract: Address value: string + /* The gas price for the swap portion of the t/x only */ gasPrice: string } diff --git a/sdk/swap-common/src/types/index.ts b/sdk/swap-common/src/types/index.ts index 8a053b1849..3f6a4ecd4e 100644 --- a/sdk/swap-common/src/types/index.ts +++ b/sdk/swap-common/src/types/index.ts @@ -1,2 +1,2 @@ export type { SwapData } from './SwapData' -export type { QuoteData } from './QuoteData' +export type { QuoteData, SwapRoute } from './QuoteData' diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index 2e7c9ed4ee..ebe6dd9f9a 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -1,19 +1,19 @@ import { ISwapProvider } from '@summerfi/swap-common/interfaces' import { SwapProviderType } from '@summerfi/swap-common/enums' -import { SwapData, QuoteData } from '@summerfi/swap-common/types' +import { SwapData, SwapRoute, QuoteData } from '@summerfi/swap-common/types' import { OneInchAuthHeader, OneInchAuthHeaderKey, OneInchQuoteResponse, OneInchSwapProviderConfig, - OneInchSwapResponse, + OneInchSwapResponse, OneInchSwapRoute, } from './types' import { HexData } from '@summerfi/sdk-common/common/aliases' import fetch from 'node-fetch' import { type ChainInfo, TokenAmount, - type Percentage, + Percentage, type Token, Address, } from '@summerfi/sdk-common/common' @@ -106,6 +106,7 @@ export class OneInchSwapProvider implements ISwapProvider { token: params.toToken, amount: responseData.toTokenAmount, }), + routes: this._extractSwapRoutes(responseData.protocols), estimatedGas: responseData.estimatedGas, } } @@ -152,4 +153,13 @@ export class OneInchSwapProvider implements ISwapProvider { return `${this._apiUrl}/${this._version}/${chainId}/quote?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&protocols=${protocolsParam}` } + + private _extractSwapRoutes(protocols: OneInchSwapRoute[]): SwapRoute[] { + return protocols.map(route => (route.map(hop => hop.map(hopPart => ({ + name: hopPart.name, + part: Percentage.createFrom({percentage: hopPart.part}), + fromTokenAddress: Address.createFrom({value: hopPart.fromTokenAddress as HexData}), + toTokenAddress: Address.createFrom({value: hopPart.toTokenAddress as HexData}) + }))))) + } } diff --git a/sdk/swap-service/src/implementation/oneinch/types.ts b/sdk/swap-service/src/implementation/oneinch/types.ts index 6642b6b91d..fd4867a989 100644 --- a/sdk/swap-service/src/implementation/oneinch/types.ts +++ b/sdk/swap-service/src/implementation/oneinch/types.ts @@ -28,8 +28,20 @@ export interface OneInchSwapResponse extends OneInchBaseResponse { } export interface OneInchQuoteResponse extends OneInchBaseResponse { - protocols: unknown + /* One Inch can provide multiple routes */ + protocols: OneInchSwapRoute[] fromTokenAmount: string toTokenAmount: string estimatedGas: string } + +export type OneInchSwapRoute = OneInchSwapHop[] + +type OneInchSwapHop = OneInchSwapHopPart[] + +type OneInchSwapHopPart = { + name: string, + part: number + fromTokenAddress: string + toTokenAddress: string +} \ No newline at end of file From ed8a1f44f78bde6ac87a0fc31b0d381c7a523958 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Mon, 25 Mar 2024 15:15:50 +0000 Subject: [PATCH 02/26] feat: add swaps to Simulate state and SwapReducer --- sdk/sdk-common/src/simulation/Steps.ts | 1 + .../simulator-engine/reducer/swapReducer.ts | 7 ++++--- sdk/simulator-service/src/interfaces/simulation.ts | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index 55e329fdf1..f9cc9e82e6 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -64,6 +64,7 @@ export interface SwapStep routes: SwapRoute[] fromTokenAmount: TokenAmount toTokenAmount: TokenAmount + estimatedGas: string slippage: Percentage fee: Percentage }, diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 2a4cbca449..1407e689c4 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -1,11 +1,11 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { addBalance, subtractBalance } from '../../helpers' +import { addBalance, subtractBalance } from '../../utils' import { SimulationState } from '../../../interfaces/simulation' export function swapReducer(step: steps.SwapStep, state: SimulationState): SimulationState { const balanceWithoutFromToken = subtractBalance(step.inputs.fromTokenAmount, state.balances) const balanceWithToToken = addBalance(step.outputs.receivedAmount, balanceWithoutFromToken) - step.inputs.fromTokenAmount + return { ...state, steps: { @@ -20,7 +20,8 @@ export function swapReducer(step: steps.SwapStep, state: SimulationState): Simul fromTokenAmount: step.inputs.fromTokenAmount, toTokenAmount: step.inputs.toTokenAmount, slippage: step.inputs.slippage, - fee: step.inputs.fee + fee: step.inputs.fee, + estimatedGas: step.inputs.estimatedGas } }, balances: balanceWithToToken, diff --git a/sdk/simulator-service/src/interfaces/simulation.ts b/sdk/simulator-service/src/interfaces/simulation.ts index 2e24ba5d56..37acfc321c 100644 --- a/sdk/simulator-service/src/interfaces/simulation.ts +++ b/sdk/simulator-service/src/interfaces/simulation.ts @@ -1,9 +1,15 @@ import type { steps } from '@summerfi/sdk-common/simulation' -import type { TokenAmount, Position } from '@summerfi/sdk-common/common' +import type { TokenAmount, Position, Percentage } from '@summerfi/sdk-common/common' +import {QuoteData} from "@summerfi/swap-common/types"; export interface SimulationState { - swaps: any + swaps: Record balances: Record positions: Record steps: Record } + +type SimulatedSwap = QuoteData & { + slippage: Percentage + fee: Percentage +} From cc1d61e87439997de44624a5335aedf0c26bf17e Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Mon, 25 Mar 2024 15:17:58 +0000 Subject: [PATCH 03/26] feat: add swaps to Refinance strategy output --- .../src/implementation/strategies/Refinance.ts | 5 +---- .../src/implementation/utils/SimulatorUtils.ts | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/sdk/simulator-service/src/implementation/strategies/Refinance.ts b/sdk/simulator-service/src/implementation/strategies/Refinance.ts index 5e7b733a67..dceb986728 100644 --- a/sdk/simulator-service/src/implementation/strategies/Refinance.ts +++ b/sdk/simulator-service/src/implementation/strategies/Refinance.ts @@ -118,9 +118,6 @@ export async function refinanceLendingToLending( toToken: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, })), - // inputs: await dependencies.getQuote({ - // from: args.position.collateralAmount, - // to: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, slippage: args.slippage, fee: dependencies.getSummerFee(), }, @@ -189,7 +186,7 @@ export async function refinanceLendingToLending( simulationType: SimulationType.Refinance, sourcePosition: args.position, targetPosition, - swaps: [], + swaps: simulation.swaps, steps: Object.values(simulation.steps), } } diff --git a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts index 0b23815a10..9facfba1f5 100644 --- a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts +++ b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts @@ -23,8 +23,6 @@ export function getReferencedValue(referenceableValue: ReferenceableField) return referenceableValue } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any export function tail(arr: T): Tail { const [, ...rest] = arr From 1fa7f2f68549b59b757c8838718c10b17128063c Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Mon, 25 Mar 2024 22:33:33 +0000 Subject: [PATCH 04/26] refactor: clear up imports and deps issues --- .../src/interfaces/IOrderPlanner.ts | 4 +- .../refinance/RefinanceParameters.ts | 3 + sdk/sdk-common/src/simulation/Simulation.ts | 7 +- .../src/swap/Enums.ts} | 2 +- .../src/swap}/QuoteData.ts | 2 +- sdk/sdk-common/src/swap/SimulatedSwapData.ts | 7 + .../types => sdk-common/src/swap}/SwapData.ts | 2 +- sdk/sdk-common/src/swap/index.ts | 4 + sdk/sdk-server/tests/utils/TestUtils.ts | 1 + .../src/implementation/index.ts | 1 - .../reducer/depositBorrowReducer.ts | 6 +- .../reducer/flashloanReducer.ts | 8 +- .../reducer/paybackWithdrawReducer.ts | 8 +- .../reducer/pullTokenReducer.ts | 8 +- .../reducer/repayFlashloanReducer.ts | 8 +- .../reducer/returnFundsReducer.ts | 8 +- .../simulator-engine/reducer/stateReducers.ts | 4 +- .../simulator-engine/reducer/swapReducer.ts | 4 +- .../simulator-engine/simulator.ts | 8 +- .../depositBorrowOutputProcessor.ts | 2 +- .../paybackWithdrawOutputProcessor.ts | 2 +- .../implementation/strategies/Refinance.ts | 192 ------------------ .../src/implementation/strategies/index.ts | 1 - sdk/simulator-service/src/index.ts | 1 - .../src/interfaces/simulation.ts | 15 +- .../src/strategies/refinance/Refinance.ts | 153 ++++++++++++++ .../src/strategies/refinance/Strategy.ts | 35 ++++ .../src/strategies/refinance/Types.ts | 7 + .../src/strategies/refinance/index.ts | 3 + sdk/swap-common/src/enums/index.ts | 1 - .../src/interfaces/ISwapManager.ts | 7 +- .../src/interfaces/ISwapProvider.ts | 6 +- sdk/swap-common/src/types/index.ts | 2 - 33 files changed, 269 insertions(+), 253 deletions(-) rename sdk/{swap-common/src/enums/SwapProviderType.ts => sdk-common/src/swap/Enums.ts} (83%) rename sdk/{swap-common/src/types => sdk-common/src/swap}/QuoteData.ts (90%) create mode 100644 sdk/sdk-common/src/swap/SimulatedSwapData.ts rename sdk/{swap-common/src/types => sdk-common/src/swap}/SwapData.ts (88%) create mode 100644 sdk/sdk-common/src/swap/index.ts delete mode 100644 sdk/simulator-service/src/implementation/strategies/Refinance.ts delete mode 100644 sdk/simulator-service/src/implementation/strategies/index.ts delete mode 100644 sdk/simulator-service/src/index.ts create mode 100644 sdk/simulator-service/src/strategies/refinance/Refinance.ts create mode 100644 sdk/simulator-service/src/strategies/refinance/Strategy.ts create mode 100644 sdk/simulator-service/src/strategies/refinance/Types.ts create mode 100644 sdk/simulator-service/src/strategies/refinance/index.ts delete mode 100644 sdk/swap-common/src/enums/index.ts delete mode 100644 sdk/swap-common/src/types/index.ts diff --git a/sdk/order-planner-common/src/interfaces/IOrderPlanner.ts b/sdk/order-planner-common/src/interfaces/IOrderPlanner.ts index e9303eb34c..e21a387f54 100644 --- a/sdk/order-planner-common/src/interfaces/IOrderPlanner.ts +++ b/sdk/order-planner-common/src/interfaces/IOrderPlanner.ts @@ -1,6 +1,6 @@ import { Deployment } from '@summerfi/deployment-utils' import { Order, type IPositionsManager } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { ISwapManager } from '@summerfi/swap-common/interfaces' import { ActionBuildersMap } from '../builders/Types' import { Maybe } from '@summerfi/sdk-common/common' @@ -11,7 +11,7 @@ export interface IOrderPlanner { buildOrder(params: { user: IUser positionsManager: IPositionsManager - simulation: Simulation + simulation: ISimulation actionBuildersMap: ActionBuildersMap deployment: Deployment swapManager: ISwapManager diff --git a/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts b/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts index 72d039c615..7d1b6c0513 100644 --- a/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts +++ b/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts @@ -1,5 +1,6 @@ import type { Percentage } from '../../../common/implementation/Percentage' import type { Position } from '../../../common/implementation/Position' +import type { Address } from '../../../common/implementation/Address' import type { LendingPool } from '../../../protocols/implementation/LendingPool' /** @@ -9,6 +10,8 @@ import type { LendingPool } from '../../../protocols/implementation/LendingPool' export interface RefinanceParameters { position: Position targetPool: LendingPool + targetCollateral: Address + targetDebt: Address slippage: Percentage } diff --git a/sdk/sdk-common/src/simulation/Simulation.ts b/sdk/sdk-common/src/simulation/Simulation.ts index 0650ac7904..7449309b16 100644 --- a/sdk/sdk-common/src/simulation/Simulation.ts +++ b/sdk/sdk-common/src/simulation/Simulation.ts @@ -1,18 +1,19 @@ import { Position } from '../common/implementation/Position' +import {SimulatedSwapData} from "../swap"; import type { SimulationType } from './Enums' import type { Steps } from './Steps' /** - * @interface Simulation + * @interface ISimulation * @description Simulation of a position. Specialized into the different types of simulations needed */ -export interface Simulation { +export interface ISimulation { simulationType: T sourcePosition?: Position // TODO figure what do to when opening position (empty position or optional) /* The output of the simulation. The simulated position is the target position */ targetPosition: Position /* The details of any swaps required as part of the simulation */ - swaps: any + swaps: SimulatedSwapData[] steps: Steps[] // TODO: OPEN QUESTION: where errors and warnings and info messages? } diff --git a/sdk/swap-common/src/enums/SwapProviderType.ts b/sdk/sdk-common/src/swap/Enums.ts similarity index 83% rename from sdk/swap-common/src/enums/SwapProviderType.ts rename to sdk/sdk-common/src/swap/Enums.ts index d4efdcdd6b..254dd779ad 100644 --- a/sdk/swap-common/src/enums/SwapProviderType.ts +++ b/sdk/sdk-common/src/swap/Enums.ts @@ -3,5 +3,5 @@ * @description Represents the different swap providers */ export enum SwapProviderType { - OneInch = 'OneInch', + OneInch = 'OneInch', } diff --git a/sdk/swap-common/src/types/QuoteData.ts b/sdk/sdk-common/src/swap/QuoteData.ts similarity index 90% rename from sdk/swap-common/src/types/QuoteData.ts rename to sdk/sdk-common/src/swap/QuoteData.ts index 7541323f7d..8510ec5662 100644 --- a/sdk/swap-common/src/types/QuoteData.ts +++ b/sdk/sdk-common/src/swap/QuoteData.ts @@ -1,5 +1,5 @@ import { TokenAmount, Percentage, Address } from '@summerfi/sdk-common/common' -import type { SwapProviderType } from '../enums/SwapProviderType' +import type { SwapProviderType } from './Enums' /** * @name QuoteData diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts new file mode 100644 index 0000000000..1470b31dc0 --- /dev/null +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -0,0 +1,7 @@ +import {Percentage} from "@summerfi/sdk-common/common"; +import {QuoteData} from "./QuoteData"; + +export type SimulatedSwapData = QuoteData & { + slippage: Percentage + fee: Percentage +} diff --git a/sdk/swap-common/src/types/SwapData.ts b/sdk/sdk-common/src/swap/SwapData.ts similarity index 88% rename from sdk/swap-common/src/types/SwapData.ts rename to sdk/sdk-common/src/swap/SwapData.ts index 5d98fe6ef8..cbaf91f7d9 100644 --- a/sdk/swap-common/src/types/SwapData.ts +++ b/sdk/sdk-common/src/swap/SwapData.ts @@ -1,6 +1,6 @@ import { Address, TokenAmount } from '@summerfi/sdk-common/common' import { HexData } from '@summerfi/sdk-common/common/aliases' -import type { SwapProviderType } from '../enums/SwapProviderType' +import type { SwapProviderType } from './Enums' /** * @name SwapData diff --git a/sdk/sdk-common/src/swap/index.ts b/sdk/sdk-common/src/swap/index.ts new file mode 100644 index 0000000000..3a3b656bb8 --- /dev/null +++ b/sdk/sdk-common/src/swap/index.ts @@ -0,0 +1,4 @@ +export type { SwapData } from './SwapData' +export type { QuoteData, SwapRoute } from './QuoteData' +export type { SimulatedSwapData } from './SimulatedSwapData' +export { SwapProviderType } from './Enums' \ No newline at end of file diff --git a/sdk/sdk-server/tests/utils/TestUtils.ts b/sdk/sdk-server/tests/utils/TestUtils.ts index f92d8fdddc..121f6ce231 100644 --- a/sdk/sdk-server/tests/utils/TestUtils.ts +++ b/sdk/sdk-server/tests/utils/TestUtils.ts @@ -12,6 +12,7 @@ export const createTestContext = (opts: ContextOptions): SDKAppContext => { swapManager: {} as any, configProvider: {} as any, protocolsRegistry: {} as any, + protocolManager: {} as any } } diff --git a/sdk/simulator-service/src/implementation/index.ts b/sdk/simulator-service/src/implementation/index.ts index ae0ec5ba34..e69de29bb2 100644 --- a/sdk/simulator-service/src/implementation/index.ts +++ b/sdk/simulator-service/src/implementation/index.ts @@ -1 +0,0 @@ -export * as strategies from './strategies' diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts index ebb8f7eb4a..6e58f1756f 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/depositBorrowReducer.ts @@ -1,12 +1,12 @@ import { borrowFromPosition, depositToPosition } from '@summerfi/sdk-common/common/utils' import { steps } from '@summerfi/sdk-common/simulation' import { addBalance, subtractBalance, getReferencedValue } from '../../utils' -import { SimulationState } from '../../../interfaces/simulation' +import { ISimulationState } from '../../../interfaces/simulation' export function depositBorrowReducer( step: steps.DepositBorrowStep, - state: SimulationState, -): SimulationState { + state: ISimulationState, +): ISimulationState { const afterDeposit = subtractBalance( getReferencedValue(step.inputs.depositAmount), state.balances, diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/flashloanReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/flashloanReducer.ts index e5ac6ce384..50ca342708 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/flashloanReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/flashloanReducer.ts @@ -1,11 +1,11 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { addBalance } from '../../helpers' -import { SimulationState } from '../../../interfaces/simulation' +import { addBalance } from '../../utils' +import { ISimulationState } from '../../../interfaces/simulation' export function flashloanReducer( step: steps.FlashloanStep, - state: SimulationState, -): SimulationState { + state: ISimulationState, +): ISimulationState { return { ...state, steps: { diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/paybackWithdrawReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/paybackWithdrawReducer.ts index cef48e167d..f87b139f8b 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/paybackWithdrawReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/paybackWithdrawReducer.ts @@ -1,12 +1,12 @@ import { depositToPosition } from '@summerfi/sdk-common/common/utils' import { steps } from '@summerfi/sdk-common/simulation' -import { addBalance, getReferencedValue, subtractBalance } from '../../helpers' -import { SimulationState } from '../../../interfaces/simulation' +import { addBalance, getReferencedValue, subtractBalance } from '../../utils' +import { ISimulationState } from '../../../interfaces/simulation' export function paybackWithdrawReducer( step: steps.PaybackWithdrawStep, - state: SimulationState, -): SimulationState { + state: ISimulationState, +): ISimulationState { const afterPayback = addBalance(getReferencedValue(step.inputs.paybackAmount), state.balances) const afterWithdraw = subtractBalance( getReferencedValue(step.inputs.withdrawAmount), diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/pullTokenReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/pullTokenReducer.ts index bdc207ba28..493d13a83f 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/pullTokenReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/pullTokenReducer.ts @@ -1,11 +1,11 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { addBalance, getReferencedValue } from '../../helpers' -import { SimulationState } from '../../../interfaces/simulation' +import { addBalance, getReferencedValue } from '../../utils' +import { ISimulationState } from '../../../interfaces/simulation' export function pullTokenReducer( step: steps.PullTokenStep, - state: SimulationState, -): SimulationState { + state: ISimulationState, +): ISimulationState { return { ...state, steps: { diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/repayFlashloanReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/repayFlashloanReducer.ts index d2f6b44fa0..5ce116fe7c 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/repayFlashloanReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/repayFlashloanReducer.ts @@ -1,11 +1,11 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { subtractBalance } from '../../helpers' -import { SimulationState } from '../../../interfaces/simulation' +import { subtractBalance } from '../../utils' +import { ISimulationState } from '../../../interfaces/simulation' export function repayFlashloanReducer( step: steps.RepayFlashloan, - state: SimulationState, -): SimulationState { + state: ISimulationState, +): ISimulationState { return { ...state, steps: { diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/returnFundsReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/returnFundsReducer.ts index 4daa0e6168..75166b5328 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/returnFundsReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/returnFundsReducer.ts @@ -1,11 +1,11 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { getTokenBalance, subtractBalance } from '../../helpers' -import { SimulationState } from '../../../interfaces/simulation' +import { getTokenBalance, subtractBalance } from '../../utils' +import { ISimulationState } from '../../../interfaces/simulation' export function returnFundsReducer( step: steps.ReturnFunds, - state: SimulationState, -): SimulationState { + state: ISimulationState, +): ISimulationState { return { ...state, steps: { diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/stateReducers.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/stateReducers.ts index 3630f2b75a..d88692759b 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/stateReducers.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/stateReducers.ts @@ -1,5 +1,5 @@ import { SimulationSteps, steps } from '@summerfi/sdk-common/simulation' -import { SimulationState } from '../../../interfaces/simulation' +import { ISimulationState } from '../../../interfaces/simulation' import type { StateReducer, StateReducers } from '../../../interfaces/steps' import { flashloanReducer } from './flashloanReducer' import { depositBorrowReducer } from './depositBorrowReducer' @@ -19,7 +19,7 @@ const stateReducers: StateReducers = { [SimulationSteps.PullToken]: pullTokenReducer, } -export function stateReducer(step: steps.Steps, state: SimulationState): SimulationState { +export function stateReducer(step: steps.Steps, state: ISimulationState): ISimulationState { const reducer = stateReducers[step.type] as StateReducer return reducer(step, state) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 1407e689c4..035ee7466a 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -1,8 +1,8 @@ import { steps } from '@summerfi/sdk-common/simulation' import { addBalance, subtractBalance } from '../../utils' -import { SimulationState } from '../../../interfaces/simulation' +import { ISimulationState } from '../../../interfaces/simulation' -export function swapReducer(step: steps.SwapStep, state: SimulationState): SimulationState { +export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISimulationState { const balanceWithoutFromToken = subtractBalance(step.inputs.fromTokenAmount, state.balances) const balanceWithToToken = addBalance(step.outputs.receivedAmount, balanceWithoutFromToken) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts index 65f63f8a18..66b1022536 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts @@ -1,4 +1,4 @@ -import type { SimulationState } from '../../interfaces/simulation' +import type { ISimulationState } from '../../interfaces/simulation' import type { Tail } from '../../interfaces/helperTypes' import type { NextFunction } from '../../interfaces' import { head, tail } from '../utils' @@ -14,13 +14,13 @@ export class Simulator< > { public schema: Strategy public originalSchema: SimulationStrategy - private state: SimulationState + private state: ISimulationState private readonly nextArray: NextArray private constructor( schema: Strategy, originalSchema: SimulationStrategy, - state: SimulationState = { swaps: [], balances: {}, positions: {}, steps: {} }, + state: ISimulationState = { swaps: {}, balances: {}, positions: {}, steps: {} }, nextArray: Readonly = [] as unknown as NextArray, ) { this.schema = schema @@ -35,7 +35,7 @@ export class Simulator< return new Simulator(schema, schema) } - public async run(): Promise { + public async run(): Promise { for (let i = 0; i < this.nextArray.length; i++) { const processedStepSchema = this.originalSchema[i] const getReference = (path: [string, string]) => { diff --git a/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/depositBorrowOutputProcessor.ts b/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/depositBorrowOutputProcessor.ts index e1a559b4c5..3954fad229 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/depositBorrowOutputProcessor.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/depositBorrowOutputProcessor.ts @@ -1,5 +1,5 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { getReferencedValue } from '../../helpers' +import { getReferencedValue } from '../../utils' import type { StepOutputProcessor } from '../../../interfaces/steps' export const depositBorrowOutputProcessor: StepOutputProcessor = async ( diff --git a/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/paybackWithdrawOutputProcessor.ts b/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/paybackWithdrawOutputProcessor.ts index cb8f26edc3..404fd7183a 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/paybackWithdrawOutputProcessor.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/stepProcessor/paybackWithdrawOutputProcessor.ts @@ -1,5 +1,5 @@ import { steps } from '@summerfi/sdk-common/simulation' -import { getReferencedValue } from '../../helpers' +import { getReferencedValue } from '../../utils' import type { StepOutputProcessor } from '../../../interfaces/steps' export const paybackWithdrawOutputProcessor: StepOutputProcessor< diff --git a/sdk/simulator-service/src/implementation/strategies/Refinance.ts b/sdk/simulator-service/src/implementation/strategies/Refinance.ts deleted file mode 100644 index dceb986728..0000000000 --- a/sdk/simulator-service/src/implementation/strategies/Refinance.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { - FlashloanProvider, - Simulation, - SimulationSteps, - SimulationType, -} from '@summerfi/sdk-common/simulation' -import { getReferencedValue, makeStrategy } from '../utils' -import { Simulator } from '../simulator-engine' -import { Percentage, TokenAmount } from '@summerfi/sdk-common/common' -import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' -import { RefinanceParameters } from '@summerfi/sdk-common/orders' -import { type ISwapManager } from '@summerfi/swap-common/interfaces' -import { isLendingPool } from '@summerfi/sdk-common/protocols' - -export const refinanceStrategy = makeStrategy([ - { - step: SimulationSteps.Flashloan, - optional: false, - }, - { - step: SimulationSteps.PaybackWithdraw, - optional: false, - }, - { - step: SimulationSteps.Swap, - optional: true, - }, - { - step: SimulationSteps.DepositBorrow, - optional: false, - }, - { - step: SimulationSteps.Swap, - optional: true, - }, - { - step: SimulationSteps.RepayFlashloan, - optional: false, - }, - { - // In case of target debt being different then source debt we need a swap, - // We cannot forsee the exact amount of the swap, so we need to return excess tokens to user - step: SimulationSteps.ReturnFunds, - optional: true, - }, -]) - -// TODO move those interfaces to more appropriate place - -export interface RefinanceDependencies { - swapManager: ISwapManager - getSummerFee: () => Percentage -} - -export async function refinanceLendingToLending( - args: RefinanceParameters, - dependencies: RefinanceDependencies, -): Promise> { - // args validation - if (!isLendingPool(args.targetPool)) { - throw new Error('Target pool is not a lending pool') - } - - const FLASHLOAN_MARGIN = 0.001 - const flashloanAmount = args.position.debtAmount.multiply(FLASHLOAN_MARGIN) - const simulator = Simulator.create(refinanceStrategy) - - const isCollateralSwapSkipped = - args.targetPool.collaterals[args.position.collateralAmount.token.address.value] !== undefined - const isDebtSwapSkipped = - args.targetPool.debts[args.position.debtAmount.token.address.value] !== undefined - - // let debtSwapQuote: Quote | undefined - // TODO: implement case with swaps - // if (!isDebtSwapSkipped) { - // debtSwapQuote = await dependencies.getQuote({ - // from: args.targetPool.debtTokens[0], - // to: args.position.debtAmount.token, - // slippage: args.slippage, - // fee: 0, - // }) - // } - - // TODO: read debt amount from chain (special step: ReadDebtAmount) - // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, - // before or after the swap, it influences actual call to oneInch api - const simulation = await simulator - .next(async () => ({ - name: 'Flashloan', - type: SimulationSteps.Flashloan, - inputs: { - amount: flashloanAmount, - provider: FlashloanProvider.Maker, - }, - })) - .next(async () => ({ - name: 'PaybackWithdrawFromSource', - type: SimulationSteps.PaybackWithdraw, - inputs: { - paybackAmount: TokenAmount.createFrom({ - amount: Number.MAX_SAFE_INTEGER.toString(), - token: args.position.debtAmount.token, - }), - withdrawAmount: TokenAmount.createFrom({ - amount: Number.MAX_SAFE_INTEGER.toString(), - token: args.position.collateralAmount.token, - }), - position: args.position, - }, - })) - .next(async () => ({ - name: 'CollateralSwap', - type: SimulationSteps.Swap, - inputs: { - ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: args.position.pool.protocol.chainInfo, - fromAmount: args.position.collateralAmount, - toToken: - args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, - })), - slippage: args.slippage, - fee: dependencies.getSummerFee(), - }, - skip: isCollateralSwapSkipped, - })) - .next(async (ctx) => ({ - name: 'DepositBorrowToTarget', - type: SimulationSteps.DepositBorrow, - inputs: { - depositAmount: ctx.getReference( - isCollateralSwapSkipped - ? ['PaybackWithdrawFromSource', 'withdrawAmount'] - : ['CollateralSwap', 'receivedAmount'], - ), - borrowAmount: args.position.debtAmount, // TODO figure the debt amount - position: newEmptyPositionFromPool( - args.targetPool, - args.position.debtAmount.token.address.value, - args.position.collateralAmount.token.address.value, - ), - }, - })) - .next(async (ctx) => ({ - name: 'DebtSwap', - type: SimulationSteps.Swap, - inputs: { - ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: args.position.pool.protocol.chainInfo, - fromAmount: getReferencedValue( - ctx.getReference(['DepositBorrowToTarget', 'borrowAmount']), - ), - toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, - })), - slippage: args.slippage, - fee: dependencies.getSummerFee(), - }, - skip: isDebtSwapSkipped, - })) - .next(async () => ({ - name: 'RepayFlashloan', - type: SimulationSteps.RepayFlashloan, - inputs: { - amount: args.position.debtAmount, // TODO add some amount - }, - })) - .next(async () => ({ - name: 'ReturnFunds', - type: SimulationSteps.ReturnFunds, - inputs: { - token: - args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, - }, - skip: isDebtSwapSkipped, - })) - .run() - - const targetPosition = Object.values(simulation.positions).find( - (p) => p.pool.protocol === args.targetPool.protocol, - ) - - if (!targetPosition) { - throw new Error('Target position not found') - } - - return { - simulationType: SimulationType.Refinance, - sourcePosition: args.position, - targetPosition, - swaps: simulation.swaps, - steps: Object.values(simulation.steps), - } -} diff --git a/sdk/simulator-service/src/implementation/strategies/index.ts b/sdk/simulator-service/src/implementation/strategies/index.ts deleted file mode 100644 index 6a4cecc123..0000000000 --- a/sdk/simulator-service/src/implementation/strategies/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Refinance' diff --git a/sdk/simulator-service/src/index.ts b/sdk/simulator-service/src/index.ts deleted file mode 100644 index 8aac1b5dce..0000000000 --- a/sdk/simulator-service/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './implementation/strategies' diff --git a/sdk/simulator-service/src/interfaces/simulation.ts b/sdk/simulator-service/src/interfaces/simulation.ts index 37acfc321c..b4153d7fb4 100644 --- a/sdk/simulator-service/src/interfaces/simulation.ts +++ b/sdk/simulator-service/src/interfaces/simulation.ts @@ -1,15 +1,10 @@ import type { steps } from '@summerfi/sdk-common/simulation' -import type { TokenAmount, Position, Percentage } from '@summerfi/sdk-common/common' -import {QuoteData} from "@summerfi/swap-common/types"; +import type { TokenAmount, Position } from '@summerfi/sdk-common/common' +import type { SimulatedSwapData } from '@summerfi/sdk-common/swap' -export interface SimulationState { - swaps: Record +export interface ISimulationState { + swaps: Record balances: Record positions: Record steps: Record -} - -type SimulatedSwap = QuoteData & { - slippage: Percentage - fee: Percentage -} +} \ No newline at end of file diff --git a/sdk/simulator-service/src/strategies/refinance/Refinance.ts b/sdk/simulator-service/src/strategies/refinance/Refinance.ts new file mode 100644 index 0000000000..f6fd619916 --- /dev/null +++ b/sdk/simulator-service/src/strategies/refinance/Refinance.ts @@ -0,0 +1,153 @@ +import { + FlashloanProvider, + ISimulation, + SimulationSteps, + SimulationType, +} from '@summerfi/sdk-common/simulation' +import { getReferencedValue } from '../../implementation/utils' +import { Simulator } from '../../implementation/simulator-engine' +import { TokenAmount } from '@summerfi/sdk-common/common' +import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' +import { RefinanceParameters } from '@summerfi/sdk-common/orders' +import { isLendingPool } from '@summerfi/sdk-common/protocols' +import {refinanceLendingToLendingStrategy} from "./Strategy"; +import {RefinanceDependencies} from "./Types"; + +export async function refinance( + args: RefinanceParameters, + dependencies: RefinanceDependencies, +): Promise> { + // args validation + if (!isLendingPool(args.targetPool)) { + throw new Error('Target pool is not a lending pool') + } + + const FLASHLOAN_MARGIN = 0.001 + const flashloanAmount = args.position.debtAmount.multiply(FLASHLOAN_MARGIN) + const simulator = Simulator.create(refinanceLendingToLendingStrategy) + + // TODO: Update this check + const isCollateralSwapSkipped = + args.targetPool.collaterals[args.position.collateralAmount.token.address.value] !== undefined + const isDebtSwapSkipped = + args.targetPool.debts[args.position.debtAmount.token.address.value] !== undefined + + // let debtSwapQuote: Quote | undefined + // TODO: implement case with swaps + // if (!isDebtSwapSkipped) { + // debtSwapQuote = await dependencies.getQuote({ + // from: args.targetPool.debtTokens[0], + // to: args.position.debtAmount.token, + // slippage: args.slippage, + // fee: 0, + // }) + // } + + // TODO: read debt amount from chain (special step: ReadDebtAmount) + // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, + // before or after the swap, it influences actual call to oneInch api + const simulation = await simulator + .next(async () => ({ + name: 'Flashloan', + type: SimulationSteps.Flashloan, + inputs: { + amount: flashloanAmount, + provider: FlashloanProvider.Maker, + }, + })) + .next(async () => ({ + name: 'PaybackWithdrawFromSource', + type: SimulationSteps.PaybackWithdraw, + inputs: { + paybackAmount: TokenAmount.createFrom({ + amount: Number.MAX_SAFE_INTEGER.toString(), + token: args.position.debtAmount.token, + }), + withdrawAmount: TokenAmount.createFrom({ + amount: Number.MAX_SAFE_INTEGER.toString(), + token: args.position.collateralAmount.token, + }), + position: args.position, + }, + })) + .next(async () => ({ + name: 'CollateralSwap', + type: SimulationSteps.Swap, + inputs: { + ...(await dependencies.swapManager.getSwapQuoteExactInput({ + chainInfo: args.position.pool.protocol.chainInfo, + fromAmount: args.position.collateralAmount, + toToken: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + })), + slippage: args.slippage, + fee: dependencies.getSummerFee(), + }, + skip: isCollateralSwapSkipped, + })) + .next(async (ctx) => ({ + name: 'DepositBorrowToTarget', + type: SimulationSteps.DepositBorrow, + inputs: { + depositAmount: ctx.getReference( + isCollateralSwapSkipped + ? ['PaybackWithdrawFromSource', 'withdrawAmount'] + : ['CollateralSwap', 'receivedAmount'], + ), + borrowAmount: args.position.debtAmount, // TODO figure the debt amount + position: newEmptyPositionFromPool( + args.targetPool, + args.position.debtAmount.token.address.value, + args.position.collateralAmount.token.address.value, + ), + }, + })) + .next(async (ctx) => ({ + name: 'DebtSwap', + type: SimulationSteps.Swap, + inputs: { + ...(await dependencies.swapManager.getSwapQuoteExactInput({ + chainInfo: args.position.pool.protocol.chainInfo, + fromAmount: getReferencedValue( + ctx.getReference(['DepositBorrowToTarget', 'borrowAmount']), + ), + toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, + })), + slippage: args.slippage, + fee: dependencies.getSummerFee(), + }, + skip: isDebtSwapSkipped, + })) + .next(async () => ({ + name: 'RepayFlashloan', + type: SimulationSteps.RepayFlashloan, + inputs: { + amount: args.position.debtAmount, // TODO add some amount + }, + })) + .next(async () => ({ + name: 'ReturnFunds', + type: SimulationSteps.ReturnFunds, + inputs: { + token: + args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + }, + skip: isDebtSwapSkipped, + })) + .run() + + const targetPosition = Object.values(simulation.positions).find( + (p) => p.pool.protocol === args.targetPool.protocol, + ) + + if (!targetPosition) { + throw new Error('Target position not found') + } + + return { + simulationType: SimulationType.Refinance, + sourcePosition: args.position, + targetPosition, + swaps: Object.values(simulation.swaps), + steps: Object.values(simulation.steps), + } +} diff --git a/sdk/simulator-service/src/strategies/refinance/Strategy.ts b/sdk/simulator-service/src/strategies/refinance/Strategy.ts new file mode 100644 index 0000000000..4edfe25baa --- /dev/null +++ b/sdk/simulator-service/src/strategies/refinance/Strategy.ts @@ -0,0 +1,35 @@ +import { SimulationSteps } from '@summerfi/sdk-common/simulation' +import { makeStrategy } from '../../implementation/utils' + +export const refinanceLendingToLendingStrategy = makeStrategy([ + { + step: SimulationSteps.Flashloan, + optional: false, + }, + { + step: SimulationSteps.PaybackWithdraw, + optional: false, + }, + { + step: SimulationSteps.Swap, + optional: true, + }, + { + step: SimulationSteps.DepositBorrow, + optional: false, + }, + { + step: SimulationSteps.Swap, + optional: true, + }, + { + step: SimulationSteps.RepayFlashloan, + optional: false, + }, + { + // In case of target debt being different then source debt we need a swap, + // We cannot forsee the exact amount of the swap, so we need to return excess tokens to user + step: SimulationSteps.ReturnFunds, + optional: true, + }, +]) \ No newline at end of file diff --git a/sdk/simulator-service/src/strategies/refinance/Types.ts b/sdk/simulator-service/src/strategies/refinance/Types.ts new file mode 100644 index 0000000000..88a341412a --- /dev/null +++ b/sdk/simulator-service/src/strategies/refinance/Types.ts @@ -0,0 +1,7 @@ +import { Percentage } from '@summerfi/sdk-common/common' +import { type ISwapManager } from '@summerfi/swap-common/interfaces' + +export interface RefinanceDependencies { + swapManager: ISwapManager + getSummerFee: () => Percentage +} \ No newline at end of file diff --git a/sdk/simulator-service/src/strategies/refinance/index.ts b/sdk/simulator-service/src/strategies/refinance/index.ts new file mode 100644 index 0000000000..f84b53ee2d --- /dev/null +++ b/sdk/simulator-service/src/strategies/refinance/index.ts @@ -0,0 +1,3 @@ +export * from './Refinance' +export * from './Strategy' +export * from './Types' diff --git a/sdk/swap-common/src/enums/index.ts b/sdk/swap-common/src/enums/index.ts deleted file mode 100644 index caae5cdc87..0000000000 --- a/sdk/swap-common/src/enums/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SwapProviderType } from './SwapProviderType' diff --git a/sdk/swap-common/src/interfaces/ISwapManager.ts b/sdk/swap-common/src/interfaces/ISwapManager.ts index c065a56906..c509ff19be 100644 --- a/sdk/swap-common/src/interfaces/ISwapManager.ts +++ b/sdk/swap-common/src/interfaces/ISwapManager.ts @@ -5,8 +5,11 @@ import type { Token, Address, } from '@summerfi/sdk-common/common' -import type { QuoteData } from '../types/QuoteData' -import type { SwapData } from '../types/SwapData' +import type { + QuoteData, + SwapData +} from '@summerfi/sdk-common/swap' + /** * @name ISwapManager diff --git a/sdk/swap-common/src/interfaces/ISwapProvider.ts b/sdk/swap-common/src/interfaces/ISwapProvider.ts index 71930bf8d6..437642e18f 100644 --- a/sdk/swap-common/src/interfaces/ISwapProvider.ts +++ b/sdk/swap-common/src/interfaces/ISwapProvider.ts @@ -1,7 +1,9 @@ import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' import type { SwapProviderType } from '../enums/SwapProviderType' -import type { SwapData } from '../types/SwapData' -import type { QuoteData } from '../types/QuoteData' +import type { + QuoteData, + SwapData +} from '@summerfi/sdk-common/swap' /** * @name ISwapProvider diff --git a/sdk/swap-common/src/types/index.ts b/sdk/swap-common/src/types/index.ts deleted file mode 100644 index 3f6a4ecd4e..0000000000 --- a/sdk/swap-common/src/types/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { SwapData } from './SwapData' -export type { QuoteData, SwapRoute } from './QuoteData' From 8346e41857f5a8288b8d1a1062086c4ef9f9561f Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 26 Mar 2024 08:21:17 +0000 Subject: [PATCH 05/26] refactor: fix imports and format --- pnpm-lock.yaml | 3 - .../src/implementation/OrderPlanner.ts | 6 +- .../src/implementation/OrderPlannerService.ts | 4 +- .../src/interfaces/IOrderPlannerService.ts | 4 +- .../tests/builders/SwapActionBuilder.spec.ts | 5 +- .../tests/mocks/SwapManagerMock.ts | 2 +- .../tests/service/OrderPlannerService.spec.ts | 6 +- .../RefinanceSimulation.ts | 5 +- sdk/sdk-client/src/implementation/User.ts | 4 +- .../simulations/RefinanceSimulationManager.ts | 4 +- sdk/sdk-client/src/interfaces/IUserClient.ts | 4 +- sdk/sdk-client/src/mocks/mockOrder.ts | 4 +- .../tests/queries/newOrder.subtest.ts | 5 +- .../queries/simulateRefinance.subtest.ts | 5 +- sdk/sdk-common/package.json | 3 +- .../src/orders/interfaces/common/Order.ts | 4 +- .../refinance/RefinanceParameters.ts | 7 +- sdk/sdk-common/src/simulation/Simulation.ts | 2 +- sdk/sdk-common/src/simulation/Steps.ts | 3 +- sdk/sdk-common/src/swap/Enums.ts | 2 +- sdk/sdk-common/src/swap/QuoteData.ts | 2 +- sdk/sdk-common/src/swap/SimulatedSwapData.ts | 8 +- sdk/sdk-common/src/swap/index.ts | 2 +- sdk/sdk-e2e/tests/refinance.test.ts | 4 +- sdk/sdk-server/src/Context.ts | 9 +- sdk/sdk-server/src/handlers/buildOrder.ts | 4 +- .../src/handlers/getRefinanceSimulation.ts | 8 +- sdk/sdk-server/src/handlers/getSwapData.ts | 2 +- sdk/sdk-server/src/handlers/getSwapQuote.ts | 2 +- sdk/sdk-server/tests/utils/TestUtils.ts | 2 +- sdk/simulator-service/package.json | 6 +- .../simulator-engine/reducer/swapReducer.ts | 6 +- .../src/implementation/utils/BalanceUtils.ts | 36 +-- .../implementation/utils/SimulatorUtils.ts | 32 +-- .../src/implementation/utils/index.ts | 2 +- .../src/interfaces/simulation.ts | 2 +- sdk/simulator-service/src/interfaces/steps.ts | 8 +- sdk/simulator-service/src/strategies/index.ts | 1 + .../src/strategies/refinance/Refinance.ts | 269 +++++++++--------- .../src/strategies/refinance/Strategy.ts | 62 ++-- .../src/strategies/refinance/Types.ts | 6 +- .../tests/mocks/contextMock.ts | 2 +- sdk/simulator-service/tests/simulator.test.ts | 12 +- .../src/interfaces/ISwapManager.ts | 6 +- .../src/interfaces/ISwapProvider.ts | 7 +- sdk/swap-service/e2e/oneinch.spec.ts | 3 +- .../src/implementation/SwapManager.ts | 3 +- .../oneinch/OneInchSwapProvider.ts | 22 +- .../src/implementation/oneinch/types.ts | 4 +- 49 files changed, 308 insertions(+), 306 deletions(-) create mode 100644 sdk/simulator-service/src/strategies/index.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4e044fb01..aedd5303ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -850,9 +850,6 @@ importers: '@summerfi/common': specifier: workspace:* version: link:../../packages/common - '@summerfi/swap-common': - specifier: workspace:* - version: link:../swap-common bignumber.js: specifier: 9.0.1 version: 9.0.1 diff --git a/sdk/order-planner-common/src/implementation/OrderPlanner.ts b/sdk/order-planner-common/src/implementation/OrderPlanner.ts index ba7ec49eff..5c7adb9317 100644 --- a/sdk/order-planner-common/src/implementation/OrderPlanner.ts +++ b/sdk/order-planner-common/src/implementation/OrderPlanner.ts @@ -1,5 +1,5 @@ import { Order, type IPositionsManager } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType, steps } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType, steps } from '@summerfi/sdk-common/simulation' import { Deployment } from '@summerfi/deployment-utils' import { Address, Maybe } from '@summerfi/sdk-common/common' import { HexData } from '@summerfi/sdk-common/common/aliases' @@ -18,7 +18,7 @@ export class OrderPlanner implements IOrderPlanner { async buildOrder(params: { user: IUser positionsManager: IPositionsManager - simulation: Simulation + simulation: ISimulation actionBuildersMap: ActionBuildersMap deployment: Deployment swapManager: ISwapManager @@ -72,7 +72,7 @@ export class OrderPlanner implements IOrderPlanner { } private _generateOrder( - simulation: Simulation, + simulation: ISimulation, simulationCalls: ActionCall[], deployment: Deployment, ): Order { diff --git a/sdk/order-planner-service/src/implementation/OrderPlannerService.ts b/sdk/order-planner-service/src/implementation/OrderPlannerService.ts index f7d129d264..bec5db15a2 100644 --- a/sdk/order-planner-service/src/implementation/OrderPlannerService.ts +++ b/sdk/order-planner-service/src/implementation/OrderPlannerService.ts @@ -1,6 +1,6 @@ import { OrderPlanner } from '@summerfi/order-planner-common/implementation' import { Order, type IPositionsManager } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { IUser } from '@summerfi/sdk-common/user' import { ChainInfo, Maybe } from '@summerfi/sdk-common/common' import { DeploymentIndex } from '@summerfi/deployment-utils' @@ -29,7 +29,7 @@ export class OrderPlannerService implements IOrderPlannerService { async buildOrder(params: { user: IUser positionsManager: IPositionsManager - simulation: Simulation + simulation: ISimulation swapManager: ISwapManager protocolsRegistry: ProtocolBuilderRegistryType }): Promise> { diff --git a/sdk/order-planner-service/src/interfaces/IOrderPlannerService.ts b/sdk/order-planner-service/src/interfaces/IOrderPlannerService.ts index 1022a7c473..4e7810012e 100644 --- a/sdk/order-planner-service/src/interfaces/IOrderPlannerService.ts +++ b/sdk/order-planner-service/src/interfaces/IOrderPlannerService.ts @@ -1,5 +1,5 @@ import { Order, type IPositionsManager } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { IUser } from '@summerfi/sdk-common/user' import { Maybe } from '@summerfi/sdk-common/common' import { ISwapManager } from '@summerfi/swap-common/interfaces' @@ -9,7 +9,7 @@ export interface IOrderPlannerService { buildOrder(params: { user: IUser positionsManager: IPositionsManager - simulation: Simulation + simulation: ISimulation swapManager: ISwapManager protocolsRegistry: ProtocolBuilderRegistryType }): Promise> diff --git a/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts b/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts index a3eb824d1a..80e2bd5964 100644 --- a/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts +++ b/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts @@ -8,7 +8,7 @@ import { TokenAmount, } from '@summerfi/sdk-common/common' import { SimulationSteps, steps } from '@summerfi/sdk-common/simulation' -import { SwapProviderType } from '@summerfi/swap-common/enums' +import { SwapProviderType } from '@summerfi/sdk-common/swap' import { SetupBuilderReturnType, setupBuilderParams } from '../utils/SetupBuilderParams' import { SwapActionBuilder } from '../../src/builders' @@ -58,6 +58,9 @@ describe('Swap Action Builder', () => { type: SimulationSteps.Swap, name: 'SwapStep', inputs: { + provider: SwapProviderType.OneInch, + routes: [], + estimatedGas: '', fromTokenAmount: fromAmount, toTokenAmount: toAmount, fee: fee, diff --git a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts index 855a182013..a0cd8b5083 100644 --- a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts +++ b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts @@ -1,6 +1,6 @@ import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' import { ISwapManager } from '@summerfi/swap-common/interfaces' -import { QuoteData, SwapData } from '@summerfi/swap-common/types' +import { QuoteData, SwapData } from '@summerfi/sdk-common/swap' export class SwapManagerMock implements ISwapManager { private _swapDataReturnValue: SwapData = {} as SwapData diff --git a/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts b/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts index 9fbff1d9bb..f6a8c913b2 100644 --- a/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts +++ b/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts @@ -1,4 +1,4 @@ -import { FlashloanProvider, Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { FlashloanProvider, ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { DeploymentIndex } from '@summerfi/deployment-utils' import { ISwapManager } from '@summerfi/swap-common/interfaces' import { Address, ChainFamilyMap, ChainInfo } from '@summerfi/sdk-common/common' @@ -57,7 +57,7 @@ describe('Order Planner Service', () => { const sourcePosition = getMakerPosition() const targetPosition = getSparkPosition() - const refinanceSimulation: Simulation = getRefinanceSimulation({ + const refinanceSimulation: ISimulation = getRefinanceSimulation({ sourcePosition, targetPosition, }) @@ -81,7 +81,7 @@ describe('Order Planner Service', () => { const sourcePosition = getMakerPosition() const targetPosition = getSparkPosition() - const refinanceSimulation: Simulation = getRefinanceSimulation({ + const refinanceSimulation: ISimulation = getRefinanceSimulation({ sourcePosition, targetPosition, }) diff --git a/sdk/order-planner-service/tests/utils/RefinanceSimulation/RefinanceSimulation.ts b/sdk/order-planner-service/tests/utils/RefinanceSimulation/RefinanceSimulation.ts index f66e993de9..a8d2d86733 100644 --- a/sdk/order-planner-service/tests/utils/RefinanceSimulation/RefinanceSimulation.ts +++ b/sdk/order-planner-service/tests/utils/RefinanceSimulation/RefinanceSimulation.ts @@ -1,6 +1,6 @@ import { FlashloanProvider, - Simulation, + ISimulation, SimulationSteps, SimulationType, steps, @@ -10,13 +10,14 @@ import { Position } from '@summerfi/sdk-common/common' export function getRefinanceSimulation(params: { sourcePosition: Position targetPosition: Position -}): Simulation { +}): ISimulation { const { sourcePosition, targetPosition } = params return { simulationType: SimulationType.Refinance, sourcePosition: sourcePosition, targetPosition: targetPosition, + swaps: [], steps: [ { name: 'Flashloan', diff --git a/sdk/sdk-client/src/implementation/User.ts b/sdk/sdk-client/src/implementation/User.ts index 2b17244f6b..485cf5aef2 100644 --- a/sdk/sdk-client/src/implementation/User.ts +++ b/sdk/sdk-client/src/implementation/User.ts @@ -8,7 +8,7 @@ import { } from '@summerfi/sdk-common/common' import { getMockPosition } from '../mocks/mockPosition' import { IPositionsManager, Order } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { IUserClient } from '../interfaces/IUserClient' import { IRPCClient } from '../interfaces/IRPCClient' import { RPCClientType } from '../rpc/SDKClient' @@ -52,7 +52,7 @@ export class User extends IRPCClient implements IUserClient { public async newOrder(params: { positionsManager: IPositionsManager - simulation: Simulation + simulation: ISimulation }): Promise> { return await this.rpcClient.orders.buildOrder.query({ user: this, diff --git a/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts b/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts index e9fd1ed311..db0481c94d 100644 --- a/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts +++ b/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts @@ -1,5 +1,5 @@ import { RefinanceParameters } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { RPCClientType } from '../../rpc/SDKClient' import { IRPCClient } from '../../interfaces/IRPCClient' @@ -10,7 +10,7 @@ export class RefinanceSimulationManager extends IRPCClient { public async simulateRefinancePosition( params: RefinanceParameters, - ): Promise> { + ): Promise> { return this.rpcClient.simulation.refinance.query(params) } } diff --git a/sdk/sdk-client/src/interfaces/IUserClient.ts b/sdk/sdk-client/src/interfaces/IUserClient.ts index 432f1869cc..27b53bdfee 100644 --- a/sdk/sdk-client/src/interfaces/IUserClient.ts +++ b/sdk/sdk-client/src/interfaces/IUserClient.ts @@ -1,6 +1,6 @@ import { Maybe, Position, PositionId } from '@summerfi/sdk-common/common' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { IUser } from '@summerfi/sdk-common/user' import { Order } from '@summerfi/sdk-common/orders' import { IProtocol } from '@summerfi/sdk-common/protocols' @@ -39,5 +39,5 @@ export interface IUserClient extends IUser { * * @returns The new order created for the user */ - newOrder(params: { simulation: Simulation }): Promise> + newOrder(params: { simulation: ISimulation }): Promise> } diff --git a/sdk/sdk-client/src/mocks/mockOrder.ts b/sdk/sdk-client/src/mocks/mockOrder.ts index bd15ac8951..e41dc8fd8a 100644 --- a/sdk/sdk-client/src/mocks/mockOrder.ts +++ b/sdk/sdk-client/src/mocks/mockOrder.ts @@ -1,11 +1,11 @@ -import { SimulationType, Simulation } from '@summerfi/sdk-common/simulation' +import { SimulationType, ISimulation } from '@summerfi/sdk-common/simulation' import { Order, IPositionsManager } from '@summerfi/sdk-common/orders' import { IUser } from '@summerfi/sdk-common/user' export async function getMockOrder(params: { user: IUser positionsManager: IPositionsManager - simulation: Simulation + simulation: ISimulation }): Promise { return { simulation: params.simulation, diff --git a/sdk/sdk-client/tests/queries/newOrder.subtest.ts b/sdk/sdk-client/tests/queries/newOrder.subtest.ts index 3113be7d21..2eb57531d3 100644 --- a/sdk/sdk-client/tests/queries/newOrder.subtest.ts +++ b/sdk/sdk-client/tests/queries/newOrder.subtest.ts @@ -2,7 +2,7 @@ import { IProtocol, PoolType, ProtocolName } from '@summerfi/sdk-common/protocol import { SDKManager } from '../../src/implementation/SDKManager' import { RPCClientType } from '../../src/rpc/SDKClient' import { MakerLendingPool, SparkLendingPool } from '@summerfi/protocol-plugins' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { Address, ChainFamilyMap, @@ -76,9 +76,10 @@ export default async function simulateNewOrder() { baseCurrency: DAI, } - const simulation: Simulation = { + const simulation: ISimulation = { simulationType: SimulationType.Refinance, sourcePosition: prevPosition, + swaps: [], targetPosition: { positionId: PositionId.createFrom({ id: '1234567890' }), debtAmount: TokenAmount.createFrom({ token: DAI, amount: '56.78' }), diff --git a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts index 6a67a36b45..04c92f5c13 100644 --- a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts +++ b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts @@ -2,7 +2,7 @@ import { IProtocol, PoolType, ProtocolName } from '@summerfi/sdk-common/protocol import { SDKManager } from '../../src/implementation/SDKManager' import { RPCClientType } from '../../src/rpc/SDKClient' import { MakerLendingPool, SparkLendingPool } from '@summerfi/protocol-plugins' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { Address, ChainFamilyMap, @@ -22,6 +22,7 @@ export default async function simulateRefinanceTest() { return { simulationType: SimulationType.Refinance, sourcePosition: params.position, + swaps: [], targetPosition: { positionId: PositionId.createFrom({ id: '0987654321' }), debtAmount: params.position.debtAmount, @@ -30,7 +31,7 @@ export default async function simulateRefinanceTest() { riskRatio: params.position.riskRatio, }, steps: [], - } as Simulation + } as ISimulation }) const rpcClient = { diff --git a/sdk/sdk-common/package.json b/sdk/sdk-common/package.json index a984dddc0e..fef5a4cf61 100644 --- a/sdk/sdk-common/package.json +++ b/sdk/sdk-common/package.json @@ -21,8 +21,7 @@ "bignumber.js": "9.0.1", "superjson": "^1.13.3", "viem": "^2.2.0", - "@summerfi/common": "workspace:*", - "@summerfi/swap-common": "workspace:*" + "@summerfi/common": "workspace:*" }, "devDependencies": { "@summerfi/eslint-config": "workspace:*", diff --git a/sdk/sdk-common/src/orders/interfaces/common/Order.ts b/sdk/sdk-common/src/orders/interfaces/common/Order.ts index 0e4eb34313..6d65289066 100644 --- a/sdk/sdk-common/src/orders/interfaces/common/Order.ts +++ b/sdk/sdk-common/src/orders/interfaces/common/Order.ts @@ -1,5 +1,5 @@ +import { ISimulation } from '../../../simulation' import { SimulationType } from '../../../simulation/Enums' -import { Simulation } from '../../../simulation/Simulation' import { TransactionInfo } from './TransactionInfo' /** @@ -8,7 +8,7 @@ import { TransactionInfo } from './TransactionInfo' */ export interface Order { /** @description Simulation */ - simulation: Simulation + simulation: ISimulation /** @description Transaction info */ transactions: TransactionInfo[] } diff --git a/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts b/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts index 7d1b6c0513..6ec48f0eb3 100644 --- a/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts +++ b/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts @@ -1,6 +1,6 @@ import type { Percentage } from '../../../common/implementation/Percentage' import type { Position } from '../../../common/implementation/Position' -import type { Address } from '../../../common/implementation/Address' +// import type { Address } from '../../../common/implementation/Address' import type { LendingPool } from '../../../protocols/implementation/LendingPool' /** @@ -10,8 +10,9 @@ import type { LendingPool } from '../../../protocols/implementation/LendingPool' export interface RefinanceParameters { position: Position targetPool: LendingPool - targetCollateral: Address - targetDebt: Address + // TODO: Implement means of determining if a swap is required. Follow-up PR + // targetCollateral: Address + // targetDebt: Address slippage: Percentage } diff --git a/sdk/sdk-common/src/simulation/Simulation.ts b/sdk/sdk-common/src/simulation/Simulation.ts index 7449309b16..4180549d9a 100644 --- a/sdk/sdk-common/src/simulation/Simulation.ts +++ b/sdk/sdk-common/src/simulation/Simulation.ts @@ -1,5 +1,5 @@ import { Position } from '../common/implementation/Position' -import {SimulatedSwapData} from "../swap"; +import { SimulatedSwapData } from '../swap' import type { SimulationType } from './Enums' import type { Steps } from './Steps' diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index f9cc9e82e6..886387f7a9 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -2,10 +2,9 @@ import { Percentage } from '../common/implementation/Percentage' import { Position } from '../common/implementation/Position' import { Token } from '../common/implementation/Token' import { TokenAmount } from '../common/implementation/TokenAmount' +import { SwapProviderType, SwapRoute } from '../swap' import { FlashloanProvider, SimulationSteps } from './Enums' import { ReferenceableField, ValueReference } from './ValueReference' -import { SwapProviderType } from '@summerfi/swap-common/enums' -import { SwapRoute } from "@summerfi/swap-common/types" export interface Step { type: T diff --git a/sdk/sdk-common/src/swap/Enums.ts b/sdk/sdk-common/src/swap/Enums.ts index 254dd779ad..d4efdcdd6b 100644 --- a/sdk/sdk-common/src/swap/Enums.ts +++ b/sdk/sdk-common/src/swap/Enums.ts @@ -3,5 +3,5 @@ * @description Represents the different swap providers */ export enum SwapProviderType { - OneInch = 'OneInch', + OneInch = 'OneInch', } diff --git a/sdk/sdk-common/src/swap/QuoteData.ts b/sdk/sdk-common/src/swap/QuoteData.ts index 8510ec5662..1289455290 100644 --- a/sdk/sdk-common/src/swap/QuoteData.ts +++ b/sdk/sdk-common/src/swap/QuoteData.ts @@ -20,7 +20,7 @@ export type SwapRoute = SwapHop[] type SwapHop = SwapHopPart[] type SwapHopPart = { - name: string, + name: string part: Percentage fromTokenAddress: Address toTokenAddress: Address diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts index 1470b31dc0..27f968386c 100644 --- a/sdk/sdk-common/src/swap/SimulatedSwapData.ts +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -1,7 +1,7 @@ -import {Percentage} from "@summerfi/sdk-common/common"; -import {QuoteData} from "./QuoteData"; +import { Percentage } from '@summerfi/sdk-common/common' +import { QuoteData } from './QuoteData' export type SimulatedSwapData = QuoteData & { - slippage: Percentage - fee: Percentage + slippage: Percentage + fee: Percentage } diff --git a/sdk/sdk-common/src/swap/index.ts b/sdk/sdk-common/src/swap/index.ts index 3a3b656bb8..3dc6159970 100644 --- a/sdk/sdk-common/src/swap/index.ts +++ b/sdk/sdk-common/src/swap/index.ts @@ -1,4 +1,4 @@ export type { SwapData } from './SwapData' export type { QuoteData, SwapRoute } from './QuoteData' export type { SimulatedSwapData } from './SimulatedSwapData' -export { SwapProviderType } from './Enums' \ No newline at end of file +export { SwapProviderType } from './Enums' diff --git a/sdk/sdk-e2e/tests/refinance.test.ts b/sdk/sdk-e2e/tests/refinance.test.ts index c88bc88ba8..ce51d2d781 100644 --- a/sdk/sdk-e2e/tests/refinance.test.ts +++ b/sdk/sdk-e2e/tests/refinance.test.ts @@ -25,7 +25,7 @@ import { import { makeSDK, type Chain, type User, Protocol } from '@summerfi/sdk-client' import { TokenSymbol } from '@summerfi/sdk-common/common/enums' import { Order, RefinanceParameters } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { PoolIds } from '@summerfi/protocol-manager' describe.skip('Refinance | SDK', () => { @@ -181,7 +181,7 @@ describe.skip('Refinance | SDK', () => { slippage: Percentage.createFrom({ percentage: 0.5 }), } - const refinanceSimulation: Simulation = + const refinanceSimulation: ISimulation = await sdk.simulator.refinance.simulateRefinancePosition(refinanceParameters) expect(refinanceSimulation).toBeDefined() diff --git a/sdk/sdk-server/src/Context.ts b/sdk/sdk-server/src/Context.ts index 94da20cf2d..6603da0b1a 100644 --- a/sdk/sdk-server/src/Context.ts +++ b/sdk/sdk-server/src/Context.ts @@ -1,5 +1,10 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { PriceService, TokenService } from '@summerfi/protocol-plugins' +import { + PriceService, + TokenService, + ProtocolPluginsRegistry, + MockContractProvider, +} from '@summerfi/protocol-plugins' import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda' import type { APIGatewayProxyEventV2 } from 'aws-lambda' import { DeploymentIndex } from '@summerfi/deployment-utils' @@ -8,12 +13,10 @@ import { OrderPlannerService } from '@summerfi/order-planner-service/implementat import { SwapManagerFactory } from '@summerfi/swap-service' import { ConfigurationProvider, IConfigurationProvider } from '@summerfi/configuration-provider' import { ISwapManager } from '@summerfi/swap-common/interfaces' -import { ProtocolPluginsRegistry } from '@summerfi/protocol-plugins' import { ProtocolBuilderRegistryType } from '@summerfi/order-planner-common/interfaces' import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { protocolManager } from '@summerfi/protocol-manager' -import { MockContractProvider } from '@summerfi/protocol-plugins' export type ContextOptions = CreateAWSLambdaContextOptions diff --git a/sdk/sdk-server/src/handlers/buildOrder.ts b/sdk/sdk-server/src/handlers/buildOrder.ts index 24810562b9..4c8a4a308e 100644 --- a/sdk/sdk-server/src/handlers/buildOrder.ts +++ b/sdk/sdk-server/src/handlers/buildOrder.ts @@ -1,6 +1,6 @@ import { z } from 'zod' import type { IPositionsManager, Order } from '@summerfi/sdk-common/orders' -import type { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import type { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import type { IUser } from '@summerfi/sdk-common/user' import { Maybe } from '@summerfi/sdk-common/common' import { publicProcedure } from '../TRPC' @@ -12,7 +12,7 @@ export const buildOrder = publicProcedure positionsManager: z.custom( (positionsManager) => positionsManager !== undefined, ), - simulation: z.custom>((simulation) => simulation !== undefined), + simulation: z.custom>((simulation) => simulation !== undefined), }), ) .query(async (opts): Promise> => { diff --git a/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts b/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts index 547ae77081..bb8184745d 100644 --- a/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts +++ b/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts @@ -1,7 +1,7 @@ +import { refinance, type RefinanceDependencies } from '@summerfi/simulator-service/strategies' import { z } from 'zod' import { Percentage } from '@summerfi/sdk-common/common' -import type { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' -import { refinaceLendingToLending, type RefinanceDependencies } from '@summerfi/simulator-service' +import type { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import type { RefinanceParameters } from '@summerfi/sdk-common/orders' import { publicProcedure } from '../TRPC' @@ -9,7 +9,7 @@ const inputSchema = z.custom((parameters) => parameters !== export const getRefinanceSimulation = publicProcedure .input(inputSchema) - .query(async (opts): Promise> => { + .query(async (opts): Promise> => { const args: RefinanceParameters = opts.input const dependencies: RefinanceDependencies = { @@ -18,6 +18,6 @@ export const getRefinanceSimulation = publicProcedure getSummerFee: () => Percentage.createFrom({ percentage: 0 }), } - const simulation = await refinaceLendingToLending(args, dependencies) + const simulation = await refinance(args, dependencies) return simulation }) diff --git a/sdk/sdk-server/src/handlers/getSwapData.ts b/sdk/sdk-server/src/handlers/getSwapData.ts index fbb46a3809..6bb6f21670 100644 --- a/sdk/sdk-server/src/handlers/getSwapData.ts +++ b/sdk/sdk-server/src/handlers/getSwapData.ts @@ -1,6 +1,6 @@ import { z } from 'zod' import { ChainInfo, Token, TokenAmount, Address, Percentage } from '@summerfi/sdk-common/common' -import { SwapData } from '@summerfi/swap-common/types' +import { SwapData } from '@summerfi/sdk-common/swap' import { publicProcedure } from '../TRPC' export const getSwapDataExactInput = publicProcedure diff --git a/sdk/sdk-server/src/handlers/getSwapQuote.ts b/sdk/sdk-server/src/handlers/getSwapQuote.ts index 445fe9b16d..8da0d294ef 100644 --- a/sdk/sdk-server/src/handlers/getSwapQuote.ts +++ b/sdk/sdk-server/src/handlers/getSwapQuote.ts @@ -1,6 +1,6 @@ import { z } from 'zod' import { ChainInfo, Token, TokenAmount } from '@summerfi/sdk-common/common' -import { QuoteData } from '@summerfi/swap-common/types' +import { QuoteData } from '@summerfi/sdk-common/swap' import { publicProcedure } from '../TRPC' export const getSwapQuoteExactInput = publicProcedure diff --git a/sdk/sdk-server/tests/utils/TestUtils.ts b/sdk/sdk-server/tests/utils/TestUtils.ts index 121f6ce231..405d7dce2a 100644 --- a/sdk/sdk-server/tests/utils/TestUtils.ts +++ b/sdk/sdk-server/tests/utils/TestUtils.ts @@ -12,7 +12,7 @@ export const createTestContext = (opts: ContextOptions): SDKAppContext => { swapManager: {} as any, configProvider: {} as any, protocolsRegistry: {} as any, - protocolManager: {} as any + protocolManager: {} as any, } } diff --git a/sdk/simulator-service/package.json b/sdk/simulator-service/package.json index dce9b406db..52aeebc669 100644 --- a/sdk/simulator-service/package.json +++ b/sdk/simulator-service/package.json @@ -4,9 +4,9 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "exports": { - ".": { - "import": "./src/index.ts", - "types": "./src/index.d.ts" + "./*": { + "import": "./src/*/index.ts", + "types": "./src/*/index.d.ts" } }, "scripts": { diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 035ee7466a..98ee84ff17 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -21,9 +21,9 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim toTokenAmount: step.inputs.toTokenAmount, slippage: step.inputs.slippage, fee: step.inputs.fee, - estimatedGas: step.inputs.estimatedGas - } + estimatedGas: step.inputs.estimatedGas, + }, }, balances: balanceWithToToken, } -} \ No newline at end of file +} diff --git a/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts b/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts index 2290281a0c..6a80c95fd9 100644 --- a/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts +++ b/sdk/simulator-service/src/implementation/utils/BalanceUtils.ts @@ -1,29 +1,29 @@ import { TokenAmount, type Token } from '@summerfi/sdk-common/common' export function getTokenBalance(token: Token, balances: Record): TokenAmount { - return balances[token.address.value] || TokenAmount.createFrom({ amount: '0', token }) + return balances[token.address.value] || TokenAmount.createFrom({ amount: '0', token }) } export function addBalance( - amount: TokenAmount, - balance: Record, + amount: TokenAmount, + balance: Record, ): Record { - return { - ...balance, - [amount.token.address.value]: balance[amount.token.address.value] - ? balance[amount.token.address.value].add(amount) - : amount, - } + return { + ...balance, + [amount.token.address.value]: balance[amount.token.address.value] + ? balance[amount.token.address.value].add(amount) + : amount, + } } export function subtractBalance( - amount: TokenAmount, - balance: Record, + amount: TokenAmount, + balance: Record, ): Record { - return { - ...balance, - [amount.token.address.value]: balance[amount.token.address.value] - ? balance[amount.token.address.value].subtract(amount) - : TokenAmount.createFrom({ amount: amount.toBN().negated().toString(), token: amount.token }), - } -} \ No newline at end of file + return { + ...balance, + [amount.token.address.value]: balance[amount.token.address.value] + ? balance[amount.token.address.value].subtract(amount) + : TokenAmount.createFrom({ amount: amount.toBN().negated().toString(), token: amount.token }), + } +} diff --git a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts index 9facfba1f5..6d663a9f0c 100644 --- a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts +++ b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts @@ -1,36 +1,36 @@ import type { - ReferenceableField, - SimulationStrategy, - ValueReference, + ReferenceableField, + SimulationStrategy, + ValueReference, } from '@summerfi/sdk-common/simulation' import type { Tail } from '../../interfaces/helperTypes' export function makeStrategy(strategy: T): T { - return strategy + return strategy } export function isValueReference(value: ReferenceableField): value is ValueReference { - return ( - (value as ValueReference).path !== undefined && - (value as ValueReference).estimatedValue !== undefined - ) + return ( + (value as ValueReference).path !== undefined && + (value as ValueReference).estimatedValue !== undefined + ) } export function getReferencedValue(referenceableValue: ReferenceableField): T { - if (isValueReference(referenceableValue)) { - return referenceableValue.estimatedValue - } - return referenceableValue + if (isValueReference(referenceableValue)) { + return referenceableValue.estimatedValue + } + return referenceableValue } // eslint-disable-next-line @typescript-eslint/no-explicit-any export function tail(arr: T): Tail { - const [, ...rest] = arr - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return rest as any as Tail + const [, ...rest] = arr + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return rest as any as Tail } // eslint-disable-next-line @typescript-eslint/no-explicit-any export function head(arr: T): T[0] { - return arr[0] + return arr[0] } diff --git a/sdk/simulator-service/src/implementation/utils/index.ts b/sdk/simulator-service/src/implementation/utils/index.ts index e4f8e159b9..297bef7257 100644 --- a/sdk/simulator-service/src/implementation/utils/index.ts +++ b/sdk/simulator-service/src/implementation/utils/index.ts @@ -1,2 +1,2 @@ export { getTokenBalance, addBalance, subtractBalance } from './BalanceUtils' -export { makeStrategy, isValueReference, getReferencedValue, tail, head } from './SimulatorUtils' \ No newline at end of file +export { makeStrategy, isValueReference, getReferencedValue, tail, head } from './SimulatorUtils' diff --git a/sdk/simulator-service/src/interfaces/simulation.ts b/sdk/simulator-service/src/interfaces/simulation.ts index b4153d7fb4..a9ad7e21d3 100644 --- a/sdk/simulator-service/src/interfaces/simulation.ts +++ b/sdk/simulator-service/src/interfaces/simulation.ts @@ -7,4 +7,4 @@ export interface ISimulationState { balances: Record positions: Record steps: Record -} \ No newline at end of file +} diff --git a/sdk/simulator-service/src/interfaces/steps.ts b/sdk/simulator-service/src/interfaces/steps.ts index 8948ec8353..adcea67930 100644 --- a/sdk/simulator-service/src/interfaces/steps.ts +++ b/sdk/simulator-service/src/interfaces/steps.ts @@ -1,6 +1,6 @@ import { SimulationStrategy, ValueReference, steps } from '@summerfi/sdk-common/simulation' import { EmptyArray, Where } from './helperTypes' -import { SimulationState } from './simulation' +import { ISimulationState } from './simulation' export type StepOutputProcessor = (step: Omit) => Promise export type StepOutputProcessors = { @@ -9,8 +9,8 @@ export type StepOutputProcessors = { export type StepsWithoutOutputs = Omit export type StateReducer = ( step: T, - state: SimulationState, -) => SimulationState + state: ISimulationState, +) => ISimulationState export type StateReducers = { [Type in steps.Steps['type']]: StateReducer> } @@ -21,7 +21,7 @@ export type NextFunction< > = Schema extends EmptyArray ? never : (ctx: { - state: SimulationState + state: ISimulationState // eslint-disable-next-line @typescript-eslint/no-explicit-any getReference: (path: [string, string]) => ValueReference }) => Promise, 'outputs'>> diff --git a/sdk/simulator-service/src/strategies/index.ts b/sdk/simulator-service/src/strategies/index.ts new file mode 100644 index 0000000000..0ee38d8f24 --- /dev/null +++ b/sdk/simulator-service/src/strategies/index.ts @@ -0,0 +1 @@ +export * from './refinance' diff --git a/sdk/simulator-service/src/strategies/refinance/Refinance.ts b/sdk/simulator-service/src/strategies/refinance/Refinance.ts index f6fd619916..29ba7bb590 100644 --- a/sdk/simulator-service/src/strategies/refinance/Refinance.ts +++ b/sdk/simulator-service/src/strategies/refinance/Refinance.ts @@ -1,8 +1,8 @@ import { - FlashloanProvider, - ISimulation, - SimulationSteps, - SimulationType, + FlashloanProvider, + ISimulation, + SimulationSteps, + SimulationType, } from '@summerfi/sdk-common/simulation' import { getReferencedValue } from '../../implementation/utils' import { Simulator } from '../../implementation/simulator-engine' @@ -10,144 +10,145 @@ import { TokenAmount } from '@summerfi/sdk-common/common' import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' import { RefinanceParameters } from '@summerfi/sdk-common/orders' import { isLendingPool } from '@summerfi/sdk-common/protocols' -import {refinanceLendingToLendingStrategy} from "./Strategy"; -import {RefinanceDependencies} from "./Types"; +import { refinanceLendingToLendingStrategy } from './Strategy' +import { RefinanceDependencies } from './Types' export async function refinance( - args: RefinanceParameters, - dependencies: RefinanceDependencies, + args: RefinanceParameters, + dependencies: RefinanceDependencies, ): Promise> { - // args validation - if (!isLendingPool(args.targetPool)) { - throw new Error('Target pool is not a lending pool') - } + // args validation + if (!isLendingPool(args.targetPool)) { + throw new Error('Target pool is not a lending pool') + } - const FLASHLOAN_MARGIN = 0.001 - const flashloanAmount = args.position.debtAmount.multiply(FLASHLOAN_MARGIN) - const simulator = Simulator.create(refinanceLendingToLendingStrategy) + const FLASHLOAN_MARGIN = 0.001 + const flashloanAmount = args.position.debtAmount.multiply(FLASHLOAN_MARGIN) + const simulator = Simulator.create(refinanceLendingToLendingStrategy) - // TODO: Update this check - const isCollateralSwapSkipped = - args.targetPool.collaterals[args.position.collateralAmount.token.address.value] !== undefined - const isDebtSwapSkipped = - args.targetPool.debts[args.position.debtAmount.token.address.value] !== undefined + // TODO: Update this check + const isCollateralSwapSkipped = + args.targetPool.collaterals[args.position.collateralAmount.token.address.value] !== undefined + const isDebtSwapSkipped = + args.targetPool.debts[args.position.debtAmount.token.address.value] !== undefined - // let debtSwapQuote: Quote | undefined - // TODO: implement case with swaps - // if (!isDebtSwapSkipped) { - // debtSwapQuote = await dependencies.getQuote({ - // from: args.targetPool.debtTokens[0], - // to: args.position.debtAmount.token, - // slippage: args.slippage, - // fee: 0, - // }) - // } + // let debtSwapQuote: Quote | undefined + // TODO: implement case with swaps + // if (!isDebtSwapSkipped) { + // debtSwapQuote = await dependencies.getQuote({ + // from: args.targetPool.debtTokens[0], + // to: args.position.debtAmount.token, + // slippage: args.slippage, + // fee: 0, + // }) + // } - // TODO: read debt amount from chain (special step: ReadDebtAmount) - // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, - // before or after the swap, it influences actual call to oneInch api - const simulation = await simulator - .next(async () => ({ - name: 'Flashloan', - type: SimulationSteps.Flashloan, - inputs: { - amount: flashloanAmount, - provider: FlashloanProvider.Maker, - }, - })) - .next(async () => ({ - name: 'PaybackWithdrawFromSource', - type: SimulationSteps.PaybackWithdraw, - inputs: { - paybackAmount: TokenAmount.createFrom({ - amount: Number.MAX_SAFE_INTEGER.toString(), - token: args.position.debtAmount.token, - }), - withdrawAmount: TokenAmount.createFrom({ - amount: Number.MAX_SAFE_INTEGER.toString(), - token: args.position.collateralAmount.token, - }), - position: args.position, - }, - })) - .next(async () => ({ - name: 'CollateralSwap', - type: SimulationSteps.Swap, - inputs: { - ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: args.position.pool.protocol.chainInfo, - fromAmount: args.position.collateralAmount, - toToken: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, - })), - slippage: args.slippage, - fee: dependencies.getSummerFee(), - }, - skip: isCollateralSwapSkipped, - })) - .next(async (ctx) => ({ - name: 'DepositBorrowToTarget', - type: SimulationSteps.DepositBorrow, - inputs: { - depositAmount: ctx.getReference( - isCollateralSwapSkipped - ? ['PaybackWithdrawFromSource', 'withdrawAmount'] - : ['CollateralSwap', 'receivedAmount'], - ), - borrowAmount: args.position.debtAmount, // TODO figure the debt amount - position: newEmptyPositionFromPool( - args.targetPool, - args.position.debtAmount.token.address.value, - args.position.collateralAmount.token.address.value, - ), - }, - })) - .next(async (ctx) => ({ - name: 'DebtSwap', - type: SimulationSteps.Swap, - inputs: { - ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: args.position.pool.protocol.chainInfo, - fromAmount: getReferencedValue( - ctx.getReference(['DepositBorrowToTarget', 'borrowAmount']), - ), - toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, - })), - slippage: args.slippage, - fee: dependencies.getSummerFee(), - }, - skip: isDebtSwapSkipped, - })) - .next(async () => ({ - name: 'RepayFlashloan', - type: SimulationSteps.RepayFlashloan, - inputs: { - amount: args.position.debtAmount, // TODO add some amount - }, - })) - .next(async () => ({ - name: 'ReturnFunds', - type: SimulationSteps.ReturnFunds, - inputs: { - token: - args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, - }, - skip: isDebtSwapSkipped, - })) - .run() + // TODO: read debt amount from chain (special step: ReadDebtAmount) + // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, + // before or after the swap, it influences actual call to oneInch api + const simulation = await simulator + .next(async () => ({ + name: 'Flashloan', + type: SimulationSteps.Flashloan, + inputs: { + amount: flashloanAmount, + provider: FlashloanProvider.Maker, + }, + })) + .next(async () => ({ + name: 'PaybackWithdrawFromSource', + type: SimulationSteps.PaybackWithdraw, + inputs: { + paybackAmount: TokenAmount.createFrom({ + amount: Number.MAX_SAFE_INTEGER.toString(), + token: args.position.debtAmount.token, + }), + withdrawAmount: TokenAmount.createFrom({ + amount: Number.MAX_SAFE_INTEGER.toString(), + token: args.position.collateralAmount.token, + }), + position: args.position, + }, + })) + .next(async () => ({ + name: 'CollateralSwap', + type: SimulationSteps.Swap, + inputs: { + ...(await dependencies.swapManager.getSwapQuoteExactInput({ + chainInfo: args.position.pool.protocol.chainInfo, + fromAmount: args.position.collateralAmount, + toToken: + args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + })), + slippage: args.slippage, + fee: dependencies.getSummerFee(), + }, + skip: isCollateralSwapSkipped, + })) + .next(async (ctx) => ({ + name: 'DepositBorrowToTarget', + type: SimulationSteps.DepositBorrow, + inputs: { + depositAmount: ctx.getReference( + isCollateralSwapSkipped + ? ['PaybackWithdrawFromSource', 'withdrawAmount'] + : ['CollateralSwap', 'receivedAmount'], + ), + borrowAmount: args.position.debtAmount, // TODO figure the debt amount + position: newEmptyPositionFromPool( + args.targetPool, + args.position.debtAmount.token.address.value, + args.position.collateralAmount.token.address.value, + ), + }, + })) + .next(async (ctx) => ({ + name: 'DebtSwap', + type: SimulationSteps.Swap, + inputs: { + ...(await dependencies.swapManager.getSwapQuoteExactInput({ + chainInfo: args.position.pool.protocol.chainInfo, + fromAmount: getReferencedValue( + ctx.getReference(['DepositBorrowToTarget', 'borrowAmount']), + ), + toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, + })), + slippage: args.slippage, + fee: dependencies.getSummerFee(), + }, + skip: isDebtSwapSkipped, + })) + .next(async () => ({ + name: 'RepayFlashloan', + type: SimulationSteps.RepayFlashloan, + inputs: { + amount: args.position.debtAmount, // TODO add some amount + }, + })) + .next(async () => ({ + name: 'ReturnFunds', + type: SimulationSteps.ReturnFunds, + inputs: { + token: + args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + }, + skip: isDebtSwapSkipped, + })) + .run() - const targetPosition = Object.values(simulation.positions).find( - (p) => p.pool.protocol === args.targetPool.protocol, - ) + const targetPosition = Object.values(simulation.positions).find( + (p) => p.pool.protocol === args.targetPool.protocol, + ) - if (!targetPosition) { - throw new Error('Target position not found') - } + if (!targetPosition) { + throw new Error('Target position not found') + } - return { - simulationType: SimulationType.Refinance, - sourcePosition: args.position, - targetPosition, - swaps: Object.values(simulation.swaps), - steps: Object.values(simulation.steps), - } + return { + simulationType: SimulationType.Refinance, + sourcePosition: args.position, + targetPosition, + swaps: Object.values(simulation.swaps), + steps: Object.values(simulation.steps), + } } diff --git a/sdk/simulator-service/src/strategies/refinance/Strategy.ts b/sdk/simulator-service/src/strategies/refinance/Strategy.ts index 4edfe25baa..aca24441ce 100644 --- a/sdk/simulator-service/src/strategies/refinance/Strategy.ts +++ b/sdk/simulator-service/src/strategies/refinance/Strategy.ts @@ -2,34 +2,34 @@ import { SimulationSteps } from '@summerfi/sdk-common/simulation' import { makeStrategy } from '../../implementation/utils' export const refinanceLendingToLendingStrategy = makeStrategy([ - { - step: SimulationSteps.Flashloan, - optional: false, - }, - { - step: SimulationSteps.PaybackWithdraw, - optional: false, - }, - { - step: SimulationSteps.Swap, - optional: true, - }, - { - step: SimulationSteps.DepositBorrow, - optional: false, - }, - { - step: SimulationSteps.Swap, - optional: true, - }, - { - step: SimulationSteps.RepayFlashloan, - optional: false, - }, - { - // In case of target debt being different then source debt we need a swap, - // We cannot forsee the exact amount of the swap, so we need to return excess tokens to user - step: SimulationSteps.ReturnFunds, - optional: true, - }, -]) \ No newline at end of file + { + step: SimulationSteps.Flashloan, + optional: false, + }, + { + step: SimulationSteps.PaybackWithdraw, + optional: false, + }, + { + step: SimulationSteps.Swap, + optional: true, + }, + { + step: SimulationSteps.DepositBorrow, + optional: false, + }, + { + step: SimulationSteps.Swap, + optional: true, + }, + { + step: SimulationSteps.RepayFlashloan, + optional: false, + }, + { + // In case of target debt being different then source debt we need a swap, + // We cannot forsee the exact amount of the swap, so we need to return excess tokens to user + step: SimulationSteps.ReturnFunds, + optional: true, + }, +]) diff --git a/sdk/simulator-service/src/strategies/refinance/Types.ts b/sdk/simulator-service/src/strategies/refinance/Types.ts index 88a341412a..0046a0a98e 100644 --- a/sdk/simulator-service/src/strategies/refinance/Types.ts +++ b/sdk/simulator-service/src/strategies/refinance/Types.ts @@ -2,6 +2,6 @@ import { Percentage } from '@summerfi/sdk-common/common' import { type ISwapManager } from '@summerfi/swap-common/interfaces' export interface RefinanceDependencies { - swapManager: ISwapManager - getSummerFee: () => Percentage -} \ No newline at end of file + swapManager: ISwapManager + getSummerFee: () => Percentage +} diff --git a/sdk/simulator-service/tests/mocks/contextMock.ts b/sdk/simulator-service/tests/mocks/contextMock.ts index fd5893f60b..f6170fb0ff 100644 --- a/sdk/simulator-service/tests/mocks/contextMock.ts +++ b/sdk/simulator-service/tests/mocks/contextMock.ts @@ -1,5 +1,5 @@ import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' -import { SwapProviderType } from '@summerfi/swap-common/enums' +import { SwapProviderType } from '@summerfi/sdk-common/swap' async function getSwapDataExactInput(params: { chainInfo: ChainInfo diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index bda396a7c4..8595768a21 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -1,6 +1,6 @@ import { Percentage } from '@summerfi/sdk-common/common' -import { refinanceLendingToLending } from '../src/implementation/strategies' -import { Simulation, SimulationSteps, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationSteps, SimulationType } from '@summerfi/sdk-common/simulation' +import { refinance } from '../src/strategies' import { otherTestCollateral, otherTestDebt, @@ -12,9 +12,9 @@ import { mockRefinanceContext } from './mocks/contextMock' describe('Refinance', () => { describe('to the position with the same collateral and debt (no swaps)', () => { - let simulation: Simulation + let simulation: ISimulation beforeAll(async () => { - simulation = await refinanceLendingToLending( + simulation = await refinance( { position: testSourcePosition, targetPool: testTargetLendingPool, @@ -58,9 +58,9 @@ describe('Refinance', () => { }) describe.skip('to the position with the different collateral and debt (with swaps)', () => { - let simulation: Simulation + let simulation: ISimulation beforeAll(async () => { - simulation = await refinanceLendingToLending( + simulation = await refinance( { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, diff --git a/sdk/swap-common/src/interfaces/ISwapManager.ts b/sdk/swap-common/src/interfaces/ISwapManager.ts index c509ff19be..f1e50e2f2f 100644 --- a/sdk/swap-common/src/interfaces/ISwapManager.ts +++ b/sdk/swap-common/src/interfaces/ISwapManager.ts @@ -5,11 +5,7 @@ import type { Token, Address, } from '@summerfi/sdk-common/common' -import type { - QuoteData, - SwapData -} from '@summerfi/sdk-common/swap' - +import type { QuoteData, SwapData } from '@summerfi/sdk-common/swap' /** * @name ISwapManager diff --git a/sdk/swap-common/src/interfaces/ISwapProvider.ts b/sdk/swap-common/src/interfaces/ISwapProvider.ts index 437642e18f..e297b823b5 100644 --- a/sdk/swap-common/src/interfaces/ISwapProvider.ts +++ b/sdk/swap-common/src/interfaces/ISwapProvider.ts @@ -1,9 +1,6 @@ import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' -import type { SwapProviderType } from '../enums/SwapProviderType' -import type { - QuoteData, - SwapData -} from '@summerfi/sdk-common/swap' +import { SwapProviderType } from '@summerfi/sdk-common/swap' +import type { QuoteData, SwapData } from '@summerfi/sdk-common/swap' /** * @name ISwapProvider diff --git a/sdk/swap-service/e2e/oneinch.spec.ts b/sdk/swap-service/e2e/oneinch.spec.ts index 77ed08f11b..ca1626cf2d 100644 --- a/sdk/swap-service/e2e/oneinch.spec.ts +++ b/sdk/swap-service/e2e/oneinch.spec.ts @@ -8,8 +8,7 @@ import { type ChainInfo, } from '@summerfi/sdk-common/common' import { subtractPercentage } from '@summerfi/sdk-common/utils' -import { SwapProviderType } from '@summerfi/swap-common/enums' -import { QuoteData, SwapData } from '@summerfi/swap-common/types' +import { QuoteData, SwapData, SwapProviderType } from '@summerfi/sdk-common/swap' import { SwapManagerFactory } from '../src/implementation/SwapManagerFactory' describe('OneInch | SwapManager | Integration', () => { diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index d1e42fda5b..f1594ef6cb 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -9,8 +9,7 @@ import type { import { ChainId } from '@summerfi/sdk-common/common' import { ISwapProvider, ISwapManager } from '@summerfi/swap-common/interfaces' -import { QuoteData, SwapData } from '@summerfi/swap-common/types' -import { SwapProviderType } from '@summerfi/swap-common/enums' +import { QuoteData, SwapData, SwapProviderType } from '@summerfi/sdk-common/swap' export type SwapManagerProviderConfig = { provider: ISwapProvider diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index ebe6dd9f9a..9f36fe6877 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -1,12 +1,12 @@ import { ISwapProvider } from '@summerfi/swap-common/interfaces' -import { SwapProviderType } from '@summerfi/swap-common/enums' -import { SwapData, SwapRoute, QuoteData } from '@summerfi/swap-common/types' +import { SwapProviderType, SwapData, SwapRoute, QuoteData } from '@summerfi/sdk-common/swap' import { OneInchAuthHeader, OneInchAuthHeaderKey, OneInchQuoteResponse, OneInchSwapProviderConfig, - OneInchSwapResponse, OneInchSwapRoute, + OneInchSwapResponse, + OneInchSwapRoute, } from './types' import { HexData } from '@summerfi/sdk-common/common/aliases' import fetch from 'node-fetch' @@ -155,11 +155,15 @@ export class OneInchSwapProvider implements ISwapProvider { } private _extractSwapRoutes(protocols: OneInchSwapRoute[]): SwapRoute[] { - return protocols.map(route => (route.map(hop => hop.map(hopPart => ({ - name: hopPart.name, - part: Percentage.createFrom({percentage: hopPart.part}), - fromTokenAddress: Address.createFrom({value: hopPart.fromTokenAddress as HexData}), - toTokenAddress: Address.createFrom({value: hopPart.toTokenAddress as HexData}) - }))))) + return protocols.map((route) => + route.map((hop) => + hop.map((hopPart) => ({ + name: hopPart.name, + part: Percentage.createFrom({ percentage: hopPart.part }), + fromTokenAddress: Address.createFrom({ value: hopPart.fromTokenAddress as HexData }), + toTokenAddress: Address.createFrom({ value: hopPart.toTokenAddress as HexData }), + })), + ), + ) } } diff --git a/sdk/swap-service/src/implementation/oneinch/types.ts b/sdk/swap-service/src/implementation/oneinch/types.ts index fd4867a989..e143763b4d 100644 --- a/sdk/swap-service/src/implementation/oneinch/types.ts +++ b/sdk/swap-service/src/implementation/oneinch/types.ts @@ -40,8 +40,8 @@ export type OneInchSwapRoute = OneInchSwapHop[] type OneInchSwapHop = OneInchSwapHopPart[] type OneInchSwapHopPart = { - name: string, + name: string part: number fromTokenAddress: string toTokenAddress: string -} \ No newline at end of file +} From e0d2d7c1b9d13f3b80f602e1f72b0e8a8ff0581c Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 26 Mar 2024 09:05:20 +0000 Subject: [PATCH 06/26] refactor: strip estimatedGas from SimulatedSwapData type --- .../tests/builders/SwapActionBuilder.spec.ts | 1 - sdk/sdk-common/src/simulation/Steps.ts | 1 - sdk/sdk-common/src/swap/SimulatedSwapData.ts | 7 ++++++- .../implementation/simulator-engine/reducer/swapReducer.ts | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts b/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts index 80e2bd5964..c6aba50a6a 100644 --- a/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts +++ b/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts @@ -60,7 +60,6 @@ describe('Swap Action Builder', () => { inputs: { provider: SwapProviderType.OneInch, routes: [], - estimatedGas: '', fromTokenAmount: fromAmount, toTokenAmount: toAmount, fee: fee, diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index 886387f7a9..db8dc1b77d 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -63,7 +63,6 @@ export interface SwapStep routes: SwapRoute[] fromTokenAmount: TokenAmount toTokenAmount: TokenAmount - estimatedGas: string slippage: Percentage fee: Percentage }, diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts index 27f968386c..b8f49bd260 100644 --- a/sdk/sdk-common/src/swap/SimulatedSwapData.ts +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -1,7 +1,12 @@ import { Percentage } from '@summerfi/sdk-common/common' import { QuoteData } from './QuoteData' -export type SimulatedSwapData = QuoteData & { +/** + * Represents the data returned for each Swap in simulation. + * It is derived from the `QuoteData` type with the `estimatedGas` field omitted, + * as gas estimation is not relevant for simulation purposes. + */ +export type SimulatedSwapData = Omit & { slippage: Percentage fee: Percentage } diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 98ee84ff17..bd05c7b018 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -21,7 +21,6 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim toTokenAmount: step.inputs.toTokenAmount, slippage: step.inputs.slippage, fee: step.inputs.fee, - estimatedGas: step.inputs.estimatedGas, }, }, balances: balanceWithToToken, From 5668608fb930b1bd3e622c66f1d7e3ea7120fe0a Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 26 Mar 2024 10:32:00 +0000 Subject: [PATCH 07/26] feat: determine if Swaps should be included based on targetCollateral & targetDebt RefinanceParams --- .../refinance/RefinanceParameters.ts | 7 +++---- .../src/strategies/refinance/Refinance.ts | 20 ++++--------------- sdk/simulator-service/tests/simulator.test.ts | 14 +++++++++++++ 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts b/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts index 6ec48f0eb3..7d1b6c0513 100644 --- a/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts +++ b/sdk/sdk-common/src/orders/interfaces/refinance/RefinanceParameters.ts @@ -1,6 +1,6 @@ import type { Percentage } from '../../../common/implementation/Percentage' import type { Position } from '../../../common/implementation/Position' -// import type { Address } from '../../../common/implementation/Address' +import type { Address } from '../../../common/implementation/Address' import type { LendingPool } from '../../../protocols/implementation/LendingPool' /** @@ -10,9 +10,8 @@ import type { LendingPool } from '../../../protocols/implementation/LendingPool' export interface RefinanceParameters { position: Position targetPool: LendingPool - // TODO: Implement means of determining if a swap is required. Follow-up PR - // targetCollateral: Address - // targetDebt: Address + targetCollateral: Address + targetDebt: Address slippage: Percentage } diff --git a/sdk/simulator-service/src/strategies/refinance/Refinance.ts b/sdk/simulator-service/src/strategies/refinance/Refinance.ts index 29ba7bb590..38efe2b528 100644 --- a/sdk/simulator-service/src/strategies/refinance/Refinance.ts +++ b/sdk/simulator-service/src/strategies/refinance/Refinance.ts @@ -26,22 +26,10 @@ export async function refinance( const flashloanAmount = args.position.debtAmount.multiply(FLASHLOAN_MARGIN) const simulator = Simulator.create(refinanceLendingToLendingStrategy) - // TODO: Update this check - const isCollateralSwapSkipped = - args.targetPool.collaterals[args.position.collateralAmount.token.address.value] !== undefined - const isDebtSwapSkipped = - args.targetPool.debts[args.position.debtAmount.token.address.value] !== undefined - - // let debtSwapQuote: Quote | undefined - // TODO: implement case with swaps - // if (!isDebtSwapSkipped) { - // debtSwapQuote = await dependencies.getQuote({ - // from: args.targetPool.debtTokens[0], - // to: args.position.debtAmount.token, - // slippage: args.slippage, - // fee: 0, - // }) - // } + const isCollateralSwapSkipped = args.position.collateralAmount.token.address.equals( + args.targetCollateral, + ) + const isDebtSwapSkipped = args.position.debtAmount.token.address.equals(args.targetDebt) // TODO: read debt amount from chain (special step: ReadDebtAmount) // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index 8595768a21..0c46d16da5 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -18,6 +18,13 @@ describe('Refinance', () => { { position: testSourcePosition, targetPool: testTargetLendingPool, + targetDebt: + testTargetLendingPool.debts[testSourcePosition.debtAmount.token.address.value].token + .address, + targetCollateral: + testTargetLendingPool.collaterals[ + testSourcePosition.collateralAmount.token.address.value + ].token.address, slippage: Percentage.createFrom({ percentage: 1 }), }, mockRefinanceContext, @@ -64,6 +71,13 @@ describe('Refinance', () => { { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, + targetDebt: + testTargetLendingPool.debts[testSourcePosition.debtAmount.token.address.value].token + .address, + targetCollateral: + testTargetLendingPool.collaterals[ + testSourcePosition.collateralAmount.token.address.value + ].token.address, slippage: Percentage.createFrom({ percentage: 1 }), }, mockRefinanceContext, From 7d0931922eecdb0d27b23b402e097806c6996322 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 26 Mar 2024 11:28:49 +0000 Subject: [PATCH 08/26] test: WIP work on refinance tests with swap --- .../src/strategies/refinance/Refinance.ts | 11 ++++---- .../tests/mocks/testSourcePosition.ts | 28 +++++++++---------- sdk/simulator-service/tests/simulator.test.ts | 14 +++++----- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/sdk/simulator-service/src/strategies/refinance/Refinance.ts b/sdk/simulator-service/src/strategies/refinance/Refinance.ts index 38efe2b528..9caceff454 100644 --- a/sdk/simulator-service/src/strategies/refinance/Refinance.ts +++ b/sdk/simulator-service/src/strategies/refinance/Refinance.ts @@ -65,8 +65,7 @@ export async function refinance( ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: args.position.pool.protocol.chainInfo, fromAmount: args.position.collateralAmount, - toToken: - args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + toToken: args.targetPool.collaterals[args.targetCollateral.value].token, })), slippage: args.slippage, fee: dependencies.getSummerFee(), @@ -82,11 +81,11 @@ export async function refinance( ? ['PaybackWithdrawFromSource', 'withdrawAmount'] : ['CollateralSwap', 'receivedAmount'], ), - borrowAmount: args.position.debtAmount, // TODO figure the debt amount + borrowAmount: args.position.debtAmount, // TODO: figure the debt amount after Swap position: newEmptyPositionFromPool( args.targetPool, - args.position.debtAmount.token.address.value, - args.position.collateralAmount.token.address.value, + args.targetDebt.value, + args.targetCollateral.value, ), }, })) @@ -99,7 +98,7 @@ export async function refinance( fromAmount: getReferencedValue( ctx.getReference(['DepositBorrowToTarget', 'borrowAmount']), ), - toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, + toToken: args.targetPool.debts[args.targetDebt.value].token, })), slippage: args.slippage, fee: dependencies.getSummerFee(), diff --git a/sdk/simulator-service/tests/mocks/testSourcePosition.ts b/sdk/simulator-service/tests/mocks/testSourcePosition.ts index ca238ef6b7..55e7b17ec7 100644 --- a/sdk/simulator-service/tests/mocks/testSourcePosition.ts +++ b/sdk/simulator-service/tests/mocks/testSourcePosition.ts @@ -140,29 +140,29 @@ export const testTargetLendingPool = new LendingPool({ export const testTargetLendingPoolRequiredSwaps = new LendingPool({ collaterals: { - [testCollateral.address.value]: { - token: testCollateral, - price: Price.createFrom({ value: '100', baseToken: testCollateral }), - priceUSD: Price.createFrom({ value: '100', baseToken: testCollateral }), + [testDebt.address.value]: { + token: testDebt, + price: Price.createFrom({ value: '100', baseToken: testDebt }), + priceUSD: Price.createFrom({ value: '100', baseToken: testDebt }), liquidationThreshold: RiskRatio.createFrom({ ratio: Percentage.createFrom({ percentage: 80 }), type: RiskRatio.type.LTV, }), - maxSupply: TokenAmount.createFrom({ token: testCollateral, amount: '10000000' }), - tokensLocked: TokenAmount.createFrom({ token: testCollateral, amount: '1000000' }), + maxSupply: TokenAmount.createFrom({ token: testDebt, amount: '10000000' }), + tokensLocked: TokenAmount.createFrom({ token: testDebt, amount: '1000000' }), liquidationPenalty: Percentage.createFrom({ percentage: 5 }), }, }, debts: { - [testDebt.address.value]: { - token: testDebt, - price: Price.createFrom({ value: '100', baseToken: testDebt }), - priceUSD: Price.createFrom({ value: '100', baseToken: testDebt }), + [testCollateral.address.value]: { + token: testCollateral, + price: Price.createFrom({ value: '100', baseToken: testCollateral }), + priceUSD: Price.createFrom({ value: '100', baseToken: testCollateral }), rate: Percentage.createFrom({ percentage: 5 }), - totalBorrowed: TokenAmount.createFrom({ token: testDebt, amount: '100000' }), - debtCeiling: TokenAmount.createFrom({ token: testDebt, amount: '1000000' }), - debtAvailable: TokenAmount.createFrom({ token: testDebt, amount: '100000' }), - dustLimit: TokenAmount.createFrom({ token: testDebt, amount: '100' }), + totalBorrowed: TokenAmount.createFrom({ token: testCollateral, amount: '100000' }), + debtCeiling: TokenAmount.createFrom({ token: testCollateral, amount: '1000000' }), + debtAvailable: TokenAmount.createFrom({ token: testCollateral, amount: '100000' }), + dustLimit: TokenAmount.createFrom({ token: testCollateral, amount: '100' }), originationFee: Percentage.createFrom({ percentage: 1 }), }, }, diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index 0c46d16da5..3b8e8ce761 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -64,27 +64,27 @@ describe('Refinance', () => { }) }) - describe.skip('to the position with the different collateral and debt (with swaps)', () => { + describe.only('to the position with the different collateral and debt (with swaps)', () => { let simulation: ISimulation beforeAll(async () => { + // Swapped the tokens around to force two swaps + console.log("targetDebt", testTargetLendingPoolRequiredSwaps.debts[testSourcePosition.collateralAmount.token.address.value].token.address) + console.log("targetColl", testTargetLendingPoolRequiredSwaps.collaterals[testSourcePosition.debtAmount.token.address.value].token.address) simulation = await refinance( { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, targetDebt: - testTargetLendingPool.debts[testSourcePosition.debtAmount.token.address.value].token - .address, + testTargetLendingPoolRequiredSwaps.debts[testSourcePosition.collateralAmount.token.address.value].token.address, targetCollateral: - testTargetLendingPool.collaterals[ - testSourcePosition.collateralAmount.token.address.value - ].token.address, + testTargetLendingPoolRequiredSwaps.collaterals[testSourcePosition.debtAmount.token.address.value].token.address, slippage: Percentage.createFrom({ percentage: 1 }), }, mockRefinanceContext, ) }) - it('should include two swap steps', async () => { + it.only('should include two swap steps', async () => { const steps = simulation.steps.filter((step) => !step.skip).map((step) => step.type) expect(steps.length).toBe(2) From 61ec9b15deaa34ec97600367195947c7107dc423 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 26 Mar 2024 15:50:36 +0000 Subject: [PATCH 09/26] feat: extend simulatedSwapData to include offerPrice, marketPrice and priceImpact --- .../src/common/implementation/Price.ts | 11 ++- .../src/common/implementation/TokenAmount.ts | 4 ++ sdk/sdk-common/src/simulation/Steps.ts | 2 + sdk/sdk-common/src/swap/SimulatedSwapData.ts | 13 ++-- sdk/sdk-common/src/swap/SpotData.ts | 11 +++ sdk/sdk-common/src/swap/index.ts | 1 + .../src/handlers/getRefinanceSimulation.ts | 7 +- .../simulator-engine/reducer/swapReducer.ts | 64 +++++++++++++++++- ...inance.ts => RefinanceLendingToLending.ts} | 28 ++++---- .../src/strategies/refinance/index.ts | 2 +- sdk/simulator-service/tests/simulator.test.ts | 6 +- .../src/interfaces/ISwapManager.ts | 17 ++++- .../src/interfaces/ISwapProvider.ts | 18 ++++- .../src/implementation/SwapManager.ts | 18 ++++- .../oneinch/OneInchSwapProvider.ts | 67 ++++++++++++++++++- .../src/implementation/oneinch/types.ts | 2 + 16 files changed, 238 insertions(+), 33 deletions(-) create mode 100644 sdk/sdk-common/src/swap/SpotData.ts rename sdk/simulator-service/src/strategies/refinance/{Refinance.ts => RefinanceLendingToLending.ts} (85%) diff --git a/sdk/sdk-common/src/common/implementation/Price.ts b/sdk/sdk-common/src/common/implementation/Price.ts index a29d5a921e..d7545b9cb9 100644 --- a/sdk/sdk-common/src/common/implementation/Price.ts +++ b/sdk/sdk-common/src/common/implementation/Price.ts @@ -1,9 +1,10 @@ +import { BigNumber } from 'bignumber.js' import { SerializationService } from '../../services/SerializationService' import { isToken } from '../../utils/isToken' import { CurrencySymbol } from '../enums/CurrencySymbol' import { Token } from './Token' -interface IPriceSerialized { +interface IPrice { value: string baseToken: Token quoteToken: Token | CurrencySymbol @@ -13,14 +14,14 @@ interface IPriceSerialized { * @class Price * @description Represents a price of a token (baseToken) in a given currency (quoteToken) */ -export class Price implements IPriceSerialized { +export class Price implements IPrice { private static readonly DEFAULT_QUOTE_TOKEN = CurrencySymbol.USD readonly value: string readonly baseToken: Token readonly quoteToken: Token | CurrencySymbol - private constructor(params: IPriceSerialized) { + private constructor(params: IPrice) { this.value = params.value this.baseToken = params.baseToken this.quoteToken = params.quoteToken @@ -45,6 +46,10 @@ export class Price implements IPriceSerialized { return `${this.value} ${this.baseToken.symbol}/${this.quoteToken}` } } + + public toBN(): BigNumber { + return new BigNumber(this.value) + } } SerializationService.registerClass(Price) diff --git a/sdk/sdk-common/src/common/implementation/TokenAmount.ts b/sdk/sdk-common/src/common/implementation/TokenAmount.ts index 5823232aa7..a145fe6066 100644 --- a/sdk/sdk-common/src/common/implementation/TokenAmount.ts +++ b/sdk/sdk-common/src/common/implementation/TokenAmount.ts @@ -83,6 +83,10 @@ export class TokenAmount implements ITokenAmountSerialized { return new BigNumber(this.amount).times(this._baseUnitFactor).toFixed(0) } + public toBaseUnitAsBn(): BigNumber { + return new BigNumber(this.amount).times(this._baseUnitFactor) + } + public toBN(): BigNumber { return new BigNumber(this.amount) } diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index db8dc1b77d..504db6644f 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -1,3 +1,4 @@ +import { Price } from '../common/implementation/Price' import { Percentage } from '../common/implementation/Percentage' import { Position } from '../common/implementation/Position' import { Token } from '../common/implementation/Token' @@ -61,6 +62,7 @@ export interface SwapStep { provider: SwapProviderType routes: SwapRoute[] + prices: Price[] fromTokenAmount: TokenAmount toTokenAmount: TokenAmount slippage: Percentage diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts index b8f49bd260..9a840a29c8 100644 --- a/sdk/sdk-common/src/swap/SimulatedSwapData.ts +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -1,12 +1,17 @@ -import { Percentage } from '@summerfi/sdk-common/common' +import { TokenAmount } from '../common/implementation/TokenAmount' +import { Percentage } from '../common/implementation/Percentage' +import { Price } from '../common/implementation/Price' import { QuoteData } from './QuoteData' /** * Represents the data returned for each Swap in simulation. - * It is derived from the `QuoteData` type with the `estimatedGas` field omitted, + * It is derived from the `QuoteData` type with the `estimatedGas` and 'routes' fields omitted, * as gas estimation is not relevant for simulation purposes. */ -export type SimulatedSwapData = Omit & { +export type SimulatedSwapData = Omit & { slippage: Percentage - fee: Percentage + offerPrice: Price + marketPrice: Price + priceImpact: Percentage + summerFee: TokenAmount } diff --git a/sdk/sdk-common/src/swap/SpotData.ts b/sdk/sdk-common/src/swap/SpotData.ts new file mode 100644 index 0000000000..64b2e475ba --- /dev/null +++ b/sdk/sdk-common/src/swap/SpotData.ts @@ -0,0 +1,11 @@ +import { Price } from '../common' +import type { SwapProviderType } from './Enums' + +/** + * @name SpotData + * @description Gives the current market price for a specific asset + */ +export type SpotData = { + provider: SwapProviderType + prices: Price[] +} diff --git a/sdk/sdk-common/src/swap/index.ts b/sdk/sdk-common/src/swap/index.ts index 3dc6159970..5997c37e20 100644 --- a/sdk/sdk-common/src/swap/index.ts +++ b/sdk/sdk-common/src/swap/index.ts @@ -2,3 +2,4 @@ export type { SwapData } from './SwapData' export type { QuoteData, SwapRoute } from './QuoteData' export type { SimulatedSwapData } from './SimulatedSwapData' export { SwapProviderType } from './Enums' +export type { SpotData } from './SpotData' diff --git a/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts b/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts index bb8184745d..ae05142822 100644 --- a/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts +++ b/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts @@ -1,4 +1,7 @@ -import { refinance, type RefinanceDependencies } from '@summerfi/simulator-service/strategies' +import { + refinanceLendingToLending, + type RefinanceDependencies, +} from '@summerfi/simulator-service/strategies' import { z } from 'zod' import { Percentage } from '@summerfi/sdk-common/common' import type { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' @@ -18,6 +21,6 @@ export const getRefinanceSimulation = publicProcedure getSummerFee: () => Percentage.createFrom({ percentage: 0 }), } - const simulation = await refinance(args, dependencies) + const simulation = await refinanceLendingToLending(args, dependencies) return simulation }) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index bd05c7b018..452fdbf693 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -1,3 +1,4 @@ +import { TokenAmount, Price, Percentage } from '@summerfi/sdk-common/common' import { steps } from '@summerfi/sdk-common/simulation' import { addBalance, subtractBalance } from '../../utils' import { ISimulationState } from '../../../interfaces/simulation' @@ -6,6 +7,35 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim const balanceWithoutFromToken = subtractBalance(step.inputs.fromTokenAmount, state.balances) const balanceWithToToken = addBalance(step.outputs.receivedAmount, balanceWithoutFromToken) + const baseToken = step.inputs.toTokenAmount.token + const quoteToken = step.inputs.fromTokenAmount.token + + const offerPrice = Price.createFrom({ + value: step.inputs.toTokenAmount + .toBaseUnitAsBn() + .div(step.inputs.fromTokenAmount.toBaseUnitAsBn()) + .toString(), + baseToken, + quoteToken, + }) + + const spotPriceOfToToken = step.inputs.prices.find((price) => + price.baseToken.address.equals(baseToken.address), + ) + const spotPriceOfFromToken = step.inputs.prices.find((price) => + price.baseToken.address.equals(quoteToken.address), + ) + + if (!spotPriceOfToToken || !spotPriceOfFromToken) { + throw new Error('Spot price for either From/To token could not be found') + } + + const marketPrice = Price.createFrom({ + value: spotPriceOfToToken.toBN().div(spotPriceOfFromToken.toBN()).toString(), + baseToken: step.inputs.fromTokenAmount.token, + quoteToken: step.inputs.toTokenAmount.token, + }) + return { ...state, steps: { @@ -16,13 +46,43 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim ...state.swaps, [step.name]: { provider: step.inputs.provider, - routes: step.inputs.routes, + // Note: Can add routes back in later if we need them for the UI + // routes: step.inputs.routes, fromTokenAmount: step.inputs.fromTokenAmount, toTokenAmount: step.inputs.toTokenAmount, slippage: step.inputs.slippage, - fee: step.inputs.fee, + offerPrice, + marketPrice, + priceImpact: calculatePriceImpact(marketPrice, offerPrice), + summerFee: TokenAmount.createFrom({ + token: step.inputs.fromTokenAmount.token, + amount: step.inputs.fromTokenAmount.multiply(step.inputs.fee.value).amount, + }), }, }, balances: balanceWithToToken, } } + +/** + * + * @param marketPrice - This price represent how much it will cost for selling some very small amount + * such as 0.1. It is the best possible price on the market. + * @param offerPrice - If the amount we would like to sell we might get deeper into the liquidity + * meaning the price won't be a good as when you sell small amount. This is the price that is + * represent how much it will cost for us to sell the desired amount. + * + * Both prices might be equal which means that there is no price impact. Having no + * price impact means that you sell at the best possible price. + */ +export function calculatePriceImpact(marketPrice: Price, offerPrice: Price): Percentage { + return Percentage.createFrom({ + percentage: marketPrice + .toBN() + .minus(offerPrice.toBN()) + .div(marketPrice.toBN()) + .abs() + .times(100) + .toNumber(), + }) +} diff --git a/sdk/simulator-service/src/strategies/refinance/Refinance.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts similarity index 85% rename from sdk/simulator-service/src/strategies/refinance/Refinance.ts rename to sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index 29ba7bb590..f8114a7075 100644 --- a/sdk/simulator-service/src/strategies/refinance/Refinance.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -13,7 +13,7 @@ import { isLendingPool } from '@summerfi/sdk-common/protocols' import { refinanceLendingToLendingStrategy } from './Strategy' import { RefinanceDependencies } from './Types' -export async function refinance( +export async function refinanceLendingToLending( args: RefinanceParameters, dependencies: RefinanceDependencies, ): Promise> { @@ -32,17 +32,6 @@ export async function refinance( const isDebtSwapSkipped = args.targetPool.debts[args.position.debtAmount.token.address.value] !== undefined - // let debtSwapQuote: Quote | undefined - // TODO: implement case with swaps - // if (!isDebtSwapSkipped) { - // debtSwapQuote = await dependencies.getQuote({ - // from: args.targetPool.debtTokens[0], - // to: args.position.debtAmount.token, - // slippage: args.slippage, - // fee: 0, - // }) - // } - // TODO: read debt amount from chain (special step: ReadDebtAmount) // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, // before or after the swap, it influences actual call to oneInch api @@ -80,6 +69,13 @@ export async function refinance( toToken: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, })), + ...(await dependencies.swapManager.getSpotPrice({ + chainInfo: args.position.pool.protocol.chainInfo, + tokens: [ + args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, + ], + })), slippage: args.slippage, fee: dependencies.getSummerFee(), }, @@ -102,6 +98,7 @@ export async function refinance( ), }, })) + // TODO: Implement swapping logic properly. Current implementation is just placeholder .next(async (ctx) => ({ name: 'DebtSwap', type: SimulationSteps.Swap, @@ -113,6 +110,13 @@ export async function refinance( ), toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, })), + ...(await dependencies.swapManager.getSpotPrice({ + chainInfo: args.position.pool.protocol.chainInfo, + tokens: [ + args.targetPool.debts[args.position.debtAmount.token.address.value].token, + args.targetPool.debts[args.position.debtAmount.token.address.value].token, + ], + })), slippage: args.slippage, fee: dependencies.getSummerFee(), }, diff --git a/sdk/simulator-service/src/strategies/refinance/index.ts b/sdk/simulator-service/src/strategies/refinance/index.ts index f84b53ee2d..3b6c974181 100644 --- a/sdk/simulator-service/src/strategies/refinance/index.ts +++ b/sdk/simulator-service/src/strategies/refinance/index.ts @@ -1,3 +1,3 @@ -export * from './Refinance' +export * from './RefinanceLendingToLending' export * from './Strategy' export * from './Types' diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index 8595768a21..7243b78c59 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -1,6 +1,6 @@ import { Percentage } from '@summerfi/sdk-common/common' import { ISimulation, SimulationSteps, SimulationType } from '@summerfi/sdk-common/simulation' -import { refinance } from '../src/strategies' +import { refinanceLendingToLending } from '../src/strategies' import { otherTestCollateral, otherTestDebt, @@ -14,7 +14,7 @@ describe('Refinance', () => { describe('to the position with the same collateral and debt (no swaps)', () => { let simulation: ISimulation beforeAll(async () => { - simulation = await refinance( + simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPool, @@ -60,7 +60,7 @@ describe('Refinance', () => { describe.skip('to the position with the different collateral and debt (with swaps)', () => { let simulation: ISimulation beforeAll(async () => { - simulation = await refinance( + simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, diff --git a/sdk/swap-common/src/interfaces/ISwapManager.ts b/sdk/swap-common/src/interfaces/ISwapManager.ts index f1e50e2f2f..cf6b85d0fa 100644 --- a/sdk/swap-common/src/interfaces/ISwapManager.ts +++ b/sdk/swap-common/src/interfaces/ISwapManager.ts @@ -4,8 +4,9 @@ import type { Percentage, Token, Address, + CurrencySymbol, } from '@summerfi/sdk-common/common' -import type { QuoteData, SwapData } from '@summerfi/sdk-common/swap' +import type { QuoteData, SwapData, SpotData } from '@summerfi/sdk-common/swap' /** * @name ISwapManager @@ -43,4 +44,18 @@ export interface ISwapManager { fromAmount: TokenAmount toToken: Token }): Promise + + /** + * @name getSpotPrice + * @description Returns the prevailing market price for a given asset + * in terms of a base currency + * @param chainInfo The chain information + * @param tokens An array of tokens for which you require a price + * @param quoteCurrency The currency in which the token is quoted in + */ + getSpotPrice(params: { + chainInfo: ChainInfo + tokens: Token[] + quoteCurrency?: CurrencySymbol + }): Promise } diff --git a/sdk/swap-common/src/interfaces/ISwapProvider.ts b/sdk/swap-common/src/interfaces/ISwapProvider.ts index e297b823b5..d430fe6e94 100644 --- a/sdk/swap-common/src/interfaces/ISwapProvider.ts +++ b/sdk/swap-common/src/interfaces/ISwapProvider.ts @@ -1,6 +1,6 @@ import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' -import { SwapProviderType } from '@summerfi/sdk-common/swap' -import type { QuoteData, SwapData } from '@summerfi/sdk-common/swap' +import { CurrencySymbol } from '@summerfi/sdk-common/common' +import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' /** * @name ISwapProvider @@ -40,4 +40,18 @@ export interface ISwapProvider { fromAmount: TokenAmount toToken: Token }): Promise + + /** + * @name getSpotPrice + * @description Returns the prevailing market price for a given asset + * in terms of a base currency + * @param chainInfo The chain information + * @param tokens An array of token's for which you require a price + * @param quoteCurrency The currency in which a token is quoted in (the denominator) + */ + getSpotPrice(params: { + chainInfo: ChainInfo + tokens: Token[] + quoteCurrency?: CurrencySymbol + }): Promise } diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index f1594ef6cb..36ef7bca74 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -6,10 +6,10 @@ import type { Percentage, Address, } from '@summerfi/sdk-common/common' -import { ChainId } from '@summerfi/sdk-common/common' +import { ChainId, CurrencySymbol } from '@summerfi/sdk-common/common' import { ISwapProvider, ISwapManager } from '@summerfi/swap-common/interfaces' -import { QuoteData, SwapData, SwapProviderType } from '@summerfi/sdk-common/swap' +import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' export type SwapManagerProviderConfig = { provider: ISwapProvider @@ -59,6 +59,20 @@ export class SwapManager implements ISwapManager { return provider.getSwapQuoteExactInput(params) } + async getSpotPrice(params: { + chainInfo: ChainInfo + tokens: Token[] + quoteCurrency?: CurrencySymbol + forceUseProvider?: SwapProviderType + }): Promise { + const provider: Maybe = this._getBestProvider(params) + if (!provider) { + throw new Error('No swap provider available') + } + + return provider.getSpotPrice(params) + } + private _registerProvider(provider: ISwapProvider, forChainIds: number[]): void { for (const chainId of forChainIds) { const providers = this._providersByChainId.get(chainId) || [] diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index 9f36fe6877..4dc530e043 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -1,9 +1,16 @@ import { ISwapProvider } from '@summerfi/swap-common/interfaces' -import { SwapProviderType, SwapData, SwapRoute, QuoteData } from '@summerfi/sdk-common/swap' +import { + SwapProviderType, + SwapData, + SpotData, + SwapRoute, + QuoteData, +} from '@summerfi/sdk-common/swap' import { OneInchAuthHeader, OneInchAuthHeaderKey, OneInchQuoteResponse, + OneInchSpotResponse, OneInchSwapProviderConfig, OneInchSwapResponse, OneInchSwapRoute, @@ -16,6 +23,9 @@ import { Percentage, type Token, Address, + CurrencySymbol, + Price, + type AddressValue, } from '@summerfi/sdk-common/common' export class OneInchSwapProvider implements ISwapProvider { @@ -111,6 +121,50 @@ export class OneInchSwapProvider implements ISwapProvider { } } + async getSpotPrice(params: { + chainInfo: ChainInfo + tokens: Token[] + quoteCurrency?: CurrencySymbol + }): Promise { + const spotUrl = this._formatOneInchSpotUrl({ + chainInfo: params.chainInfo, + tokenAddresses: params.tokens.map((token) => token.address), + quoteCurrency: params.quoteCurrency ?? CurrencySymbol.USD, + }) + + const authHeader = this._getOneInchAuthHeader() + + const response = await fetch(spotUrl, { + headers: authHeader, + }) + + if (!(response.status === 200 && response.statusText === 'OK')) { + throw new Error( + `Error performing 1inch spot price request ${spotUrl}: ${await response.body}`, + ) + } + + const responseData = (await response.json()) as OneInchSpotResponse + + return { + provider: SwapProviderType.OneInch, + prices: Object.entries(responseData).map(([address, price]) => { + const baseToken = params.tokens.find((token) => + token.address.equals(Address.createFrom({ value: address as AddressValue })), + ) + if (!baseToken) { + throw new Error('BaseToken not found in params.tokens list when fetching spot prices') + } + + return Price.createFrom({ + value: price.toString(), + baseToken, + quoteToken: params.quoteCurrency, + }) + }), + } + } + private _getOneInchAuthHeader(): OneInchAuthHeader { return { [OneInchAuthHeaderKey]: this._apiKey } } @@ -154,6 +208,17 @@ export class OneInchSwapProvider implements ISwapProvider { return `${this._apiUrl}/${this._version}/${chainId}/quote?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&protocols=${protocolsParam}` } + private _formatOneInchSpotUrl(params: { + chainInfo: ChainInfo + tokenAddresses: Address[] + quoteCurrency: CurrencySymbol + }): string { + const chainId = params.chainInfo.chainId + const tokenAddresses = params.tokenAddresses.map((address) => address.value.toLowerCase()) + + return `${this._apiUrl}/${this._version}/${chainId}/price/${tokenAddresses.join(',')}?currency=${params.quoteCurrency.toUpperCase()}` + } + private _extractSwapRoutes(protocols: OneInchSwapRoute[]): SwapRoute[] { return protocols.map((route) => route.map((hop) => diff --git a/sdk/swap-service/src/implementation/oneinch/types.ts b/sdk/swap-service/src/implementation/oneinch/types.ts index e143763b4d..0f161e6403 100644 --- a/sdk/swap-service/src/implementation/oneinch/types.ts +++ b/sdk/swap-service/src/implementation/oneinch/types.ts @@ -27,6 +27,8 @@ export interface OneInchSwapResponse extends OneInchBaseResponse { } } +export type OneInchSpotResponse = Record + export interface OneInchQuoteResponse extends OneInchBaseResponse { /* One Inch can provide multiple routes */ protocols: OneInchSwapRoute[] From 741cbf59b9cce8723767ed609d59f2ee93770903 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Tue, 26 Mar 2024 19:43:27 +0000 Subject: [PATCH 10/26] chore: address PR comments & fix tests --- .../refinance/RefinanceLendingToLending.ts | 4 +-- .../tests/mocks/contextMock.ts | 36 ++++++++++++++++++- .../src/interfaces/ISwapManager.ts | 4 +-- .../src/interfaces/ISwapProvider.ts | 4 +-- .../src/implementation/SwapManager.ts | 4 +-- .../oneinch/OneInchSwapProvider.ts | 2 +- 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index f8114a7075..0a413a6e70 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -69,7 +69,7 @@ export async function refinanceLendingToLending( toToken: args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, })), - ...(await dependencies.swapManager.getSpotPrice({ + ...(await dependencies.swapManager.getSpotPrices({ chainInfo: args.position.pool.protocol.chainInfo, tokens: [ args.targetPool.collaterals[args.position.collateralAmount.token.address.value].token, @@ -110,7 +110,7 @@ export async function refinanceLendingToLending( ), toToken: args.targetPool.debts[args.position.debtAmount.token.address.value].token, })), - ...(await dependencies.swapManager.getSpotPrice({ + ...(await dependencies.swapManager.getSpotPrices({ chainInfo: args.position.pool.protocol.chainInfo, tokens: [ args.targetPool.debts[args.position.debtAmount.token.address.value].token, diff --git a/sdk/simulator-service/tests/mocks/contextMock.ts b/sdk/simulator-service/tests/mocks/contextMock.ts index f6170fb0ff..e4603653fc 100644 --- a/sdk/simulator-service/tests/mocks/contextMock.ts +++ b/sdk/simulator-service/tests/mocks/contextMock.ts @@ -1,4 +1,13 @@ -import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' +import { + Address, + ChainInfo, + Price, + Percentage, + Token, + TokenAmount, + type AddressValue, + CurrencySymbol, +} from '@summerfi/sdk-common/common' import { SwapProviderType } from '@summerfi/sdk-common/swap' async function getSwapDataExactInput(params: { @@ -32,6 +41,30 @@ async function getSwapQuoteExactInput(params: { } } +async function getSpotPrices(params: { chainInfo: ChainInfo; tokens: Token[] }) { + const MOCK_PRICE = 0.5 + const MOCK_QUOTE_CURRENCY = CurrencySymbol.USD + return { + provider: SwapProviderType.OneInch, + prices: params.tokens + .map((token) => [token.address.value, MOCK_PRICE]) + .map(([address, price]) => { + const baseToken = params.tokens.find((token) => + token.address.equals(Address.createFrom({ value: address as AddressValue })), + ) + if (!baseToken) { + throw new Error('BaseToken not found in params.tokens list when fetching spot prices') + } + + return Price.createFrom({ + value: price.toString(), + baseToken, + quoteToken: MOCK_QUOTE_CURRENCY, + }) + }), + } +} + export function mockGetFee() { return Percentage.createFrom({ percentage: 0 }) } @@ -41,5 +74,6 @@ export const mockRefinanceContext = { swapManager: { getSwapDataExactInput: getSwapDataExactInput, getSwapQuoteExactInput: jest.fn().mockImplementation(getSwapQuoteExactInput), + getSpotPrices: getSpotPrices, }, } diff --git a/sdk/swap-common/src/interfaces/ISwapManager.ts b/sdk/swap-common/src/interfaces/ISwapManager.ts index cf6b85d0fa..71596f740e 100644 --- a/sdk/swap-common/src/interfaces/ISwapManager.ts +++ b/sdk/swap-common/src/interfaces/ISwapManager.ts @@ -46,14 +46,14 @@ export interface ISwapManager { }): Promise /** - * @name getSpotPrice + * @name getSpotPrices * @description Returns the prevailing market price for a given asset * in terms of a base currency * @param chainInfo The chain information * @param tokens An array of tokens for which you require a price * @param quoteCurrency The currency in which the token is quoted in */ - getSpotPrice(params: { + getSpotPrices(params: { chainInfo: ChainInfo tokens: Token[] quoteCurrency?: CurrencySymbol diff --git a/sdk/swap-common/src/interfaces/ISwapProvider.ts b/sdk/swap-common/src/interfaces/ISwapProvider.ts index d430fe6e94..7ed7019a03 100644 --- a/sdk/swap-common/src/interfaces/ISwapProvider.ts +++ b/sdk/swap-common/src/interfaces/ISwapProvider.ts @@ -42,14 +42,14 @@ export interface ISwapProvider { }): Promise /** - * @name getSpotPrice + * @name getSpotPrices * @description Returns the prevailing market price for a given asset * in terms of a base currency * @param chainInfo The chain information * @param tokens An array of token's for which you require a price * @param quoteCurrency The currency in which a token is quoted in (the denominator) */ - getSpotPrice(params: { + getSpotPrices(params: { chainInfo: ChainInfo tokens: Token[] quoteCurrency?: CurrencySymbol diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index 36ef7bca74..c835a6fe6a 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -59,7 +59,7 @@ export class SwapManager implements ISwapManager { return provider.getSwapQuoteExactInput(params) } - async getSpotPrice(params: { + async getSpotPrices(params: { chainInfo: ChainInfo tokens: Token[] quoteCurrency?: CurrencySymbol @@ -70,7 +70,7 @@ export class SwapManager implements ISwapManager { throw new Error('No swap provider available') } - return provider.getSpotPrice(params) + return provider.getSpotPrices(params) } private _registerProvider(provider: ISwapProvider, forChainIds: number[]): void { diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index 4dc530e043..957dd3599b 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -121,7 +121,7 @@ export class OneInchSwapProvider implements ISwapProvider { } } - async getSpotPrice(params: { + async getSpotPrices(params: { chainInfo: ChainInfo tokens: Token[] quoteCurrency?: CurrencySymbol From 60c8d3789d1c5d049c4a7125999eb4bc2ac874ad Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 28 Mar 2024 12:26:08 +0000 Subject: [PATCH 11/26] feat: calculate borrowAmount when swapping debt --- .../queries/simulateRefinance.subtest.ts | 3 + .../refinance/IRefinanceParameters.ts | 2 +- sdk/sdk-common/src/simulation/Steps.ts | 2 +- sdk/sdk-common/src/swap/SpotData.ts | 2 +- .../refinance/RefinanceLendingToLending.ts | 111 ++++++++++++--- .../src/strategies/refinance/Strategy.ts | 6 +- .../tests/mocks/contextMock.ts | 14 +- .../tests/mocks/testSourcePosition.ts | 44 +++--- sdk/simulator-service/tests/simulator.test.ts | 39 ++++-- .../src/implementation/SpotPriceMap.ts | 36 +++++ sdk/swap-common/src/implementation/index.ts | 1 + .../src/interfaces/ISpotPriceMap.ts | 7 + .../src/interfaces/ISwapManager.ts | 15 +- .../src/interfaces/ISwapProvider.ts | 14 +- .../src/implementation/SwapManager.ts | 10 +- .../oneinch/OneInchSwapProvider.ts | 132 +++++++++++++----- 16 files changed, 335 insertions(+), 103 deletions(-) create mode 100644 sdk/swap-common/src/implementation/SpotPriceMap.ts create mode 100644 sdk/swap-common/src/implementation/index.ts create mode 100644 sdk/swap-common/src/interfaces/ISpotPriceMap.ts diff --git a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts index a54f502e93..6494516399 100644 --- a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts +++ b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts @@ -105,6 +105,9 @@ export default async function simulateRefinanceTest() { const refinanceParameters: IRefinanceParameters = { position: prevPosition, targetPool: targetPool, + // Assume no swaps + targetDebt: prevPosition.debtAmount.token, + targetCollateral: prevPosition.collateralAmount.token, slippage: Percentage.createFrom({ value: 0.5 }), } diff --git a/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts b/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts index e34a23de5e..4a333752d3 100644 --- a/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts +++ b/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts @@ -1,4 +1,4 @@ -import {IToken} from "../../../common/interfaces/IToken"; +import { IToken } from '../../../common/interfaces/IToken' import { IPercentage } from '../../../common/interfaces/IPercentage' import { IPosition } from '../../../common/interfaces/IPosition' import { ILendingPool } from '../../../protocols/interfaces/ILendingPool' diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index 4fa8753dd1..e2cd398541 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -63,7 +63,7 @@ export interface SwapStep { provider: SwapProviderType routes: SwapRoute[] - prices: Price[] + spotPrice: Price fromTokenAmount: TokenAmount toTokenAmount: TokenAmount slippage: Percentage diff --git a/sdk/sdk-common/src/swap/SpotData.ts b/sdk/sdk-common/src/swap/SpotData.ts index 64b2e475ba..b4605d2c83 100644 --- a/sdk/sdk-common/src/swap/SpotData.ts +++ b/sdk/sdk-common/src/swap/SpotData.ts @@ -7,5 +7,5 @@ import type { SwapProviderType } from './Enums' */ export type SpotData = { provider: SwapProviderType - prices: Price[] + price: Price } diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index a46ab2b0b9..cafb5a5bdb 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -1,3 +1,4 @@ +import { DebtConfig } from '@summerfi/sdk-common/protocols' import { FlashloanProvider, ISimulation, @@ -6,7 +7,7 @@ import { TokenTransferTargetType, } from '@summerfi/sdk-common/simulation' import { Simulator } from '../../implementation/simulator-engine' -import { Position, TokenAmount, Percentage } from '@summerfi/sdk-common/common' +import { Position, TokenAmount, Percentage, Price } from '@summerfi/sdk-common/common' import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' import { IRefinanceParameters } from '@summerfi/sdk-common/orders' import { isLendingPool } from '@summerfi/sdk-common/protocols' @@ -39,10 +40,19 @@ export async function refinanceLendingToLending( throw new Error('Target token config not found in pool') } - const isCollateralSwapSkipped = targetCollateralConfig.token.address.equals(position.collateralAmount.token.address) + const isCollateralSwapSkipped = targetCollateralConfig.token.address.equals( + position.collateralAmount.token.address, + ) const isDebtSwapSkipped = targetDebtConfig.token.address.equals(position.debtAmount.token.address) - // TODO: read debt amount from chain (special step: ReadDebtAmount) + const debtSpotPrice = ( + await dependencies.swapManager.getSpotPrice({ + chainInfo: position.pool.protocol.chainInfo, + baseToken: targetDebtConfig.token, + quoteToken: position.debtAmount.token, + }) + ).price + // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, // before or after the swap, it influences actual call to oneInch api const simulation = await simulator @@ -79,10 +89,13 @@ export async function refinanceLendingToLending( fromAmount: position.collateralAmount, toToken: targetCollateralConfig.token, })), - ...(await dependencies.swapManager.getSpotPrices({ - chainInfo: position.pool.protocol.chainInfo, - tokens: [position.collateralAmount.token, targetCollateralConfig.token], - })), + spotPrice: ( + await dependencies.swapManager.getSpotPrice({ + chainInfo: position.pool.protocol.chainInfo, + baseToken: targetCollateralConfig.token, + quoteToken: position.collateralAmount.token, + }) + ).price, slippage: Percentage.createFrom({ value: args.slippage.value }), fee: dependencies.getSummerFee(), }, @@ -92,7 +105,13 @@ export async function refinanceLendingToLending( name: 'DepositBorrowToTarget', type: SimulationSteps.DepositBorrow, inputs: { - borrowAmount: position.debtAmount, // TODO figure the debt amount + borrowAmount: await calculateBorrowAmount( + isDebtSwapSkipped, + position.debtAmount, + debtSpotPrice, + targetDebtConfig, + args, + ), depositAmount: ctx.getReference( isCollateralSwapSkipped ? ['PaybackWithdrawFromSource', 'withdrawAmount'] @@ -100,13 +119,12 @@ export async function refinanceLendingToLending( ), position: newEmptyPositionFromPool( targetPool, - position.debtAmount.token, - position.collateralAmount.token, + targetDebtConfig.token, + targetCollateralConfig.token, ), borrowTargetType: TokenTransferTargetType.PositionsManager, }, })) - // TODO: Implement swapping logic properly. Current implementation is just placeholder .next(async (ctx) => ({ name: 'DebtSwap', type: SimulationSteps.Swap, @@ -118,10 +136,7 @@ export async function refinanceLendingToLending( ), toToken: targetDebtConfig.token, })), - ...(await dependencies.swapManager.getSpotPrices({ - chainInfo: args.position.pool.protocol.chainInfo, - tokens: [position.debtAmount.token, targetDebtConfig.token], - })), + spotPrice: debtSpotPrice, slippage: Percentage.createFrom({ value: args.slippage.value }), fee: dependencies.getSummerFee(), }, @@ -131,9 +146,21 @@ export async function refinanceLendingToLending( name: 'RepayFlashloan', type: SimulationSteps.RepayFlashloan, inputs: { - amount: position.debtAmount, // TODO add some amount + amount: position.debtAmount, }, })) + .next(async () => ({ + name: 'ReturnFunds', + type: SimulationSteps.ReturnFunds, + inputs: { + /* + * We swap back to the original position's debt in order to repay the flashloan. + * Therefore, the dust amount will be in the original position's debt + * */ + token: position.debtAmount.token, + }, + skip: isDebtSwapSkipped, + })) .run() const targetPosition = Object.values(simulation.positions).find( @@ -152,3 +179,55 @@ export async function refinanceLendingToLending( steps: Object.values(simulation.steps), } satisfies ISimulation } + +/** + * CalculateBorrowAmount + * @description Determines how much to borrow. + * When the DebtSwap step is skipped we simply return the previous position's debt amount + * When a DebtSwap is required we need to borrow enough to cover the original flashloan after + * accounting the swap and assuming the worst case scenario on slippage IE max slippage. + */ +async function calculateBorrowAmount( + isDebtSwapSkipped: boolean, + prevDebtAmount: TokenAmount, + debtSpotPrice: Price, + targetDebtConfig: DebtConfig, + params: IRefinanceParameters, +) { + /** + * If no swap is required we simply borrow the same amount of debt, and the same asset, + * on the target protocol + */ + if (isDebtSwapSkipped) { + return prevDebtAmount + } + + /** + * Worked Example + * @description 3 ETH/ 5000 DAI -> X WTBC / Y USDC + * 5000 DAI * (0.98 USDC/DAI) = 4900 USDC + * but this assumes zero price impact + * + * 5000 DAI * (0.98 USDC/DAI) / (1 - 0.01) = 4949.49 USDC (slippage adjusted borrow amount) + * where 0.01 is 1% slippage + * + * More generally we'd write this as + * sourcePositionDebt * targetDebtQuotedInSourceDebtPrice / (one - slippage) = borrowAmount + */ + // TODO: Refactor common classes to make easier + // TODO: TokenAmount maths? + // TODO: Token equals + const borrowAmount = prevDebtAmount + .multiply(debtSpotPrice.toString()) + .divide( + Percentage.createFrom({ + value: 100 - params.slippage.value, + }).toString(), + ) + .toString() + + return TokenAmount.createFrom({ + amount: borrowAmount, + token: debtSpotPrice.baseToken + }) +} diff --git a/sdk/simulator-service/src/strategies/refinance/Strategy.ts b/sdk/simulator-service/src/strategies/refinance/Strategy.ts index 2a74eaf0ea..6758b53507 100644 --- a/sdk/simulator-service/src/strategies/refinance/Strategy.ts +++ b/sdk/simulator-service/src/strategies/refinance/Strategy.ts @@ -25,5 +25,9 @@ export const refinanceLendingToLendingStrategy = makeStrategy([ { step: SimulationSteps.RepayFlashloan, optional: false, - } + }, + { + step: SimulationSteps.ReturnFunds, + optional: true, + }, ]) diff --git a/sdk/simulator-service/tests/mocks/contextMock.ts b/sdk/simulator-service/tests/mocks/contextMock.ts index aa2407b73e..073b1fb5c8 100644 --- a/sdk/simulator-service/tests/mocks/contextMock.ts +++ b/sdk/simulator-service/tests/mocks/contextMock.ts @@ -10,7 +10,7 @@ import { import { CurrencySymbol } from '@summerfi/sdk-common/common' import { IPool } from '@summerfi/sdk-common/protocols' import { SwapProviderType } from '@summerfi/sdk-common/swap' -import { testTargetLendingPoolRequiredSwaps } from './testSourcePosition' +import { testTargetLendingPool, testTargetLendingPoolRequiredSwaps } from './testSourcePosition' async function getSwapDataExactInput(params: { chainInfo: ChainInfo @@ -73,6 +73,10 @@ export function mockGetFee() { /* eslint-disable @typescript-eslint/no-unused-vars */ async function mockGetPool(poolId: unknown): Promise { + return testTargetLendingPool as IPool +} + +async function mockGetPoolRequiresSwaps(poolId: unknown): Promise { return testTargetLendingPoolRequiredSwaps as IPool } @@ -88,3 +92,11 @@ export const mockRefinanceContext = { getSpotPrices, }, } + +export const mockRefinanceContextRequiredSwaps = { + ...mockRefinanceContext, + protocolManager: { + getPool: mockGetPoolRequiresSwaps, + getPosition: () => {}, + }, +} diff --git a/sdk/simulator-service/tests/mocks/testSourcePosition.ts b/sdk/simulator-service/tests/mocks/testSourcePosition.ts index 9f2086b93d..d23f110fe3 100644 --- a/sdk/simulator-service/tests/mocks/testSourcePosition.ts +++ b/sdk/simulator-service/tests/mocks/testSourcePosition.ts @@ -199,15 +199,19 @@ export const testTargetLendingPoolRequiredSwaps = SparkLendingPool.createFrom({ record: { [testDebt.address.value]: { token: testDebt, - price: Price.createFrom({value: '100', baseToken: testDebt, quoteToken: testCollateral}), - priceUSD: Price.createFrom({value: '100', baseToken: testDebt, quoteToken: testCollateral}), + price: Price.createFrom({ value: '100', baseToken: testDebt, quoteToken: testCollateral }), + priceUSD: Price.createFrom({ + value: '100', + baseToken: testDebt, + quoteToken: testCollateral, + }), liquidationThreshold: RiskRatio.createFrom({ - ratio: Percentage.createFrom({value: 80}), + ratio: Percentage.createFrom({ value: 80 }), type: RiskRatio.type.LTV, }), - maxSupply: TokenAmount.createFrom({token: testDebt, amount: '10000000'}), - tokensLocked: TokenAmount.createFrom({token: testDebt, amount: '1000000'}), - liquidationPenalty: Percentage.createFrom({value: 5}), + maxSupply: TokenAmount.createFrom({ token: testDebt, amount: '10000000' }), + tokensLocked: TokenAmount.createFrom({ token: testDebt, amount: '1000000' }), + liquidationPenalty: Percentage.createFrom({ value: 5 }), usageAsCollateralEnabled: true, apy: Percentage.createFrom({ value: 0.5 }), maxLtv: RiskRatio.createFrom({ @@ -215,23 +219,31 @@ export const testTargetLendingPoolRequiredSwaps = SparkLendingPool.createFrom({ type: RiskRatioType.LTV, }), }, - } + }, }, debts: { record: { [testCollateral.address.value]: { token: testCollateral, - price: Price.createFrom({value: '100', baseToken: testCollateral, quoteToken: testCollateral}), - priceUSD: Price.createFrom({value: '100', baseToken: testCollateral, quoteToken: testCollateral}), - rate: Percentage.createFrom({value: 5}), - totalBorrowed: TokenAmount.createFrom({token: testCollateral, amount: '100000'}), - debtCeiling: TokenAmount.createFrom({token: testCollateral, amount: '1000000'}), - debtAvailable: TokenAmount.createFrom({token: testCollateral, amount: '100000'}), - dustLimit: TokenAmount.createFrom({token: testCollateral, amount: '100'}), - originationFee: Percentage.createFrom({value: 1}), + price: Price.createFrom({ + value: '100', + baseToken: testCollateral, + quoteToken: testCollateral, + }), + priceUSD: Price.createFrom({ + value: '100', + baseToken: testCollateral, + quoteToken: testCollateral, + }), + rate: Percentage.createFrom({ value: 5 }), + totalBorrowed: TokenAmount.createFrom({ token: testCollateral, amount: '100000' }), + debtCeiling: TokenAmount.createFrom({ token: testCollateral, amount: '1000000' }), + debtAvailable: TokenAmount.createFrom({ token: testCollateral, amount: '100000' }), + dustLimit: TokenAmount.createFrom({ token: testCollateral, amount: '100' }), + originationFee: Percentage.createFrom({ value: 1 }), borrowingEnabled: true, }, - } + }, }, baseCurrency: testCollateral, poolId: { diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index 2bbf701603..87f4009439 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -8,18 +8,22 @@ import { testTargetLendingPool, testTargetLendingPoolRequiredSwaps, } from './mocks/testSourcePosition' -import { mockRefinanceContext } from './mocks/contextMock' +import { mockRefinanceContext, mockRefinanceContextRequiredSwaps } from './mocks/contextMock' describe('Refinance', () => { - describe('to the position with the same collateral and debt (no swaps)', () => { + describe.skip('to the position with the same collateral and debt (no swaps)', () => { let simulation: ISimulation beforeAll(async () => { simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPool, - targetDebt: testTargetLendingPool.debts.get({token: testSourcePosition.debtAmount.token })!.token, - targetCollateral: testTargetLendingPool.collaterals.get({token: testSourcePosition.collateralAmount.token })!.token, + targetDebt: testTargetLendingPool.debts.get({ + token: testSourcePosition.debtAmount.token, + })!.token, + targetCollateral: testTargetLendingPool.collaterals.get({ + token: testSourcePosition.collateralAmount.token, + })!.token, slippage: Percentage.createFrom({ value: 1 }), }, mockRefinanceContext, @@ -58,22 +62,37 @@ describe('Refinance', () => { }) }) - describe.only('to the position with the different collateral and debt (with swaps)', () => { + describe('to the position with the different collateral and debt (with swaps)', () => { let simulation: ISimulation beforeAll(async () => { // Swapped the tokens around to force two swaps - // console.log("targetDebt", testTargetLendingPoolRequiredSwaps.debts[testSourcePosition.collateralAmount.token.address.value].token.address) - // console.log("targetColl", testTargetLendingPoolRequiredSwaps.collaterals[testSourcePosition.debtAmount.token.address.value].token.address) + console.log( + 'targetDebt', + testTargetLendingPoolRequiredSwaps.debts.get({ + token: testSourcePosition.collateralAmount.token, + })!.token, + ) + console.log( + 'targetColl', + testTargetLendingPoolRequiredSwaps.collaterals.get({ + token: testSourcePosition.debtAmount.token, + })!.token, + ) + simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, // Note: they two tokens have been inverted - targetDebt: testTargetLendingPoolRequiredSwaps.debts.get({token: testSourcePosition.collateralAmount.token })!.token, - targetCollateral: testTargetLendingPoolRequiredSwaps.collaterals.get({token: testSourcePosition.debtAmount.token })!.token, + targetDebt: testTargetLendingPoolRequiredSwaps.debts.get({ + token: testSourcePosition.collateralAmount.token, + })!.token, + targetCollateral: testTargetLendingPoolRequiredSwaps.collaterals.get({ + token: testSourcePosition.debtAmount.token, + })!.token, slippage: Percentage.createFrom({ value: 1 }), }, - mockRefinanceContext, + mockRefinanceContextRequiredSwaps, ) }) diff --git a/sdk/swap-common/src/implementation/SpotPriceMap.ts b/sdk/swap-common/src/implementation/SpotPriceMap.ts new file mode 100644 index 0000000000..3a32c8bed8 --- /dev/null +++ b/sdk/swap-common/src/implementation/SpotPriceMap.ts @@ -0,0 +1,36 @@ +import { + Price, + type IPrice, + type IToken, + type Maybe, + type AddressValue, +} from '@summerfi/sdk-common/common' +import { ISpotPriceMap, ISpotPriceMapRecord } from '../interfaces/ISpotPriceMap' + +export class SpotPriceMap implements ISpotPriceMap { + readonly record: ISpotPriceMapRecord = {} + + private constructor(params: ISpotPriceMap) { + this._createSpotPriceMap(params) + } + + static createFrom(params: Price): SpotPriceMap { + return new SpotPriceMap(params) + } + + public add(params: { price: IPrice }): void { + this.record[params.price.baseToken.address.value as AddressValue] = Price.createFrom( + params.price, + ) + } + + public get(params: { token: IToken }): Maybe { + return this.record[params.token.address.value] + } + + private _createSpotPriceMap(params: ISpotPriceMap): void { + return Object.entries(params.record).forEach(([, price]) => { + this.add({ price }) + }) + } +} diff --git a/sdk/swap-common/src/implementation/index.ts b/sdk/swap-common/src/implementation/index.ts new file mode 100644 index 0000000000..014f75a08d --- /dev/null +++ b/sdk/swap-common/src/implementation/index.ts @@ -0,0 +1 @@ +export { SpotPriceMap } from './SpotPriceMap' diff --git a/sdk/swap-common/src/interfaces/ISpotPriceMap.ts b/sdk/swap-common/src/interfaces/ISpotPriceMap.ts new file mode 100644 index 0000000000..922fc2f926 --- /dev/null +++ b/sdk/swap-common/src/interfaces/ISpotPriceMap.ts @@ -0,0 +1,7 @@ +import { AddressValue, Price } from '@summerfi/sdk-common/common' + +export type ISpotPriceMapRecord = Record + +export interface ISpotPriceMap { + record: ISpotPriceMapRecord +} diff --git a/sdk/swap-common/src/interfaces/ISwapManager.ts b/sdk/swap-common/src/interfaces/ISwapManager.ts index 71596f740e..b1169ebeb0 100644 --- a/sdk/swap-common/src/interfaces/ISwapManager.ts +++ b/sdk/swap-common/src/interfaces/ISwapManager.ts @@ -6,7 +6,8 @@ import type { Address, CurrencySymbol, } from '@summerfi/sdk-common/common' -import type { QuoteData, SwapData, SpotData } from '@summerfi/sdk-common/swap' +import { SpotData } from '@summerfi/sdk-common/swap' +import type { QuoteData, SwapData } from '@summerfi/sdk-common/swap' /** * @name ISwapManager @@ -46,16 +47,16 @@ export interface ISwapManager { }): Promise /** - * @name getSpotPrices + * @name getSpotPrice * @description Returns the prevailing market price for a given asset * in terms of a base currency * @param chainInfo The chain information - * @param tokens An array of tokens for which you require a price - * @param quoteCurrency The currency in which the token is quoted in + * @param baseToken A price request for baseToken + * @param quoteToken A price request - QuoteToken is optional with a USD default. */ - getSpotPrices(params: { + getSpotPrice(params: { chainInfo: ChainInfo - tokens: Token[] - quoteCurrency?: CurrencySymbol + baseToken: Token + quoteToken?: CurrencySymbol | Token }): Promise } diff --git a/sdk/swap-common/src/interfaces/ISwapProvider.ts b/sdk/swap-common/src/interfaces/ISwapProvider.ts index 7ed7019a03..a8bbb34641 100644 --- a/sdk/swap-common/src/interfaces/ISwapProvider.ts +++ b/sdk/swap-common/src/interfaces/ISwapProvider.ts @@ -1,6 +1,6 @@ import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' import { CurrencySymbol } from '@summerfi/sdk-common/common' -import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' +import type { QuoteData, SpotData, SwapData, SwapProviderType } from '@summerfi/sdk-common/swap' /** * @name ISwapProvider @@ -42,16 +42,16 @@ export interface ISwapProvider { }): Promise /** - * @name getSpotPrices + * @name getSpotPrice * @description Returns the prevailing market price for a given asset * in terms of a base currency * @param chainInfo The chain information - * @param tokens An array of token's for which you require a price - * @param quoteCurrency The currency in which a token is quoted in (the denominator) + * @param baseToken A price request for baseToken + * @param quoteToken A price request - QuoteToken is optional with a USD default. */ - getSpotPrices(params: { + getSpotPrice(params: { chainInfo: ChainInfo - tokens: Token[] - quoteCurrency?: CurrencySymbol + baseToken: Token + quoteToken?: CurrencySymbol | Token }): Promise } diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index c835a6fe6a..5594acc53a 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -7,7 +7,7 @@ import type { Address, } from '@summerfi/sdk-common/common' import { ChainId, CurrencySymbol } from '@summerfi/sdk-common/common' - +import { SpotPriceMap } from '@summerfi/swap-common/implementation' import { ISwapProvider, ISwapManager } from '@summerfi/swap-common/interfaces' import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' @@ -59,10 +59,10 @@ export class SwapManager implements ISwapManager { return provider.getSwapQuoteExactInput(params) } - async getSpotPrices(params: { + async getSpotPrice(params: { chainInfo: ChainInfo - tokens: Token[] - quoteCurrency?: CurrencySymbol + baseToken: Token + quoteToken?: CurrencySymbol | Token forceUseProvider?: SwapProviderType }): Promise { const provider: Maybe = this._getBestProvider(params) @@ -70,7 +70,7 @@ export class SwapManager implements ISwapManager { throw new Error('No swap provider available') } - return provider.getSpotPrices(params) + return provider.getSpotPrice(params) } private _registerProvider(provider: ISwapProvider, forChainIds: number[]): void { diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index 13441caaed..4a52c86b16 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -1,11 +1,6 @@ import { ISwapProvider } from '@summerfi/swap-common/interfaces' -import { - SwapProviderType, - SwapData, - SpotData, - SwapRoute, - QuoteData, -} from '@summerfi/sdk-common/swap' +import { SwapProviderType } from '@summerfi/sdk-common/swap' +import type { SwapData, SwapRoute, QuoteData, SpotData } from '@summerfi/sdk-common/swap' import { OneInchAuthHeader, OneInchAuthHeaderKey, @@ -21,7 +16,7 @@ import { type ChainInfo, TokenAmount, Percentage, - type Token, + Token, Address, CurrencySymbol, Price, @@ -121,47 +116,101 @@ export class OneInchSwapProvider implements ISwapProvider { } } - async getSpotPrices(params: { + async getSpotPrice(params: { chainInfo: ChainInfo - tokens: Token[] - quoteCurrency?: CurrencySymbol + baseToken: Token + quoteToken?: CurrencySymbol | Token }): Promise { - const spotUrl = this._formatOneInchSpotUrl({ - chainInfo: params.chainInfo, - tokenAddresses: params.tokens.map((token) => token.address), - quoteCurrency: params.quoteCurrency ?? CurrencySymbol.USD, - }) - const authHeader = this._getOneInchAuthHeader() + if (params.quoteToken && params.quoteToken instanceof Token) { + isTokenType(params.quoteToken) - const response = await fetch(spotUrl, { - headers: authHeader, - }) + const baseTokenAddress = params.baseToken.address + const quoteTokenAddress = params.quoteToken.address + const quoteCurrencySymbol = CurrencySymbol.USD - if (!(response.status === 200 && response.statusText === 'OK')) { - throw new Error( - `Error performing 1inch spot price request ${spotUrl}: ${await response.body}`, - ) - } + const spotUrl = this._formatOneInchSpotUrl({ + chainInfo: params.chainInfo, + tokenAddresses: [baseTokenAddress, quoteTokenAddress], + // We use USD as base for both tokens and then derive a spot price + quoteCurrency: quoteCurrencySymbol, + }) - const responseData = (await response.json()) as OneInchSpotResponse + const response = await fetch(spotUrl, { + headers: authHeader, + }) - return { - provider: SwapProviderType.OneInch, - prices: Object.entries(responseData).map(([address, price]) => { - const baseToken = params.tokens.find((token) => - token.address.equals(Address.createFromEthereum({ value: address as AddressValue })), + if (!(response.status === 200 && response.statusText === 'OK')) { + throw new Error( + `Error performing 1inch spot price request ${spotUrl}: ${await response.body}`, ) - if (!baseToken) { - throw new Error('BaseToken not found in params.tokens list when fetching spot prices') - } + } + const responseData = (await response.json()) as OneInchSpotResponse + const quoteToken = params.quoteToken + const prices = Object.entries(responseData).map(([address, price]) => { + const isBaseToken = params.baseToken.address.equals( + Address.createFromEthereum({ value: address as AddressValue }), + ) return Price.createFrom({ value: price.toString(), - baseToken, - quoteToken: params.quoteCurrency || CurrencySymbol.USD, + baseToken: isBaseToken ? params.baseToken : quoteToken, + quoteToken: quoteCurrencySymbol, }) - }), + }) + + const baseTokenPriceQuotedInCurrencySymbol = prices.find((p) => + p.baseToken.address.equals(params.baseToken.address), + ) + const quoteTokenPriceQuoteInCurrencySymbol = prices.find((p) => + p.baseToken.address.equals(quoteToken.address), + ) + + if (!baseTokenPriceQuotedInCurrencySymbol || !quoteTokenPriceQuoteInCurrencySymbol) { + throw new Error('BaseToken | QuoteToken spot prices could not be determined') + } + + return { + provider: SwapProviderType.OneInch, + price: Price.createFrom({ + value: baseTokenPriceQuotedInCurrencySymbol + .toBN() + .div(quoteTokenPriceQuoteInCurrencySymbol.toBN()) + .toString(), + baseToken: params.baseToken, + quoteToken: quoteToken, + }), + } + } else { + const quoteCurrency = params.quoteToken ?? CurrencySymbol.USD + const spotUrl = this._formatOneInchSpotUrl({ + chainInfo: params.chainInfo, + tokenAddresses: [params.baseToken.address], + quoteCurrency: params.quoteToken ?? CurrencySymbol.USD, + }) + + const response = await fetch(spotUrl, { + headers: authHeader, + }) + + if (!(response.status === 200 && response.statusText === 'OK')) { + throw new Error( + `Error performing 1inch spot price request ${spotUrl}: ${await response.body}`, + ) + } + + const responseData = (await response.json()) as OneInchSpotResponse + + const [, price] = Object.entries(responseData)[0] + + return { + provider: SwapProviderType.OneInch, + price: Price.createFrom({ + value: price.toString(), + baseToken: params.baseToken, + quoteToken: quoteCurrency, + }), + } } } @@ -234,3 +283,12 @@ export class OneInchSwapProvider implements ISwapProvider { ) } } + +function isTokenType(quoteToken: unknown): asserts quoteToken is Token { + if (!quoteToken) { + throw new Error('QuoteToken is undefined') + } + if (!(quoteToken instanceof Token)) { + throw new Error('QuoteToken is not of type Token') + } +} From 43fe186841507b2948f8ebbe1a4fe1971b6ff32f Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 28 Mar 2024 13:23:29 +0000 Subject: [PATCH 12/26] tests: fix remaining issues with build and tests --- sdk/sdk-common/src/swap/SimulatedSwapData.ts | 3 +- .../simulator-engine/reducer/swapReducer.ts | 27 +++---------- .../refinance/RefinanceLendingToLending.ts | 8 ++-- .../tests/mocks/contextMock.ts | 30 +++++--------- .../tests/mocks/testSourcePosition.ts | 40 ++++++++++--------- sdk/simulator-service/tests/simulator.test.ts | 39 +++++------------- .../src/implementation/SpotPriceMap.ts | 36 ----------------- sdk/swap-common/src/implementation/index.ts | 1 - .../src/interfaces/ISpotPriceMap.ts | 7 ---- .../src/implementation/SwapManager.ts | 1 - 10 files changed, 53 insertions(+), 139 deletions(-) delete mode 100644 sdk/swap-common/src/implementation/SpotPriceMap.ts delete mode 100644 sdk/swap-common/src/implementation/index.ts delete mode 100644 sdk/swap-common/src/interfaces/ISpotPriceMap.ts diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts index 9a840a29c8..3a7757bfb4 100644 --- a/sdk/sdk-common/src/swap/SimulatedSwapData.ts +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -11,7 +11,8 @@ import { QuoteData } from './QuoteData' export type SimulatedSwapData = Omit & { slippage: Percentage offerPrice: Price - marketPrice: Price + /* Also known as marketPrice or marketPrice with zero price impact */ + spotPrice: Price priceImpact: Percentage summerFee: TokenAmount } diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index d720f35a04..27874d3360 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -19,22 +19,7 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim quoteToken, }) - const spotPriceOfToToken = step.inputs.prices.find((price) => - price.baseToken.address.equals(baseToken.address), - ) - const spotPriceOfFromToken = step.inputs.prices.find((price) => - price.baseToken.address.equals(quoteToken.address), - ) - - if (!spotPriceOfToToken || !spotPriceOfFromToken) { - throw new Error('Spot price for either From/To token could not be found') - } - - const marketPrice = Price.createFrom({ - value: spotPriceOfToToken.toBN().div(spotPriceOfFromToken.toBN()).toString(), - baseToken: step.inputs.fromTokenAmount.token, - quoteToken: step.inputs.toTokenAmount.token, - }) + const spotPrice = step.inputs.spotPrice return { ...state, @@ -52,8 +37,8 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim toTokenAmount: step.inputs.toTokenAmount, slippage: Percentage.createFrom({ value: step.inputs.slippage.value }), offerPrice, - marketPrice, - priceImpact: calculatePriceImpact(marketPrice, offerPrice), + spotPrice, + priceImpact: calculatePriceImpact(spotPrice, offerPrice), summerFee: TokenAmount.createFrom({ token: step.inputs.fromTokenAmount.token, amount: step.inputs.fromTokenAmount.multiply(step.inputs.fee.value).amount, @@ -67,10 +52,10 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim /** * * @param marketPrice - This price represent how much it will cost for selling some very small amount - * such as 0.1. It is the best possible price on the market. + * such as 0.1. It is the best possible price on the market. * @param offerPrice - If the amount we would like to sell we might get deeper into the liquidity - * meaning the price won't be a good as when you sell small amount. This is the price that is - * represent how much it will cost for us to sell the desired amount. + * meaning the price won't be a good as when you sell small amount. This is the price that is + * represent how much it will cost for us to sell the desired amount. * * Both prices might be equal which means that there is no price impact. Having no * price impact means that you sell at the best possible price. diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index cafb5a5bdb..14f2f82da1 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -1,4 +1,3 @@ -import { DebtConfig } from '@summerfi/sdk-common/protocols' import { FlashloanProvider, ISimulation, @@ -10,7 +9,7 @@ import { Simulator } from '../../implementation/simulator-engine' import { Position, TokenAmount, Percentage, Price } from '@summerfi/sdk-common/common' import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' import { IRefinanceParameters } from '@summerfi/sdk-common/orders' -import { isLendingPool } from '@summerfi/sdk-common/protocols' +import { DebtConfig, isLendingPool } from '@summerfi/sdk-common/protocols' import { getReferencedValue } from '../../implementation/utils' import { refinanceLendingToLendingStrategy } from './Strategy' import { type IRefinanceDependencies } from './Types' @@ -214,8 +213,6 @@ async function calculateBorrowAmount( * More generally we'd write this as * sourcePositionDebt * targetDebtQuotedInSourceDebtPrice / (one - slippage) = borrowAmount */ - // TODO: Refactor common classes to make easier - // TODO: TokenAmount maths? // TODO: Token equals const borrowAmount = prevDebtAmount .multiply(debtSpotPrice.toString()) @@ -224,10 +221,11 @@ async function calculateBorrowAmount( value: 100 - params.slippage.value, }).toString(), ) + .multiply(100) .toString() return TokenAmount.createFrom({ amount: borrowAmount, - token: debtSpotPrice.baseToken + token: debtSpotPrice.baseToken, }) } diff --git a/sdk/simulator-service/tests/mocks/contextMock.ts b/sdk/simulator-service/tests/mocks/contextMock.ts index 073b1fb5c8..6b5df87d01 100644 --- a/sdk/simulator-service/tests/mocks/contextMock.ts +++ b/sdk/simulator-service/tests/mocks/contextMock.ts @@ -5,7 +5,6 @@ import { Token, TokenAmount, Price, - type AddressValue, } from '@summerfi/sdk-common/common' import { CurrencySymbol } from '@summerfi/sdk-common/common' import { IPool } from '@summerfi/sdk-common/protocols' @@ -43,27 +42,20 @@ async function getSwapQuoteExactInput(params: { } } -async function getSpotPrices(params: { chainInfo: ChainInfo; tokens: Token[] }) { +async function getSpotPrice(params: { + chainInfo: ChainInfo + baseToken: Token + quoteToken: Token | CurrencySymbol +}) { const MOCK_PRICE = 0.5 const MOCK_QUOTE_CURRENCY = CurrencySymbol.USD return { provider: SwapProviderType.OneInch, - prices: params.tokens - .map((token) => [token.address.value, MOCK_PRICE]) - .map(([address, price]) => { - const baseToken = params.tokens.find((token) => - token.address.equals(Address.createFromEthereum({ value: address as AddressValue })), - ) - if (!baseToken) { - throw new Error('BaseToken not found in params.tokens list when fetching spot prices') - } - - return Price.createFrom({ - value: price.toString(), - baseToken, - quoteToken: MOCK_QUOTE_CURRENCY, - }) - }), + price: Price.createFrom({ + value: MOCK_PRICE.toString(), + baseToken: params.baseToken, + quoteToken: MOCK_QUOTE_CURRENCY, + }), } } @@ -89,7 +81,7 @@ export const mockRefinanceContext = { swapManager: { getSwapDataExactInput, getSwapQuoteExactInput: jest.fn().mockImplementation(getSwapQuoteExactInput), - getSpotPrices, + getSpotPrice, }, } diff --git a/sdk/simulator-service/tests/mocks/testSourcePosition.ts b/sdk/simulator-service/tests/mocks/testSourcePosition.ts index d23f110fe3..23a9f766e1 100644 --- a/sdk/simulator-service/tests/mocks/testSourcePosition.ts +++ b/sdk/simulator-service/tests/mocks/testSourcePosition.ts @@ -197,20 +197,24 @@ export const testTargetLendingPoolRequiredSwaps = SparkLendingPool.createFrom({ type: PoolType.Lending, collaterals: { record: { - [testDebt.address.value]: { - token: testDebt, - price: Price.createFrom({ value: '100', baseToken: testDebt, quoteToken: testCollateral }), + [otherTestCollateral.address.value]: { + token: otherTestCollateral, + price: Price.createFrom({ + value: '100', + baseToken: otherTestCollateral, + quoteToken: CurrencySymbol.USD, + }), priceUSD: Price.createFrom({ value: '100', - baseToken: testDebt, - quoteToken: testCollateral, + baseToken: otherTestCollateral, + quoteToken: CurrencySymbol.USD, }), liquidationThreshold: RiskRatio.createFrom({ ratio: Percentage.createFrom({ value: 80 }), type: RiskRatio.type.LTV, }), - maxSupply: TokenAmount.createFrom({ token: testDebt, amount: '10000000' }), - tokensLocked: TokenAmount.createFrom({ token: testDebt, amount: '1000000' }), + maxSupply: TokenAmount.createFrom({ token: otherTestCollateral, amount: '10000000' }), + tokensLocked: TokenAmount.createFrom({ token: otherTestCollateral, amount: '1000000' }), liquidationPenalty: Percentage.createFrom({ value: 5 }), usageAsCollateralEnabled: true, apy: Percentage.createFrom({ value: 0.5 }), @@ -223,29 +227,29 @@ export const testTargetLendingPoolRequiredSwaps = SparkLendingPool.createFrom({ }, debts: { record: { - [testCollateral.address.value]: { - token: testCollateral, + [otherTestDebt.address.value]: { + token: otherTestDebt, price: Price.createFrom({ value: '100', - baseToken: testCollateral, - quoteToken: testCollateral, + baseToken: otherTestDebt, + quoteToken: CurrencySymbol.USD, }), priceUSD: Price.createFrom({ value: '100', - baseToken: testCollateral, - quoteToken: testCollateral, + baseToken: otherTestDebt, + quoteToken: CurrencySymbol.USD, }), rate: Percentage.createFrom({ value: 5 }), - totalBorrowed: TokenAmount.createFrom({ token: testCollateral, amount: '100000' }), - debtCeiling: TokenAmount.createFrom({ token: testCollateral, amount: '1000000' }), - debtAvailable: TokenAmount.createFrom({ token: testCollateral, amount: '100000' }), - dustLimit: TokenAmount.createFrom({ token: testCollateral, amount: '100' }), + totalBorrowed: TokenAmount.createFrom({ token: otherTestDebt, amount: '100000' }), + debtCeiling: TokenAmount.createFrom({ token: otherTestDebt, amount: '1000000' }), + debtAvailable: TokenAmount.createFrom({ token: otherTestDebt, amount: '100000' }), + dustLimit: TokenAmount.createFrom({ token: otherTestDebt, amount: '100' }), originationFee: Percentage.createFrom({ value: 1 }), borrowingEnabled: true, }, }, }, - baseCurrency: testCollateral, + baseCurrency: CurrencySymbol.USD, poolId: { protocol: testTargetProtocol, }, diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index 87f4009439..bf8ec3db64 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -11,7 +11,7 @@ import { import { mockRefinanceContext, mockRefinanceContextRequiredSwaps } from './mocks/contextMock' describe('Refinance', () => { - describe.skip('to the position with the same collateral and debt (no swaps)', () => { + describe('to the position with the same collateral and debt (no swaps)', () => { let simulation: ISimulation beforeAll(async () => { simulation = await refinanceLendingToLending( @@ -65,44 +65,27 @@ describe('Refinance', () => { describe('to the position with the different collateral and debt (with swaps)', () => { let simulation: ISimulation beforeAll(async () => { - // Swapped the tokens around to force two swaps - console.log( - 'targetDebt', - testTargetLendingPoolRequiredSwaps.debts.get({ - token: testSourcePosition.collateralAmount.token, - })!.token, - ) - console.log( - 'targetColl', - testTargetLendingPoolRequiredSwaps.collaterals.get({ - token: testSourcePosition.debtAmount.token, - })!.token, - ) - simulation = await refinanceLendingToLending( { position: testSourcePosition, targetPool: testTargetLendingPoolRequiredSwaps, - // Note: they two tokens have been inverted - targetDebt: testTargetLendingPoolRequiredSwaps.debts.get({ - token: testSourcePosition.collateralAmount.token, - })!.token, - targetCollateral: testTargetLendingPoolRequiredSwaps.collaterals.get({ - token: testSourcePosition.debtAmount.token, - })!.token, + targetDebt: otherTestDebt, + targetCollateral: otherTestCollateral, slippage: Percentage.createFrom({ value: 1 }), }, mockRefinanceContextRequiredSwaps, ) }) - it.only('should include two swap steps', async () => { - const steps = simulation.steps.filter((step) => !step.skip).map((step) => step.type) + it('should include two swap steps', async () => { + const steps = simulation.steps + .filter((step) => !step.skip) + .filter((step) => step.type === SimulationSteps.Swap) expect(steps.length).toBe(2) }) - it('should open position with other collater', async () => { + it('should open position with other collateral', async () => { const targetPosition = simulation.targetPosition expect(targetPosition.collateralAmount.token).toEqual(otherTestCollateral) @@ -111,7 +94,7 @@ describe('Refinance', () => { it('should open position with other debt', async () => { const targetPosition = simulation.targetPosition - expect(targetPosition.debtAmount).toEqual(otherTestDebt) + expect(targetPosition.debtAmount.token).toEqual(otherTestDebt) }) it('should open position as required target pool', async () => { @@ -126,10 +109,6 @@ describe('Refinance', () => { expect(targetPosition.positionId).toBeDefined() }) - it('should open position with the same collateral amount', async () => { - expect(mockRefinanceContext.swapManager.getSwapQuoteExactInput.mock.calls.length).toBe(2) - }) - it('should exchange all collateral from source position ', async () => { expect( mockRefinanceContext.swapManager.getSwapQuoteExactInput.mock.calls[0][0].fromAmount, diff --git a/sdk/swap-common/src/implementation/SpotPriceMap.ts b/sdk/swap-common/src/implementation/SpotPriceMap.ts deleted file mode 100644 index 3a32c8bed8..0000000000 --- a/sdk/swap-common/src/implementation/SpotPriceMap.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - Price, - type IPrice, - type IToken, - type Maybe, - type AddressValue, -} from '@summerfi/sdk-common/common' -import { ISpotPriceMap, ISpotPriceMapRecord } from '../interfaces/ISpotPriceMap' - -export class SpotPriceMap implements ISpotPriceMap { - readonly record: ISpotPriceMapRecord = {} - - private constructor(params: ISpotPriceMap) { - this._createSpotPriceMap(params) - } - - static createFrom(params: Price): SpotPriceMap { - return new SpotPriceMap(params) - } - - public add(params: { price: IPrice }): void { - this.record[params.price.baseToken.address.value as AddressValue] = Price.createFrom( - params.price, - ) - } - - public get(params: { token: IToken }): Maybe { - return this.record[params.token.address.value] - } - - private _createSpotPriceMap(params: ISpotPriceMap): void { - return Object.entries(params.record).forEach(([, price]) => { - this.add({ price }) - }) - } -} diff --git a/sdk/swap-common/src/implementation/index.ts b/sdk/swap-common/src/implementation/index.ts deleted file mode 100644 index 014f75a08d..0000000000 --- a/sdk/swap-common/src/implementation/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SpotPriceMap } from './SpotPriceMap' diff --git a/sdk/swap-common/src/interfaces/ISpotPriceMap.ts b/sdk/swap-common/src/interfaces/ISpotPriceMap.ts deleted file mode 100644 index 922fc2f926..0000000000 --- a/sdk/swap-common/src/interfaces/ISpotPriceMap.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AddressValue, Price } from '@summerfi/sdk-common/common' - -export type ISpotPriceMapRecord = Record - -export interface ISpotPriceMap { - record: ISpotPriceMapRecord -} diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index 5594acc53a..ccfd8dcae9 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -7,7 +7,6 @@ import type { Address, } from '@summerfi/sdk-common/common' import { ChainId, CurrencySymbol } from '@summerfi/sdk-common/common' -import { SpotPriceMap } from '@summerfi/swap-common/implementation' import { ISwapProvider, ISwapManager } from '@summerfi/swap-common/interfaces' import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' From 5a869855cf524e111fa57a2f56d60c2ac268cce4 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 28 Mar 2024 14:59:58 +0000 Subject: [PATCH 13/26] feat: add summerFee resolver to SwapManager --- .../src/builders/SwapActionBuilder.ts | 2 +- .../tests/builders/SwapActionBuilder.spec.ts | 14 ++- .../tests/mocks/SwapManagerMock.ts | 2 +- .../src/common/implementation/Percentage.ts | 4 + .../src/common/implementation/TokenAmount.ts | 8 ++ sdk/sdk-common/src/simulation/Steps.ts | 2 +- .../src/handlers/getRefinanceSimulation.ts | 3 - .../simulator-engine/reducer/swapReducer.ts | 23 ++--- .../refinance/RefinanceLendingToLending.ts | 91 ++++++++++++------- .../src/strategies/refinance/Types.ts | 2 - .../src/interfaces/ISwapManager.ts | 19 ++++ .../src/implementation/SwapManager.ts | 20 ++-- 12 files changed, 123 insertions(+), 67 deletions(-) diff --git a/sdk/order-planner-service/src/builders/SwapActionBuilder.ts b/sdk/order-planner-service/src/builders/SwapActionBuilder.ts index 56e4c80033..5fccbd9dd0 100644 --- a/sdk/order-planner-service/src/builders/SwapActionBuilder.ts +++ b/sdk/order-planner-service/src/builders/SwapActionBuilder.ts @@ -23,7 +23,7 @@ export const SwapActionBuilder: ActionBuilder = async (params): arguments: { fromAmount: step.inputs.fromTokenAmount, toMinimumAmount: step.inputs.toTokenAmount, - fee: step.inputs.fee, + fee: step.inputs.summerFee, withData: swapData.calldata, collectFeeInFromToken: true, }, diff --git a/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts b/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts index 9bcd519eec..cfbb4bf566 100644 --- a/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts +++ b/sdk/order-planner-service/tests/builders/SwapActionBuilder.spec.ts @@ -63,14 +63,12 @@ describe('Swap Action Builder', () => { routes: [], fromTokenAmount: fromAmount, toTokenAmount: toAmount, - prices: [ - Price.createFrom({ - value: '0', - quoteToken: fromAmount.token, - baseToken: toAmount.token, - }), - ], - fee: fee, + spotPrice: Price.createFrom({ + value: '0', + quoteToken: fromAmount.token, + baseToken: toAmount.token, + }), + summerFee: fee, slippage, }, outputs: { diff --git a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts index 353b8be50f..b794c306f8 100644 --- a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts +++ b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts @@ -53,7 +53,7 @@ export class SwapManagerMock implements ISwapManager { return this._quoteDataReturnValue } - async getSpotPrices(params: { chainInfo: ChainInfo; tokens: Token[] }): Promise { + async getSpotPrice(params: { chainInfo: ChainInfo; baseToken: Token }): Promise { return this._spotDataReturnValue } diff --git a/sdk/sdk-common/src/common/implementation/Percentage.ts b/sdk/sdk-common/src/common/implementation/Percentage.ts index 283eae3101..210f71917e 100644 --- a/sdk/sdk-common/src/common/implementation/Percentage.ts +++ b/sdk/sdk-common/src/common/implementation/Percentage.ts @@ -25,6 +25,10 @@ export class Percentage implements IPercentage { return Percentage.createFrom({ value: this.value + percentage.value }) } + subtract(percentage: IPercentage): Percentage { + return Percentage.createFrom({ value: this.value - percentage.value }) + } + toProportion(): number { return this.value / 100 } diff --git a/sdk/sdk-common/src/common/implementation/TokenAmount.ts b/sdk/sdk-common/src/common/implementation/TokenAmount.ts index 078d3a268a..bfcc5db6d7 100644 --- a/sdk/sdk-common/src/common/implementation/TokenAmount.ts +++ b/sdk/sdk-common/src/common/implementation/TokenAmount.ts @@ -83,6 +83,14 @@ export class TokenAmount implements ITokenAmount { return new BigNumber(this.amount).times(this._baseUnitFactor) } + public fromBaseUnit(): string { + return new BigNumber(this.amount).div(this._baseUnitFactor).toFixed(0) + } + + public fromBaseUnitAsBn(): BigNumber { + return new BigNumber(this.amount).div(this._baseUnitFactor) + } + public toBN(): BigNumber { return new BigNumber(this.amount) } diff --git a/sdk/sdk-common/src/simulation/Steps.ts b/sdk/sdk-common/src/simulation/Steps.ts index e2cd398541..fde1d2b5fa 100644 --- a/sdk/sdk-common/src/simulation/Steps.ts +++ b/sdk/sdk-common/src/simulation/Steps.ts @@ -67,7 +67,7 @@ export interface SwapStep fromTokenAmount: TokenAmount toTokenAmount: TokenAmount slippage: Percentage - fee: Percentage + summerFee: Percentage }, { receivedAmount: TokenAmount diff --git a/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts b/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts index 9fc2f8f153..238c34fc6a 100644 --- a/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts +++ b/sdk/sdk-server/src/handlers/getRefinanceSimulation.ts @@ -1,5 +1,4 @@ import { z } from 'zod' -import { Percentage } from '@summerfi/sdk-common/common' import type { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { refinanceLendingToLending, @@ -18,8 +17,6 @@ export const getRefinanceSimulation = publicProcedure const dependencies: IRefinanceDependencies = { swapManager: opts.ctx.swapManager, protocolManager: opts.ctx.protocolManager, - // TODO: get summer fee from the config provider - getSummerFee: () => Percentage.createFrom({ value: 0 }), } const simulation = await refinanceLendingToLending(args, dependencies) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 27874d3360..b0431c402c 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -1,4 +1,5 @@ import { TokenAmount, Price, Percentage } from '@summerfi/sdk-common/common' +import { applyPercentage } from '@summerfi/sdk-common/utils' import { steps } from '@summerfi/sdk-common/simulation' import { addBalance, subtractBalance } from '../../utils' import { ISimulationState } from '../../../interfaces/simulation' @@ -10,16 +11,18 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim const baseToken = step.inputs.toTokenAmount.token const quoteToken = step.inputs.fromTokenAmount.token + // We require both from & to be at similar decimal precisions const offerPrice = Price.createFrom({ value: step.inputs.toTokenAmount - .toBaseUnitAsBn() - .div(step.inputs.fromTokenAmount.toBaseUnitAsBn()) + .fromBaseUnitAsBn() + .div(step.inputs.fromTokenAmount.fromBaseUnitAsBn()) .toString(), baseToken, quoteToken, }) const spotPrice = step.inputs.spotPrice + const fromAmountPreSummerFee = applyPercentage(step.inputs.fromTokenAmount, step.inputs.summerFee) return { ...state, @@ -33,6 +36,8 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim provider: step.inputs.provider, // Note: Can add routes back in later if we need them for the UI // routes: step.inputs.routes, + // SummerFee should already have been subtracted by this stage + // Should be removed from amount when getting swap quote fromTokenAmount: step.inputs.fromTokenAmount, toTokenAmount: step.inputs.toTokenAmount, slippage: Percentage.createFrom({ value: step.inputs.slippage.value }), @@ -41,7 +46,7 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim priceImpact: calculatePriceImpact(spotPrice, offerPrice), summerFee: TokenAmount.createFrom({ token: step.inputs.fromTokenAmount.token, - amount: step.inputs.fromTokenAmount.multiply(step.inputs.fee.value).amount, + amount: fromAmountPreSummerFee.multiply(step.inputs.summerFee.toProportion()).amount, }), }, }, @@ -51,14 +56,10 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim /** * - * @param marketPrice - This price represent how much it will cost for selling some very small amount - * such as 0.1. It is the best possible price on the market. - * @param offerPrice - If the amount we would like to sell we might get deeper into the liquidity - * meaning the price won't be a good as when you sell small amount. This is the price that is - * represent how much it will cost for us to sell the desired amount. - * - * Both prices might be equal which means that there is no price impact. Having no - * price impact means that you sell at the best possible price. + * @param marketPrice - This price represents a blend of spot prices from various exchanges. + * @param offerPrice - The offer price is price quoted to us by a liquidity provider and takes + * into account price impact - where price impact is a measure of how much our trade + * affects the price. It is determined by the breadth and depth of liquidity. */ export function calculatePriceImpact(marketPrice: Price, offerPrice: Price): Percentage { return Percentage.createFrom({ diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index 14f2f82da1..9523810886 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -1,3 +1,4 @@ +import { applyPercentage } from '@summerfi/sdk-common/utils' import { FlashloanProvider, ISimulation, @@ -9,7 +10,7 @@ import { Simulator } from '../../implementation/simulator-engine' import { Position, TokenAmount, Percentage, Price } from '@summerfi/sdk-common/common' import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common/utils' import { IRefinanceParameters } from '@summerfi/sdk-common/orders' -import { DebtConfig, isLendingPool } from '@summerfi/sdk-common/protocols' +import { isLendingPool } from '@summerfi/sdk-common/protocols' import { getReferencedValue } from '../../implementation/utils' import { refinanceLendingToLendingStrategy } from './Strategy' import { type IRefinanceDependencies } from './Types' @@ -52,8 +53,15 @@ export async function refinanceLendingToLending( }) ).price - // TODO: the swap quote should also include the summer fee, in this case we need to know when we are taking the fee, - // before or after the swap, it influences actual call to oneInch api + const collateralSwapSummerFee = dependencies.swapManager.getSummerFee({ + from: { token: position.collateralAmount.token, protocol: position.pool.protocol }, + to: { token: targetCollateralConfig.token, protocol: targetPool.protocol }, + }) + const debtSwapSummerFee = dependencies.swapManager.getSummerFee({ + from: { token: position.debtAmount.token, protocol: position.pool.protocol }, + to: { token: targetDebtConfig.token, protocol: targetPool.protocol }, + }) + const simulation = await simulator .next(async () => ({ name: 'Flashloan', @@ -84,8 +92,12 @@ export async function refinanceLendingToLending( inputs: { ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: position.pool.protocol.chainInfo, - // TODO: Properly implement swaps - fromAmount: position.collateralAmount, + fromAmount: applyPercentage( + position.collateralAmount, + Percentage.createFrom({ + value: 100 - collateralSwapSummerFee.value, + }), + ), toToken: targetCollateralConfig.token, })), spotPrice: ( @@ -96,7 +108,7 @@ export async function refinanceLendingToLending( }) ).price, slippage: Percentage.createFrom({ value: args.slippage.value }), - fee: dependencies.getSummerFee(), + summerFee: collateralSwapSummerFee, }, skip: isCollateralSwapSkipped, })) @@ -104,13 +116,13 @@ export async function refinanceLendingToLending( name: 'DepositBorrowToTarget', type: SimulationSteps.DepositBorrow, inputs: { - borrowAmount: await calculateBorrowAmount( + borrowAmount: await calculateBorrowAmount({ isDebtSwapSkipped, - position.debtAmount, + prevDebtAmount: position.debtAmount, debtSpotPrice, - targetDebtConfig, - args, - ), + slippage: Percentage.createFrom(args.slippage), + summerFee: debtSwapSummerFee, + }), depositAmount: ctx.getReference( isCollateralSwapSkipped ? ['PaybackWithdrawFromSource', 'withdrawAmount'] @@ -130,14 +142,17 @@ export async function refinanceLendingToLending( inputs: { ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: args.position.pool.protocol.chainInfo, - fromAmount: getReferencedValue( - ctx.getReference(['DepositBorrowToTarget', 'borrowAmount']), + fromAmount: applyPercentage( + getReferencedValue(ctx.getReference(['DepositBorrowToTarget', 'borrowAmount'])), + Percentage.createFrom({ + value: 100 - debtSwapSummerFee.value, + }), ), toToken: targetDebtConfig.token, })), spotPrice: debtSpotPrice, slippage: Percentage.createFrom({ value: args.slippage.value }), - fee: dependencies.getSummerFee(), + summerFee: debtSwapSummerFee, }, skip: isDebtSwapSkipped, })) @@ -185,14 +200,18 @@ export async function refinanceLendingToLending( * When the DebtSwap step is skipped we simply return the previous position's debt amount * When a DebtSwap is required we need to borrow enough to cover the original flashloan after * accounting the swap and assuming the worst case scenario on slippage IE max slippage. + * + * We also need to factor in Summer fees ahead of time */ -async function calculateBorrowAmount( - isDebtSwapSkipped: boolean, - prevDebtAmount: TokenAmount, - debtSpotPrice: Price, - targetDebtConfig: DebtConfig, - params: IRefinanceParameters, -) { +async function calculateBorrowAmount(params: { + isDebtSwapSkipped: boolean + prevDebtAmount: TokenAmount + debtSpotPrice: Price + slippage: Percentage + summerFee: Percentage +}) { + const { isDebtSwapSkipped, prevDebtAmount, debtSpotPrice, slippage, summerFee } = params + /** * If no swap is required we simply borrow the same amount of debt, and the same asset, * on the target protocol @@ -210,22 +229,28 @@ async function calculateBorrowAmount( * 5000 DAI * (0.98 USDC/DAI) / (1 - 0.01) = 4949.49 USDC (slippage adjusted borrow amount) * where 0.01 is 1% slippage * + * (5000 DAI * (0.98 USDC/DAI) / (1 - 0.01)) / (1 - 0.002) = 4959.41 USDC (slippage + summer fee adjusted borrow amount) + * where 0.002 is 20 basis pt fee as an example + * * More generally we'd write this as - * sourcePositionDebt * targetDebtQuotedInSourceDebtPrice / (one - slippage) = borrowAmount + * (sourcePositionDebt * targetDebtQuotedInSourceDebtPrice / (one - slippage)) / (one - summer fee) = borrowAmount */ - // TODO: Token equals - const borrowAmount = prevDebtAmount - .multiply(debtSpotPrice.toString()) - .divide( - Percentage.createFrom({ - value: 100 - params.slippage.value, - }).toString(), - ) - .multiply(100) - .toString() + const borrowAmount = prevDebtAmount.multiply(debtSpotPrice.toString()) + const borrowAmountAdjustedForSlippage = applyPercentage( + borrowAmount, + Percentage.createFrom({ + value: 100 - slippage.value, + }), + ) + const borrowAmountAdjustedForSlippageAndSummerFee = applyPercentage( + borrowAmountAdjustedForSlippage, + Percentage.createFrom({ + value: 100 - summerFee.value, + }), + ) return TokenAmount.createFrom({ - amount: borrowAmount, + amount: borrowAmountAdjustedForSlippageAndSummerFee.toString(), token: debtSpotPrice.baseToken, }) } diff --git a/sdk/simulator-service/src/strategies/refinance/Types.ts b/sdk/simulator-service/src/strategies/refinance/Types.ts index b2060c7e78..5728754ed2 100644 --- a/sdk/simulator-service/src/strategies/refinance/Types.ts +++ b/sdk/simulator-service/src/strategies/refinance/Types.ts @@ -1,9 +1,7 @@ -import { Percentage } from '@summerfi/sdk-common/common' import { type ISwapManager } from '@summerfi/swap-common/interfaces' import { type IProtocolManager } from '@summerfi/protocol-manager-common' export interface IRefinanceDependencies { swapManager: ISwapManager protocolManager: IProtocolManager - getSummerFee: () => Percentage } diff --git a/sdk/swap-common/src/interfaces/ISwapManager.ts b/sdk/swap-common/src/interfaces/ISwapManager.ts index b1169ebeb0..9916f45470 100644 --- a/sdk/swap-common/src/interfaces/ISwapManager.ts +++ b/sdk/swap-common/src/interfaces/ISwapManager.ts @@ -6,6 +6,7 @@ import type { Address, CurrencySymbol, } from '@summerfi/sdk-common/common' +import { IProtocol } from '@summerfi/sdk-common/protocols' import { SpotData } from '@summerfi/sdk-common/swap' import type { QuoteData, SwapData } from '@summerfi/sdk-common/swap' @@ -59,4 +60,22 @@ export interface ISwapManager { baseToken: Token quoteToken?: CurrencySymbol | Token }): Promise + + /** + * @name getSummerFee + * @description Returns the Summer fee to charge on the swap + * @param protocol The protocol name and chain info + * @param fromToken The source token + * @param toToken The target token + */ + getSummerFee(params: { + from: { + protocol: IProtocol + token: Token + } + to: { + protocol: IProtocol + token: Token + } + }): Percentage } diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index ccfd8dcae9..2ed83aed29 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -1,12 +1,8 @@ import type { Maybe } from '@summerfi/sdk-common/common/aliases' -import type { - ChainInfo, - TokenAmount, - Token, - Percentage, - Address, -} from '@summerfi/sdk-common/common' +import type { ChainInfo, TokenAmount, Token, Address } from '@summerfi/sdk-common/common' +import { Percentage } from '@summerfi/sdk-common/common' import { ChainId, CurrencySymbol } from '@summerfi/sdk-common/common' +import { IProtocol } from '@summerfi/sdk-common/protocols' import { ISwapProvider, ISwapManager } from '@summerfi/swap-common/interfaces' import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' @@ -72,6 +68,16 @@ export class SwapManager implements ISwapManager { return provider.getSpotPrice(params) } + getSummerFee(params: { + from: { protocol: IProtocol; token: Token } + to: { protocol: IProtocol; token: Token } + }): Percentage { + // TODO: Implement with appropriate logic + return Percentage.createFrom({ + value: 0.2, + }) + } + private _registerProvider(provider: ISwapProvider, forChainIds: number[]): void { for (const chainId of forChainIds) { const providers = this._providersByChainId.get(chainId) || [] From d80f20b72d7ce3745a7209ae2b84d4448fccd228 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 28 Mar 2024 15:06:04 +0000 Subject: [PATCH 14/26] refactor: replace applyPercentage with subtract --- .../simulator-engine/reducer/swapReducer.ts | 2 +- .../refinance/RefinanceLendingToLending.ts | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index b0431c402c..13a8744bab 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -37,7 +37,7 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim // Note: Can add routes back in later if we need them for the UI // routes: step.inputs.routes, // SummerFee should already have been subtracted by this stage - // Should be removed from amount when getting swap quote + // Should be subtracted from `from` amount when getting swap quote in simulator fromTokenAmount: step.inputs.fromTokenAmount, toTokenAmount: step.inputs.toTokenAmount, slippage: Percentage.createFrom({ value: step.inputs.slippage.value }), diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index 9523810886..7684e0ad8f 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -1,4 +1,4 @@ -import { applyPercentage } from '@summerfi/sdk-common/utils' +import { subtractPercentage } from '@summerfi/sdk-common/utils' import { FlashloanProvider, ISimulation, @@ -92,10 +92,10 @@ export async function refinanceLendingToLending( inputs: { ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: position.pool.protocol.chainInfo, - fromAmount: applyPercentage( + fromAmount: subtractPercentage( position.collateralAmount, Percentage.createFrom({ - value: 100 - collateralSwapSummerFee.value, + value: collateralSwapSummerFee.value, }), ), toToken: targetCollateralConfig.token, @@ -142,10 +142,10 @@ export async function refinanceLendingToLending( inputs: { ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: args.position.pool.protocol.chainInfo, - fromAmount: applyPercentage( + fromAmount: subtractPercentage( getReferencedValue(ctx.getReference(['DepositBorrowToTarget', 'borrowAmount'])), Percentage.createFrom({ - value: 100 - debtSwapSummerFee.value, + value: debtSwapSummerFee.value, }), ), toToken: targetDebtConfig.token, @@ -236,16 +236,16 @@ async function calculateBorrowAmount(params: { * (sourcePositionDebt * targetDebtQuotedInSourceDebtPrice / (one - slippage)) / (one - summer fee) = borrowAmount */ const borrowAmount = prevDebtAmount.multiply(debtSpotPrice.toString()) - const borrowAmountAdjustedForSlippage = applyPercentage( + const borrowAmountAdjustedForSlippage = subtractPercentage( borrowAmount, Percentage.createFrom({ - value: 100 - slippage.value, + value: slippage.value, }), ) - const borrowAmountAdjustedForSlippageAndSummerFee = applyPercentage( + const borrowAmountAdjustedForSlippageAndSummerFee = subtractPercentage( borrowAmountAdjustedForSlippage, Percentage.createFrom({ - value: 100 - summerFee.value, + value: summerFee.value, }), ) From c6b3f9d48e70f18a20dc75c51a57450235cb7af3 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 28 Mar 2024 15:13:39 +0000 Subject: [PATCH 15/26] chore: fix minor issues --- sdk/order-planner-service/tests/mocks/SwapManagerMock.ts | 5 +++++ sdk/simulator-service/tests/mocks/contextMock.ts | 1 + sdk/swap-service/src/implementation/SwapManager.ts | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts index b794c306f8..a8466d96c0 100644 --- a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts +++ b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts @@ -6,6 +6,7 @@ export class SwapManagerMock implements ISwapManager { private _swapDataReturnValue: SwapData = {} as SwapData private _quoteDataReturnValue: QuoteData = {} as QuoteData private _spotDataReturnValue: SpotData = {} as SpotData + private _summerFeeValue: Percentage = Percentage.createFrom({ value: 0 }) private _lastGetSwapDataExactInputParams: | { @@ -57,6 +58,10 @@ export class SwapManagerMock implements ISwapManager { return this._spotDataReturnValue } + getSummerFee(): Percentage { + return this._summerFeeValue + } + get swapDataReturnValue(): SwapData { return this._swapDataReturnValue } diff --git a/sdk/simulator-service/tests/mocks/contextMock.ts b/sdk/simulator-service/tests/mocks/contextMock.ts index 6b5df87d01..faeff323d6 100644 --- a/sdk/simulator-service/tests/mocks/contextMock.ts +++ b/sdk/simulator-service/tests/mocks/contextMock.ts @@ -82,6 +82,7 @@ export const mockRefinanceContext = { getSwapDataExactInput, getSwapQuoteExactInput: jest.fn().mockImplementation(getSwapQuoteExactInput), getSpotPrice, + getSummerFee: jest.fn().mockImplementation(mockGetFee), }, } diff --git a/sdk/swap-service/src/implementation/SwapManager.ts b/sdk/swap-service/src/implementation/SwapManager.ts index 2ed83aed29..f724b40648 100644 --- a/sdk/swap-service/src/implementation/SwapManager.ts +++ b/sdk/swap-service/src/implementation/SwapManager.ts @@ -1,7 +1,6 @@ import type { Maybe } from '@summerfi/sdk-common/common/aliases' import type { ChainInfo, TokenAmount, Token, Address } from '@summerfi/sdk-common/common' -import { Percentage } from '@summerfi/sdk-common/common' -import { ChainId, CurrencySymbol } from '@summerfi/sdk-common/common' +import { ChainId, CurrencySymbol, Percentage } from '@summerfi/sdk-common/common' import { IProtocol } from '@summerfi/sdk-common/protocols' import { ISwapProvider, ISwapManager } from '@summerfi/swap-common/interfaces' import type { QuoteData, SwapData, SwapProviderType, SpotData } from '@summerfi/sdk-common/swap' @@ -68,6 +67,7 @@ export class SwapManager implements ISwapManager { return provider.getSpotPrice(params) } + // eslint-disable-next-line @typescript-eslint/no-unused-vars getSummerFee(params: { from: { protocol: IProtocol; token: Token } to: { protocol: IProtocol; token: Token } From 4659ec0d6d992f6debff936134e5af20996684c0 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 28 Mar 2024 18:34:58 +0000 Subject: [PATCH 16/26] test: fix issues with e2e's --- sdk/sdk-e2e/tests/refinance.test.ts | 2 ++ sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sdk/sdk-e2e/tests/refinance.test.ts b/sdk/sdk-e2e/tests/refinance.test.ts index 23b6e18b80..2e2c6acb5c 100644 --- a/sdk/sdk-e2e/tests/refinance.test.ts +++ b/sdk/sdk-e2e/tests/refinance.test.ts @@ -198,6 +198,8 @@ describe.skip('Refinance | SDK', () => { const refinanceParameters: IRefinanceParameters = { position: prevPosition, targetPool: newLendingPool, + targetCollateral: prevPosition.collateralAmount.token, + targetDebt: prevPosition.debtAmount.token, slippage: Percentage.createFrom({ value: 0.5 }), } diff --git a/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts b/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts index b6695ea63e..e1000e361e 100644 --- a/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts +++ b/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts @@ -133,6 +133,8 @@ describe.skip('Refinance Maker Spark | SDK', () => { await sdk.simulator.refinance.simulateRefinancePosition({ position: makerPosition, targetPool: poolAgain, + targetCollateral: makerPosition.collateralAmount.token, + targetDebt: makerPosition.debtAmount.token, slippage: Percentage.createFrom({ value: 0.2 }), } as IRefinanceParameters) From f36d761873249f1e03263c602f194c63a627c87b Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Wed, 3 Apr 2024 09:35:53 +0100 Subject: [PATCH 17/26] chore: address PR comments --- sdk/sdk-client/src/utils/PositionUtils.ts | 2 +- .../src/common/implementation/TokenAmount.ts | 19 +++++++++++++++++-- sdk/sdk-common/src/swap/SimulatedSwapData.ts | 6 ++++-- .../implementation/utils/SimulatorUtils.ts | 2 +- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sdk/sdk-client/src/utils/PositionUtils.ts b/sdk/sdk-client/src/utils/PositionUtils.ts index 1e6ec1d74a..0da030eb30 100644 --- a/sdk/sdk-client/src/utils/PositionUtils.ts +++ b/sdk/sdk-client/src/utils/PositionUtils.ts @@ -4,7 +4,7 @@ import { type Price, type TokenAmount, } from '@summerfi/sdk-common/common' -import { default as BigNumber } from 'bignumber.js' +import { BigNumber } from 'bignumber.js' export class PositionUtils { static getLTV({ diff --git a/sdk/sdk-common/src/common/implementation/TokenAmount.ts b/sdk/sdk-common/src/common/implementation/TokenAmount.ts index 078d3a268a..ca7e14eb79 100644 --- a/sdk/sdk-common/src/common/implementation/TokenAmount.ts +++ b/sdk/sdk-common/src/common/implementation/TokenAmount.ts @@ -1,4 +1,5 @@ import { BigNumber } from 'bignumber.js' +import {Percentage} from "./Percentage"; import { Token } from './Token' import { SerializationService } from '../../services/SerializationService' import { ITokenAmount } from '../interfaces/ITokenAmount' @@ -60,14 +61,28 @@ export class TokenAmount implements ITokenAmount { }) } - public multiply(multiplier: string | number): TokenAmount { + public multiply(multiplier: Percentage | string | number): TokenAmount { + if (multiplier instanceof Percentage) { + return new TokenAmount({ + token: this.token, + amount: this.amountBN.times(multiplier.value).toString(), + }) + } + return new TokenAmount({ token: this.token, amount: this.amountBN.times(multiplier).toString(), }) } - public divide(divisor: string | number): TokenAmount { + public divide(divisor: Percentage | string | number): TokenAmount { + if (divisor instanceof Percentage) { + return new TokenAmount({ + token: this.token, + amount: this.amountBN.div(divisor.value).toString(), + }) + } + return new TokenAmount({ token: this.token, amount: this.amountBN.div(divisor).toString() }) } diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts index 9a840a29c8..c9872f8065 100644 --- a/sdk/sdk-common/src/swap/SimulatedSwapData.ts +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -10,8 +10,10 @@ import { QuoteData } from './QuoteData' */ export type SimulatedSwapData = Omit & { slippage: Percentage - offerPrice: Price - marketPrice: Price + /* This is the impacted price that takes into account trade size */ + offerPrice: Price; + /* This is the un-impacted blend of market prices from various DEXs */ + marketPrice: Price; priceImpact: Percentage summerFee: TokenAmount } diff --git a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts index 6d663a9f0c..443487975b 100644 --- a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts +++ b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts @@ -1,4 +1,4 @@ -import type { + import type { ReferenceableField, SimulationStrategy, ValueReference, From 43218240024b6aa1e2c1691432fa7107fd763afa Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Wed, 3 Apr 2024 10:28:45 +0100 Subject: [PATCH 18/26] fix: merge issues --- .../src/implementation/OrderPlanner.ts | 2 +- .../tests/service/OrderPlannerService.spec.ts | 9 ++++++-- .../simulations/RefinanceSimulationManager.ts | 2 +- .../src/common/implementation/TokenAmount.ts | 2 +- sdk/sdk-common/src/swap/SimulatedSwapData.ts | 4 ++-- sdk/sdk-e2e/tests/refinance.test.ts | 0 sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts | 4 ++-- .../implementation/utils/SimulatorUtils.ts | 2 +- .../src/mocks/SwapManagerMock.ts | 22 +++++++++++++++++-- 9 files changed, 35 insertions(+), 12 deletions(-) delete mode 100644 sdk/sdk-e2e/tests/refinance.test.ts diff --git a/sdk/order-planner-common/src/implementation/OrderPlanner.ts b/sdk/order-planner-common/src/implementation/OrderPlanner.ts index a13cc8d2dc..e94a582b5d 100644 --- a/sdk/order-planner-common/src/implementation/OrderPlanner.ts +++ b/sdk/order-planner-common/src/implementation/OrderPlanner.ts @@ -75,7 +75,7 @@ export class OrderPlanner implements IOrderPlanner { return actionBuildersMap[step.type] as ActionBuilder } - private _getStrategyName(simulation: Simulation): string { + private _getStrategyName(simulation: ISimulation): string { return `${simulation.simulationType}${simulation.sourcePosition?.pool.protocol.name}${simulation.targetPosition?.pool.protocol.name}` } diff --git a/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts b/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts index fa9ac83668..4a00d371c6 100644 --- a/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts +++ b/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts @@ -1,7 +1,13 @@ -import { FlashloanProvider, ISimulation, SimulationType, TokenTransferTargetType } from '@summerfi/sdk-common/simulation' +import { + FlashloanAction, + SetApprovalAction, + ReturnFundsAction, +} from '@summerfi/protocol-plugins/plugins/common' +import { FlashloanProvider, ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { DeploymentIndex } from '@summerfi/deployment-utils' import { ISwapManager } from '@summerfi/swap-common/interfaces' import { Address, AddressValue, ChainFamilyMap, ChainInfo } from '@summerfi/sdk-common/common' +import { ProtocolName } from '@summerfi/sdk-common/protocols' import { IPositionsManager } from '@summerfi/sdk-common/orders' import { SetupDeployments } from '../utils/SetupDeployments' import { UserMock } from '../mocks/UserMock' @@ -18,7 +24,6 @@ import { import assert from 'assert' import { IUser } from '@summerfi/sdk-common/user' import { - IContractProvider, IPriceService, IProtocolPluginsRegistry, ITokenService, diff --git a/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts b/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts index e4f262b5cd..c81f61c734 100644 --- a/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts +++ b/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts @@ -10,7 +10,7 @@ export class RefinanceSimulationManager extends IRPCClient { public async simulateRefinancePosition( params: IRefinanceParameters, - ): Promise> { + ): Promise> { const refinanceParameters: IRefinanceParameters = { position: { positionId: params.position.positionId, diff --git a/sdk/sdk-common/src/common/implementation/TokenAmount.ts b/sdk/sdk-common/src/common/implementation/TokenAmount.ts index ca7e14eb79..85f8ec0d7f 100644 --- a/sdk/sdk-common/src/common/implementation/TokenAmount.ts +++ b/sdk/sdk-common/src/common/implementation/TokenAmount.ts @@ -1,5 +1,5 @@ import { BigNumber } from 'bignumber.js' -import {Percentage} from "./Percentage"; +import { Percentage } from './Percentage' import { Token } from './Token' import { SerializationService } from '../../services/SerializationService' import { ITokenAmount } from '../interfaces/ITokenAmount' diff --git a/sdk/sdk-common/src/swap/SimulatedSwapData.ts b/sdk/sdk-common/src/swap/SimulatedSwapData.ts index c9872f8065..3ead5c38ff 100644 --- a/sdk/sdk-common/src/swap/SimulatedSwapData.ts +++ b/sdk/sdk-common/src/swap/SimulatedSwapData.ts @@ -11,9 +11,9 @@ import { QuoteData } from './QuoteData' export type SimulatedSwapData = Omit & { slippage: Percentage /* This is the impacted price that takes into account trade size */ - offerPrice: Price; + offerPrice: Price /* This is the un-impacted blend of market prices from various DEXs */ - marketPrice: Price; + marketPrice: Price priceImpact: Percentage summerFee: TokenAmount } diff --git a/sdk/sdk-e2e/tests/refinance.test.ts b/sdk/sdk-e2e/tests/refinance.test.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts b/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts index ab788f5f64..2f64f82f58 100644 --- a/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts +++ b/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts @@ -14,7 +14,7 @@ import { ProtocolName, isLendingPool } from '@summerfi/sdk-common/protocols' import { makeSDK, type Chain, type User, Protocol } from '@summerfi/sdk-client' import { TokenSymbol } from '@summerfi/sdk-common/common/enums' import { IPositionsManager, IRefinanceParameters, Order } from '@summerfi/sdk-common/orders' -import { Simulation, SimulationType } from '@summerfi/sdk-common/simulation' +import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { TransactionUtils } from './utils/TransactionUtils' import { decodeActionCalldata, @@ -162,7 +162,7 @@ describe.skip('Refinance Maker Spark | SDK', () => { assert(false, 'Spark pool type is not lending') } - const refinanceSimulation: Simulation = + const refinanceSimulation: ISimulation = await sdk.simulator.refinance.simulateRefinancePosition({ position: makerPosition, targetPool: sparkPool, diff --git a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts index 443487975b..6d663a9f0c 100644 --- a/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts +++ b/sdk/simulator-service/src/implementation/utils/SimulatorUtils.ts @@ -1,4 +1,4 @@ - import type { +import type { ReferenceableField, SimulationStrategy, ValueReference, diff --git a/sdk/testing-utils/src/mocks/SwapManagerMock.ts b/sdk/testing-utils/src/mocks/SwapManagerMock.ts index 855a182013..6592d70d19 100644 --- a/sdk/testing-utils/src/mocks/SwapManagerMock.ts +++ b/sdk/testing-utils/src/mocks/SwapManagerMock.ts @@ -1,11 +1,20 @@ -import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' +import { + Address, + ChainInfo, + Percentage, + Token, + TokenAmount, + CurrencySymbol, +} from '@summerfi/sdk-common/common' +import { SpotData, SwapData, QuoteData } from '@summerfi/sdk-common/swap' import { ISwapManager } from '@summerfi/swap-common/interfaces' -import { QuoteData, SwapData } from '@summerfi/swap-common/types' export class SwapManagerMock implements ISwapManager { private _swapDataReturnValue: SwapData = {} as SwapData private _quoteDataReturnValue: QuoteData = {} as QuoteData + private _spotPricesReturnValue: SpotData = {} as SpotData + private _lastGetSwapDataExactInputParams: | { chainInfo: ChainInfo @@ -32,6 +41,15 @@ export class SwapManagerMock implements ISwapManager { this._quoteDataReturnValue = quoteData } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async getSpotPrices(params: { + chainInfo: ChainInfo + tokens: Token[] + quoteCurrency?: CurrencySymbol + }): Promise { + return this._spotPricesReturnValue + } + async getSwapDataExactInput(params: { chainInfo: ChainInfo fromAmount: TokenAmount From 2d0d5c4a4215c21b79ba29843ef494343883ce32 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Wed, 3 Apr 2024 13:20:15 +0100 Subject: [PATCH 19/26] refactor: Refinance params to source -> target position --- pnpm-lock.yaml | 3 + .../tests/mocks/SwapManagerMock.ts | 94 ------------------- .../tests/mocks/UserMock.ts | 12 --- .../tests/service/OrderPlannerService.spec.ts | 3 +- sdk/sdk-client/package.json | 3 +- .../simulations/RefinanceSimulationManager.ts | 27 +++--- .../queries/simulateRefinance.subtest.ts | 63 +++++++++---- sdk/sdk-client/tsconfig.test.json | 3 +- .../refinance/IRefinanceParameters.ts | 8 +- sdk/sdk-e2e/tests/refinance.test.ts | 0 sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts | 12 ++- .../simulator-engine/reducer/swapReducer.ts | 5 +- .../refinance/RefinanceLendingToLending.ts | 14 +-- sdk/simulator-service/tests/simulator.test.ts | 29 +++--- .../src/mocks/CollateralConfigMock.ts | 80 ++++++++++++++++ sdk/testing-utils/src/mocks/DebtConfigMock.ts | 83 ++++++++++++++++ .../src/mocks/SwapManagerMock.ts | 25 ++--- sdk/testing-utils/src/mocks/index.ts | 2 + 18 files changed, 280 insertions(+), 186 deletions(-) delete mode 100644 sdk/order-planner-service/tests/mocks/SwapManagerMock.ts delete mode 100644 sdk/order-planner-service/tests/mocks/UserMock.ts delete mode 100644 sdk/sdk-e2e/tests/refinance.test.ts create mode 100644 sdk/testing-utils/src/mocks/CollateralConfigMock.ts create mode 100644 sdk/testing-utils/src/mocks/DebtConfigMock.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3fe5bc020..3b1bd25f5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -881,6 +881,9 @@ importers: '@summerfi/jest-config': specifier: workspace:* version: link:../../packages/jest-config + '@summerfi/testing-utils': + specifier: workspace:* + version: link:../testing-utils '@summerfi/typescript-config': specifier: workspace:* version: link:../../packages/typescript-config diff --git a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts b/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts deleted file mode 100644 index a8466d96c0..0000000000 --- a/sdk/order-planner-service/tests/mocks/SwapManagerMock.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' -import { ISwapManager } from '@summerfi/swap-common/interfaces' -import { QuoteData, SwapData, SpotData } from '@summerfi/sdk-common/swap' - -export class SwapManagerMock implements ISwapManager { - private _swapDataReturnValue: SwapData = {} as SwapData - private _quoteDataReturnValue: QuoteData = {} as QuoteData - private _spotDataReturnValue: SpotData = {} as SpotData - private _summerFeeValue: Percentage = Percentage.createFrom({ value: 0 }) - - private _lastGetSwapDataExactInputParams: - | { - chainInfo: ChainInfo - fromAmount: TokenAmount - toToken: Token - recipient: Address - slippage: Percentage - } - | undefined - - private _lastGetSwapQuoteExactInputParams: - | { - chainInfo: ChainInfo - fromAmount: TokenAmount - toToken: Token - } - | undefined - - setSwapData(swapData: SwapData): void { - this._swapDataReturnValue = swapData - } - - setQuoteData(quoteData: QuoteData): void { - this._quoteDataReturnValue = quoteData - } - - async getSwapDataExactInput(params: { - chainInfo: ChainInfo - fromAmount: TokenAmount - toToken: Token - recipient: Address - slippage: Percentage - }): Promise { - this._lastGetSwapDataExactInputParams = params - return this._swapDataReturnValue - } - - async getSwapQuoteExactInput(params: { - chainInfo: ChainInfo - fromAmount: TokenAmount - toToken: Token - }): Promise { - this._lastGetSwapQuoteExactInputParams = params - return this._quoteDataReturnValue - } - - async getSpotPrice(params: { chainInfo: ChainInfo; baseToken: Token }): Promise { - return this._spotDataReturnValue - } - - getSummerFee(): Percentage { - return this._summerFeeValue - } - - get swapDataReturnValue(): SwapData { - return this._swapDataReturnValue - } - - get quoteDataReturnValue(): QuoteData { - return this._quoteDataReturnValue - } - - get lastGetSwapDataExactInputParams(): - | { - chainInfo: ChainInfo - fromAmount: TokenAmount - toToken: Token - recipient: Address - slippage: Percentage - } - | undefined { - return this._lastGetSwapDataExactInputParams - } - - get lastGetSwapQuoteExactInputParams(): - | { - chainInfo: ChainInfo - fromAmount: TokenAmount - toToken: Token - } - | undefined { - return this._lastGetSwapQuoteExactInputParams - } -} diff --git a/sdk/order-planner-service/tests/mocks/UserMock.ts b/sdk/order-planner-service/tests/mocks/UserMock.ts deleted file mode 100644 index 8884e3dca0..0000000000 --- a/sdk/order-planner-service/tests/mocks/UserMock.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Address, ChainInfo, Wallet } from '@summerfi/sdk-common/common' -import { IUser } from '@summerfi/sdk-common/user' - -export class UserMock implements IUser { - wallet: Wallet - chainInfo: ChainInfo - - constructor(params: { chainInfo: ChainInfo; walletAddress: Address }) { - this.chainInfo = params.chainInfo - this.wallet = Wallet.createFrom({ address: params.walletAddress }) - } -} diff --git a/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts b/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts index 4a00d371c6..8f66f69e4d 100644 --- a/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts +++ b/sdk/order-planner-service/tests/service/OrderPlannerService.spec.ts @@ -9,9 +9,8 @@ import { ISwapManager } from '@summerfi/swap-common/interfaces' import { Address, AddressValue, ChainFamilyMap, ChainInfo } from '@summerfi/sdk-common/common' import { ProtocolName } from '@summerfi/sdk-common/protocols' import { IPositionsManager } from '@summerfi/sdk-common/orders' +import { SwapManagerMock, UserMock } from '@summerfi/testing-utils' import { SetupDeployments } from '../utils/SetupDeployments' -import { UserMock } from '../mocks/UserMock' -import { SwapManagerMock } from '../mocks/SwapManagerMock' import { getRefinanceSimulation } from '../utils/RefinanceSimulation/RefinanceSimulation' import { OrderPlannerService } from '../../src/implementation/OrderPlannerService' import { diff --git a/sdk/sdk-client/package.json b/sdk/sdk-client/package.json index c39c443f3f..a77f8b0edb 100644 --- a/sdk/sdk-client/package.json +++ b/sdk/sdk-client/package.json @@ -27,6 +27,7 @@ "devDependencies": { "@summerfi/eslint-config": "workspace:*", "@summerfi/jest-config": "workspace:*", - "@summerfi/typescript-config": "workspace:*" + "@summerfi/typescript-config": "workspace:*", + "@summerfi/testing-utils": "workspace:*" } } diff --git a/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts b/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts index c81f61c734..478b6e7ddb 100644 --- a/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts +++ b/sdk/sdk-client/src/implementation/simulations/RefinanceSimulationManager.ts @@ -12,20 +12,25 @@ export class RefinanceSimulationManager extends IRPCClient { params: IRefinanceParameters, ): Promise> { const refinanceParameters: IRefinanceParameters = { - position: { - positionId: params.position.positionId, - debtAmount: params.position.debtAmount, - collateralAmount: params.position.collateralAmount, + sourcePosition: { + positionId: params.sourcePosition.positionId, + debtAmount: params.sourcePosition.debtAmount, + collateralAmount: params.sourcePosition.collateralAmount, pool: { - poolId: params.position.pool.poolId, - protocol: params.position.pool.protocol, - type: params.position.pool.type, + poolId: params.sourcePosition.pool.poolId, + protocol: params.sourcePosition.pool.protocol, + type: params.sourcePosition.pool.type, }, }, - targetPool: { - poolId: params.targetPool.poolId, - protocol: params.targetPool.protocol, - type: params.targetPool.type, + targetPosition: { + positionId: params.targetPosition.positionId, + debtAmount: params.targetPosition.debtAmount, + collateralAmount: params.targetPosition.collateralAmount, + pool: { + poolId: params.targetPosition.pool.poolId, + protocol: params.targetPosition.pool.protocol, + type: params.targetPosition.pool.type, + }, }, slippage: params.slippage, } diff --git a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts index 5309f0dd8a..fc887e4a54 100644 --- a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts +++ b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts @@ -1,8 +1,13 @@ -import { IProtocol, PoolType, ProtocolName } from '@summerfi/sdk-common/protocols' +import { + CollateralConfigMap, + DebtConfigMap, + IProtocol, + PoolType, + ProtocolName, +} from '@summerfi/sdk-common/protocols' import { SDKManager } from '../../src/implementation/SDKManager' import { RPCClientType } from '../../src/rpc/SDKClient' import { MakerLendingPool } from '@summerfi/protocol-plugins/plugins/maker' -import { SparkLendingPool } from '@summerfi/protocol-plugins/plugins/spark' import { ISimulation, SimulationType } from '@summerfi/sdk-common/simulation' import { Address, @@ -16,20 +21,21 @@ import { TokenAmount, } from '@summerfi/sdk-common/common' import { IRefinanceParameters } from '@summerfi/sdk-common/orders' +import { CollateralConfigMock, DebtConfigMock } from '@summerfi/testing-utils' export default async function simulateRefinanceTest() { type SimulateRefinanceType = RPCClientType['simulation']['refinance']['query'] const simulateRefinance: SimulateRefinanceType = jest.fn(async (params) => { return { simulationType: SimulationType.Refinance, - sourcePosition: params.position, - swaps: [], + sourcePosition: params.sourcePosition, targetPosition: { positionId: PositionId.createFrom({ id: '0987654321' }), - debtAmount: params.position.debtAmount, - collateralAmount: params.position.collateralAmount, - pool: params.targetPool, + debtAmount: params.targetPosition.debtAmount, + collateralAmount: params.targetPosition.collateralAmount, + pool: params.targetPosition.pool, }, + swaps: [], steps: [], } as ISimulation }) @@ -92,23 +98,42 @@ export default async function simulateRefinanceTest() { }), } - const targetPool: SparkLendingPool = { - type: PoolType.Lending, + const targetPool = { + type: PoolType.Lending as const, protocol: protocol, poolId: { protocol: protocol, }, - collaterals: {}, - debts: {}, + collaterals: CollateralConfigMap.createFrom({ + record: { + [prevPosition.collateralAmount.token.address.value]: new CollateralConfigMock({}), + }, + }), + debts: DebtConfigMap.createFrom({ + record: { + [prevPosition.debtAmount.token.address.value]: new DebtConfigMock({}), + }, + }), baseCurrency: DAI, - } as SparkLendingPool + } + const targetPosition = new Position({ + positionId: { + id: 'newEmptyPositionFromPool', + }, + debtAmount: TokenAmount.createFrom({ + token: prevPosition.debtAmount.token, + amount: prevPosition.debtAmount.amount, + }), + collateralAmount: TokenAmount.createFrom({ + token: prevPosition.collateralAmount.token, + amount: prevPosition.collateralAmount.amount, + }), + pool: targetPool, + }) const refinanceParameters: IRefinanceParameters = { - position: prevPosition, - targetPool: targetPool, - // Assume no swaps - targetDebt: prevPosition.debtAmount.token, - targetCollateral: prevPosition.collateralAmount.token, + sourcePosition: prevPosition, + targetPosition: targetPosition, slippage: Percentage.createFrom({ value: 0.5 }), } @@ -121,8 +146,8 @@ export default async function simulateRefinanceTest() { expect(simulation.sourcePosition?.positionId).toBe(prevPosition.positionId) expect(simulation.targetPosition).toBeDefined() expect(simulation.targetPosition.positionId).toBeDefined() - expect(simulation.targetPosition.debtAmount).toBe(prevPosition.debtAmount) - expect(simulation.targetPosition.collateralAmount).toBe(prevPosition.collateralAmount) + // expect(simulation.targetPosition.debtAmount).toBe(prevPosition.debtAmount) + // expect(simulation.targetPosition.collateralAmount).toBe(prevPosition.collateralAmount) expect(simulation.targetPosition.pool.poolId).toBe(targetPool.poolId) expect(simulation.steps).toBeDefined() } diff --git a/sdk/sdk-client/tsconfig.test.json b/sdk/sdk-client/tsconfig.test.json index 740ffdc0d0..26fcc74db0 100644 --- a/sdk/sdk-client/tsconfig.test.json +++ b/sdk/sdk-client/tsconfig.test.json @@ -7,7 +7,8 @@ "@summerfi/sdk-common/*": ["node_modules/@summerfi/sdk-common/src/*"], "@summerfi/sdk-server/*": ["node_modules/@summerfi/sdk-server/src/*"], "@summerfi/protocol-plugins/*": ["../protocol-plugins/src/*"], - "@summerfi/protocol-plugins-common": ["../protocol-plugins-common/src/index.ts"] + "@summerfi/protocol-plugins-common": ["../protocol-plugins-common/src/index.ts"], + "@summerfi/testing-utils": ["node_modules/@summerfi/testing-utils/src"] } }, "include": ["src/**/*.ts"], diff --git a/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts b/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts index 9fd4a2556d..74e1e47c41 100644 --- a/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts +++ b/sdk/sdk-common/src/orders/interfaces/refinance/IRefinanceParameters.ts @@ -1,13 +1,9 @@ -import { IToken } from '../../../common/interfaces/IToken' import { IPercentage } from '../../../common/interfaces/IPercentage' import { IPosition } from '../../../common/interfaces/IPosition' -import { IPool } from '../../../protocols/interfaces/IPool' export interface IRefinanceParameters { - position: IPosition - targetPool: IPool - targetCollateral: IToken - targetDebt: IToken + sourcePosition: IPosition + targetPosition: IPosition slippage: IPercentage } diff --git a/sdk/sdk-e2e/tests/refinance.test.ts b/sdk/sdk-e2e/tests/refinance.test.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts b/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts index 7b9e3a673d..87cbe97501 100644 --- a/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts +++ b/sdk/sdk-e2e/tests/refinanceMakerSpark.test.ts @@ -8,6 +8,7 @@ import { type Maybe, ChainFamilyMap, AddressValue, + newEmptyPositionFromPool, } from '@summerfi/sdk-common/common' import { ProtocolName, isLendingPool } from '@summerfi/sdk-common/protocols' @@ -162,12 +163,15 @@ describe.skip('Refinance Maker Spark | SDK', () => { assert(false, 'Spark pool type is not lending') } + const emptyTargetPosition = newEmptyPositionFromPool( + sparkPool, + makerPosition.debtAmount.token, + makerPosition.collateralAmount.token, + ) const refinanceSimulation: ISimulation = await sdk.simulator.refinance.simulateRefinancePosition({ - position: makerPosition, - targetPool: sparkPool, - targetCollateral: makerPosition.collateralAmount.token, - targetDebt: makerPosition.debtAmount.token, + sourcePosition: makerPosition, + targetPosition: emptyTargetPosition, slippage: Percentage.createFrom({ value: 0.2 }), } as IRefinanceParameters) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index 13a8744bab..c65b040271 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -1,5 +1,4 @@ import { TokenAmount, Price, Percentage } from '@summerfi/sdk-common/common' -import { applyPercentage } from '@summerfi/sdk-common/utils' import { steps } from '@summerfi/sdk-common/simulation' import { addBalance, subtractBalance } from '../../utils' import { ISimulationState } from '../../../interfaces/simulation' @@ -22,7 +21,9 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim }) const spotPrice = step.inputs.spotPrice - const fromAmountPreSummerFee = applyPercentage(step.inputs.fromTokenAmount, step.inputs.summerFee) + const fromAmountPreSummerFee = step.inputs.fromTokenAmount.divide( + Percentage.createFrom({ value: 1 }).subtract(step.inputs.summerFee), + ) return { ...state, diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index d368bf9f8e..5403db5f85 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -20,12 +20,12 @@ export async function refinanceLendingToLending( dependencies: IRefinanceDependencies, ): Promise> { // args validation - if (!isLendingPool(args.targetPool)) { + if (!isLendingPool(args.targetPosition.pool)) { throw new Error('Target pool is not a lending pool') } - const position = Position.createFrom(args.position) - const targetPool = await dependencies.protocolManager.getPool(args.targetPool.poolId) + const position = Position.createFrom(args.sourcePosition) + const targetPool = await dependencies.protocolManager.getPool(args.targetPosition.pool.poolId) if (!isLendingPool(targetPool)) { throw new Error('Target pool is not a lending pool') @@ -35,8 +35,10 @@ export async function refinanceLendingToLending( const flashloanAmount = position.debtAmount.multiply(FLASHLOAN_MARGIN) const simulator = Simulator.create(refinanceLendingToLendingStrategy) - const targetCollateralConfig = targetPool.collaterals.get({ token: args.targetCollateral }) - const targetDebtConfig = targetPool.debts.get({ token: args.targetDebt }) + const targetCollateralConfig = targetPool.collaterals.get({ + token: args.targetPosition.collateralAmount.token, + }) + const targetDebtConfig = targetPool.debts.get({ token: args.targetPosition.debtAmount.token }) if (!targetCollateralConfig || !targetDebtConfig) { throw new Error('Target token config not found in pool') } @@ -142,7 +144,7 @@ export async function refinanceLendingToLending( type: SimulationSteps.Swap, inputs: { ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: args.position.pool.protocol.chainInfo, + chainInfo: args.sourcePosition.pool.protocol.chainInfo, fromAmount: subtractPercentage( getReferencedValue(ctx.getReference(['DepositBorrowToTarget', 'borrowAmount'])), Percentage.createFrom({ diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index bf8ec3db64..0493b1cf01 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -1,6 +1,7 @@ import { Percentage } from '@summerfi/sdk-common/common' import { ISimulation, SimulationSteps, SimulationType } from '@summerfi/sdk-common/simulation' import { refinanceLendingToLending } from '../src/strategies' +import { newEmptyPositionFromPool } from '@summerfi/sdk-common/common' import { otherTestCollateral, otherTestDebt, @@ -16,14 +17,16 @@ describe('Refinance', () => { beforeAll(async () => { simulation = await refinanceLendingToLending( { - position: testSourcePosition, - targetPool: testTargetLendingPool, - targetDebt: testTargetLendingPool.debts.get({ - token: testSourcePosition.debtAmount.token, - })!.token, - targetCollateral: testTargetLendingPool.collaterals.get({ - token: testSourcePosition.collateralAmount.token, - })!.token, + sourcePosition: testSourcePosition, + targetPosition: newEmptyPositionFromPool( + testTargetLendingPool, + testTargetLendingPool.debts.get({ + token: testSourcePosition.debtAmount.token, + })!.token, + testTargetLendingPool.collaterals.get({ + token: testSourcePosition.collateralAmount.token, + })!.token, + ), slippage: Percentage.createFrom({ value: 1 }), }, mockRefinanceContext, @@ -67,10 +70,12 @@ describe('Refinance', () => { beforeAll(async () => { simulation = await refinanceLendingToLending( { - position: testSourcePosition, - targetPool: testTargetLendingPoolRequiredSwaps, - targetDebt: otherTestDebt, - targetCollateral: otherTestCollateral, + sourcePosition: testSourcePosition, + targetPosition: newEmptyPositionFromPool( + testTargetLendingPoolRequiredSwaps, + otherTestDebt, + otherTestCollateral, + ), slippage: Percentage.createFrom({ value: 1 }), }, mockRefinanceContextRequiredSwaps, diff --git a/sdk/testing-utils/src/mocks/CollateralConfigMock.ts b/sdk/testing-utils/src/mocks/CollateralConfigMock.ts new file mode 100644 index 0000000000..c701d11c30 --- /dev/null +++ b/sdk/testing-utils/src/mocks/CollateralConfigMock.ts @@ -0,0 +1,80 @@ +import { + CurrencySymbol, + Token, + Price, + RiskRatio, + TokenAmount, + ChainFamilyMap, + ChainInfo, + Percentage, + Address, +} from '@summerfi/sdk-common/common' +import { ICollateralConfig } from '@summerfi/sdk-common/protocols' + +const chainInfo: ChainInfo = ChainFamilyMap.Ethereum.Mainnet + +// TODO: Add getters/setters as required +export class CollateralConfigMock implements ICollateralConfig { + token: Token + price: Price + priceUSD: Price + liquidationThreshold: RiskRatio + maxSupply: TokenAmount + tokensLocked: TokenAmount + liquidationPenalty: Percentage + + constructor(params: Partial) { + const DAI = Token.createFrom({ + chainInfo, + address: Address.createFromEthereum({ value: '0x6B175474E89094C44Da98b954EedeAC495271d0F' }), + symbol: 'DAI', + name: 'Dai Stablecoin', + decimals: 18, + }) + const token = params.token ?? { + chainInfo, + address: Address.createFromEthereum({ value: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' }), + symbol: 'WETH', + name: 'Wrapped Ether', + decimals: 18, + } + this.token = Token.createFrom(token) + this.price = params.price + ? Price.createFrom(params.price) + : Price.createFrom({ + baseToken: this.token, + quoteToken: DAI, + value: '0', + }) + this.priceUSD = Price.createFrom({ + baseToken: this.token, + quoteToken: CurrencySymbol.USD, + value: '0', + }) + + const liquidationThreshold = params.liquidationThreshold ?? { + type: RiskRatio.type.LTV, + ratio: Percentage.createFrom({ + value: 0, + }), + } + this.liquidationThreshold = RiskRatio.createFrom(liquidationThreshold) + + const maxSupply = params.maxSupply ?? { + token: this.token, + amount: '0', + } + this.maxSupply = TokenAmount.createFrom(maxSupply) + + const tokensLocked = params.tokensLocked ?? { + token: this.token, + amount: '0', + } + this.tokensLocked = TokenAmount.createFrom(tokensLocked) + + const liquidationPenalty = params.liquidationPenalty ?? { + value: 0, + } + this.liquidationPenalty = Percentage.createFrom(liquidationPenalty) + } +} diff --git a/sdk/testing-utils/src/mocks/DebtConfigMock.ts b/sdk/testing-utils/src/mocks/DebtConfigMock.ts new file mode 100644 index 0000000000..91bedebc6c --- /dev/null +++ b/sdk/testing-utils/src/mocks/DebtConfigMock.ts @@ -0,0 +1,83 @@ +import { + CurrencySymbol, + Token, + Price, + TokenAmount, + ChainFamilyMap, + ChainInfo, + Percentage, + Address, +} from '@summerfi/sdk-common/common' +import { IDebtConfig } from '@summerfi/sdk-common/protocols' + +const chainInfo: ChainInfo = ChainFamilyMap.Ethereum.Mainnet + +// TODO: Add getters/setters as required +export class DebtConfigMock implements IDebtConfig { + token: Token + price: Price + priceUSD: Price + rate: Percentage + totalBorrowed: TokenAmount + debtCeiling: TokenAmount + debtAvailable: TokenAmount + dustLimit: TokenAmount + originationFee: Percentage + + constructor(params: Partial) { + const token = params.token ?? { + chainInfo, + address: Address.createFromEthereum({ value: '0x6B175474E89094C44Da98b954EedeAC495271d0F' }), + symbol: 'DAI', + name: 'Dai Stablecoin', + decimals: 18, + } + this.token = Token.createFrom(token) + this.price = params.price + ? Price.createFrom(params.price) + : Price.createFrom({ + baseToken: this.token, + quoteToken: CurrencySymbol.USD, + value: '0', + }) + this.priceUSD = Price.createFrom({ + baseToken: this.token, + quoteToken: CurrencySymbol.USD, + value: '0', + }) + + const rate = params.rate ?? { + value: 0, + } + this.rate = Percentage.createFrom(rate) + + const totalBorrowed = params.totalBorrowed ?? { + token: this.token, + amount: '0', + } + this.totalBorrowed = TokenAmount.createFrom(totalBorrowed) + + const debtCeiling = params.debtCeiling ?? { + token: this.token, + amount: '0', + } + this.debtCeiling = TokenAmount.createFrom(debtCeiling) + + const debtAvailable = params.debtAvailable ?? { + token: this.token, + amount: '0', + } + this.debtAvailable = TokenAmount.createFrom(debtAvailable) + + const dustLimit = params.dustLimit ?? { + token: this.token, + amount: '0', + } + this.dustLimit = TokenAmount.createFrom(dustLimit) + + const originationFee = params.originationFee ?? { + value: 0, + } + this.originationFee = Percentage.createFrom(originationFee) + } +} diff --git a/sdk/testing-utils/src/mocks/SwapManagerMock.ts b/sdk/testing-utils/src/mocks/SwapManagerMock.ts index 6592d70d19..29a59c8291 100644 --- a/sdk/testing-utils/src/mocks/SwapManagerMock.ts +++ b/sdk/testing-utils/src/mocks/SwapManagerMock.ts @@ -1,19 +1,12 @@ -import { - Address, - ChainInfo, - Percentage, - Token, - TokenAmount, - CurrencySymbol, -} from '@summerfi/sdk-common/common' +import { Address, ChainInfo, Percentage, Token, TokenAmount } from '@summerfi/sdk-common/common' import { SpotData, SwapData, QuoteData } from '@summerfi/sdk-common/swap' import { ISwapManager } from '@summerfi/swap-common/interfaces' export class SwapManagerMock implements ISwapManager { private _swapDataReturnValue: SwapData = {} as SwapData private _quoteDataReturnValue: QuoteData = {} as QuoteData - - private _spotPricesReturnValue: SpotData = {} as SpotData + private _spotDataReturnValue: SpotData = {} as SpotData + private _summerFeeValue: Percentage = Percentage.createFrom({ value: 0 }) private _lastGetSwapDataExactInputParams: | { @@ -42,12 +35,12 @@ export class SwapManagerMock implements ISwapManager { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getSpotPrices(params: { - chainInfo: ChainInfo - tokens: Token[] - quoteCurrency?: CurrencySymbol - }): Promise { - return this._spotPricesReturnValue + async getSpotPrice(params: { chainInfo: ChainInfo; baseToken: Token }): Promise { + return this._spotDataReturnValue + } + + getSummerFee(): Percentage { + return this._summerFeeValue } async getSwapDataExactInput(params: { diff --git a/sdk/testing-utils/src/mocks/index.ts b/sdk/testing-utils/src/mocks/index.ts index c51bfc12f1..8c18941024 100644 --- a/sdk/testing-utils/src/mocks/index.ts +++ b/sdk/testing-utils/src/mocks/index.ts @@ -1,3 +1,5 @@ export { StepBuilderContextMock } from './StepBuilderContextMock' export { SwapManagerMock } from './SwapManagerMock' export { UserMock } from './UserMock' +export { CollateralConfigMock } from './CollateralConfigMock' +export { DebtConfigMock } from './DebtConfigMock' From f2b43c4e9c743cfd758d90b38a1f65316259dd04 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Wed, 3 Apr 2024 13:27:04 +0100 Subject: [PATCH 20/26] refactor: strip fromBaseUnit methods --- .../src/common/implementation/TokenAmount.ts | 22 +++++++++---------- .../simulator-engine/reducer/swapReducer.ts | 5 +---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/sdk/sdk-common/src/common/implementation/TokenAmount.ts b/sdk/sdk-common/src/common/implementation/TokenAmount.ts index e7ff6bcbbe..e56a3a989d 100644 --- a/sdk/sdk-common/src/common/implementation/TokenAmount.ts +++ b/sdk/sdk-common/src/common/implementation/TokenAmount.ts @@ -94,17 +94,17 @@ export class TokenAmount implements ITokenAmount { return new BigNumber(this.amount).times(this._baseUnitFactor).toFixed(0) } - public toBaseUnitAsBn(): BigNumber { - return new BigNumber(this.amount).times(this._baseUnitFactor) - } - - public fromBaseUnit(): string { - return new BigNumber(this.amount).div(this._baseUnitFactor).toFixed(0) - } - - public fromBaseUnitAsBn(): BigNumber { - return new BigNumber(this.amount).div(this._baseUnitFactor) - } + // public toBaseUnitAsBn(): BigNumber { + // return new BigNumber(this.amount).times(this._baseUnitFactor) + // } + + // public fromBaseUnit(): string { + // return new BigNumber(this.amount).div(this._baseUnitFactor).toFixed(0) + // } + // + // public fromBaseUnitAsBn(): BigNumber { + // return new BigNumber(this.amount).div(this._baseUnitFactor) + // } public toBN(): BigNumber { return new BigNumber(this.amount) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts index c65b040271..ea8c38eb95 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/reducer/swapReducer.ts @@ -12,10 +12,7 @@ export function swapReducer(step: steps.SwapStep, state: ISimulationState): ISim // We require both from & to be at similar decimal precisions const offerPrice = Price.createFrom({ - value: step.inputs.toTokenAmount - .fromBaseUnitAsBn() - .div(step.inputs.fromTokenAmount.fromBaseUnitAsBn()) - .toString(), + value: step.inputs.toTokenAmount.divide(step.inputs.fromTokenAmount.amount).toString(), baseToken, quoteToken, }) From d9992345c47fd1458e00e72e963b559c94f5589b Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 4 Apr 2024 09:41:25 +0100 Subject: [PATCH 21/26] fix: PR comments --- .../tests/queries/simulateRefinance.subtest.ts | 14 +++----------- sdk/simulator-service/src/implementation/index.ts | 0 2 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 sdk/simulator-service/src/implementation/index.ts diff --git a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts index fc887e4a54..3f67cc7486 100644 --- a/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts +++ b/sdk/sdk-client/tests/queries/simulateRefinance.subtest.ts @@ -117,18 +117,12 @@ export default async function simulateRefinanceTest() { baseCurrency: DAI, } - const targetPosition = new Position({ + const targetPosition = Position.createFrom({ positionId: { id: 'newEmptyPositionFromPool', }, - debtAmount: TokenAmount.createFrom({ - token: prevPosition.debtAmount.token, - amount: prevPosition.debtAmount.amount, - }), - collateralAmount: TokenAmount.createFrom({ - token: prevPosition.collateralAmount.token, - amount: prevPosition.collateralAmount.amount, - }), + debtAmount: prevPosition.debtAmount, + collateralAmount: prevPosition.collateralAmount, pool: targetPool, }) const refinanceParameters: IRefinanceParameters = { @@ -146,8 +140,6 @@ export default async function simulateRefinanceTest() { expect(simulation.sourcePosition?.positionId).toBe(prevPosition.positionId) expect(simulation.targetPosition).toBeDefined() expect(simulation.targetPosition.positionId).toBeDefined() - // expect(simulation.targetPosition.debtAmount).toBe(prevPosition.debtAmount) - // expect(simulation.targetPosition.collateralAmount).toBe(prevPosition.collateralAmount) expect(simulation.targetPosition.pool.poolId).toBe(targetPool.poolId) expect(simulation.steps).toBeDefined() } diff --git a/sdk/simulator-service/src/implementation/index.ts b/sdk/simulator-service/src/implementation/index.ts deleted file mode 100644 index e69de29bb2..0000000000 From fdac1f1664873ff83c1d148ca024cd1401be9563 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 4 Apr 2024 10:53:55 +0100 Subject: [PATCH 22/26] refactor: add skip as second arg to avoid running steps that are not required --- sdk/protocol-plugins/tests/tests.spec.ts | 73 ------------------- .../simulator-engine/simulator.ts | 26 ++++--- .../refinance/RefinanceLendingToLending.ts | 9 +-- 3 files changed, 20 insertions(+), 88 deletions(-) delete mode 100644 sdk/protocol-plugins/tests/tests.spec.ts diff --git a/sdk/protocol-plugins/tests/tests.spec.ts b/sdk/protocol-plugins/tests/tests.spec.ts deleted file mode 100644 index 4363dc7ca0..0000000000 --- a/sdk/protocol-plugins/tests/tests.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ProtocolName } from '@summerfi/sdk-common/protocols' -import { ChainInfo } from '@summerfi/sdk-common/common' -import { createPublicClient, http, PublicClient } from 'viem' -import { mainnet } from 'viem/chains' - -import { MockContractProvider } from '../src/mocks/mockContractProvider' -import { TokenService, PriceService } from '../src/implementation' -import { IProtocolPluginContext } from '@summerfi/protocol-plugins-common' -import { ILKType } from '../src/plugins/maker/enums/ILKType' -import { EmodeType } from '../src/plugins/common/enums/EmodeType' -import { MakerProtocolPlugin } from '../src/plugins/maker/implementation/MakerProtocolPlugin' -import { SparkProtocolPlugin } from '../src/plugins/spark/implementation/SparkProtocolPlugin' -import { AaveV3ProtocolPlugin } from '../src/plugins/aave-v3/implementation/AAVEv3ProtocolPlugin' - -async function createProtocolPluginContext(): Promise { - const RPC_URL = process.env['MAINNET_RPC_URL'] || '' - const provider: PublicClient = createPublicClient({ - batch: { - multicall: true, - }, - chain: mainnet, - transport: http(RPC_URL), - }) - - return { - provider, - tokenService: new TokenService(), - priceService: new PriceService(provider), - contractProvider: new MockContractProvider(), - } -} - -// TODO: re-enable with separate Ci workflow and http transport properly configured -describe.skip('playground', () => { - let ctx: IProtocolPluginContext - beforeAll(async () => { - ctx = await createProtocolPluginContext() - }) - - it('template/maker', async () => { - const makerProtocolPlugin = new MakerProtocolPlugin({ context: ctx }) - await makerProtocolPlugin.getPool({ - protocol: { - name: ProtocolName.Maker, - chainInfo: ChainInfo.createFrom({ chainId: 1, name: 'Ethereum' }), - }, - ilkType: ILKType.ETH_A, - vaultId: '123', - }) - }) - - it('template/spark', async () => { - const sparkProtocolPlugin = new SparkProtocolPlugin({ context: ctx }) - await sparkProtocolPlugin.getPool({ - protocol: { - name: ProtocolName.Spark, - chainInfo: ChainInfo.createFrom({ chainId: 1, name: 'Ethereum' }), - }, - emodeType: EmodeType.None, - }) - }) - - it('template/aave-v3', async () => { - const aaveV3ProtocolPlugin = new AaveV3ProtocolPlugin({ context: ctx }) - await aaveV3ProtocolPlugin.getPool({ - protocol: { - name: ProtocolName.AAVEv3, - chainInfo: ChainInfo.createFrom({ chainId: 1, name: 'Ethereum' }), - }, - emodeType: EmodeType.None, - }) - }) -}) diff --git a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts index 66b1022536..3f18c27689 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts @@ -71,14 +71,8 @@ export class Simulator< const nextStep = await this.nextArray[i]({ state: this.state, getReference }) - if (nextStep.skip === false || nextStep.skip === undefined) { - const fullStep = await processStepOutput(nextStep) - this.state = stateReducer(fullStep, this.state) - } - - if (nextStep.skip === true && processedStepSchema.optional === false) { - throw new Error(`Step is required: ${nextStep.type}`) - } + const fullStep = await processStepOutput(nextStep) + this.state = stateReducer(fullStep, this.state) } return this.state @@ -86,11 +80,25 @@ export class Simulator< public next( next: NextFunction, - ): Simulator, [...NextArray, NextFunction]> { + skip?: boolean + ): Simulator, [...NextArray]> | Simulator, [...NextArray, NextFunction]> { const schemaHead = head(this.schema) const schemaTail = tail(this.schema) const nextArray = [...this.nextArray, next] as const + if (skip) { + if (schemaHead.optional === false) { + throw new Error(`Step is required: ${schemaHead.step}`) + } + + return new Simulator, [...NextArray]>( + schemaTail, + this.originalSchema, + this.state, + this.nextArray, + ) + } + if (!schemaHead) { throw new Error('No more steps to process') } diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index 5403db5f85..e7404f2e6c 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -113,8 +113,7 @@ export async function refinanceLendingToLending( slippage: Percentage.createFrom({ value: args.slippage.value }), summerFee: collateralSwapSummerFee, }, - skip: isCollateralSwapSkipped, - })) + }), isCollateralSwapSkipped) .next(async (ctx) => ({ name: 'DepositBorrowToTarget', type: SimulationSteps.DepositBorrow, @@ -157,8 +156,7 @@ export async function refinanceLendingToLending( slippage: Percentage.createFrom({ value: args.slippage.value }), summerFee: debtSwapSummerFee, }, - skip: isDebtSwapSkipped, - })) + }), isDebtSwapSkipped) .next(async () => ({ name: 'RepayFlashloan', type: SimulationSteps.RepayFlashloan, @@ -176,8 +174,7 @@ export async function refinanceLendingToLending( * */ token: position.debtAmount.token, }, - skip: isDebtSwapSkipped, - })) + }), isDebtSwapSkipped) .run() const targetPosition = Object.values(simulation.positions).find( From 309be54705f431ab4fbd22a7fc3732970d2dcf79 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Thu, 4 Apr 2024 13:04:21 +0100 Subject: [PATCH 23/26] refactor: add v6 from 1inch provider --- .github/workflows/build-integration-test.yaml | 2 + .github/workflows/build-unit-test.yaml | 2 + .../implementation/ConfigurationProvider.ts | 4 +- .../simulator-engine/simulator.ts | 15 +-- .../refinance/RefinanceLendingToLending.ts | 117 ++++++++++-------- .../tests/mocks/contextMock.ts | 2 +- .../src/implementation/SwapManagerFactory.ts | 20 ++- .../oneinch/OneInchSwapProvider.ts | 55 ++++++-- .../src/implementation/oneinch/types.ts | 7 +- 9 files changed, 142 insertions(+), 82 deletions(-) diff --git a/.github/workflows/build-integration-test.yaml b/.github/workflows/build-integration-test.yaml index cc6813db86..33908c769f 100644 --- a/.github/workflows/build-integration-test.yaml +++ b/.github/workflows/build-integration-test.yaml @@ -45,8 +45,10 @@ jobs: run: pnpm test:integration env: ONE_INCH_API_KEY: ${{ secrets.ONE_INCH_API_KEY }} + ONE_INCH_API_V6_KEY: ${{ secrets.ONE_INCH_API_V6_KEY }} ONE_INCH_API_VERSION: ${{ secrets.ONE_INCH_API_VERSION }} ONE_INCH_API_URL: ${{ secrets.ONE_INCH_API_URL }} + ONE_INCH_API_V6_URL: ${{ secrets.ONE_INCH_API_V6_URL }} ONE_INCH_ALLOWED_SWAP_PROTOCOLS: ${{ secrets.ONE_INCH_ALLOWED_SWAP_PROTOCOLS }} ONE_INCH_SWAP_CHAIN_IDS: ${{ secrets.ONE_INCH_SWAP_CHAIN_IDS }} MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} diff --git a/.github/workflows/build-unit-test.yaml b/.github/workflows/build-unit-test.yaml index 12c5a9919d..5108ddc1cc 100644 --- a/.github/workflows/build-unit-test.yaml +++ b/.github/workflows/build-unit-test.yaml @@ -45,8 +45,10 @@ jobs: run: pnpm test env: ONE_INCH_API_KEY: ${{ secrets.ONE_INCH_API_KEY }} + ONE_INCH_API_V6_KEY: ${{ secrets.ONE_INCH_API_V6_KEY }} ONE_INCH_API_VERSION: ${{ secrets.ONE_INCH_API_VERSION }} ONE_INCH_API_URL: ${{ secrets.ONE_INCH_API_URL }} + ONE_INCH_API_V6_URL: ${{ secrets.ONE_INCH_API_V6_URL }} ONE_INCH_ALLOWED_SWAP_PROTOCOLS: ${{ secrets.ONE_INCH_ALLOWED_SWAP_PROTOCOLS }} ONE_INCH_SWAP_CHAIN_IDS: ${{ secrets.ONE_INCH_SWAP_CHAIN_IDS }} MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} diff --git a/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts b/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts index 7470dc75c8..7170731c83 100644 --- a/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts +++ b/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts @@ -4,9 +4,11 @@ import { ConfigItem } from '../types/ConfigItem' import { ConfigKey } from '../types/ConfigKey' const CONFIG_KEYS: ConfigKey[] = [ - 'ONE_INCH_API_KEY', 'ONE_INCH_API_VERSION', + 'ONE_INCH_API_KEY', 'ONE_INCH_API_URL', + 'ONE_INCH_API_V6_KEY', + 'ONE_INCH_API_V6_URL', 'ONE_INCH_ALLOWED_SWAP_PROTOCOLS', 'ONE_INCH_SWAP_CHAIN_IDS', ] diff --git a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts index 3f18c27689..7bc78377a8 100644 --- a/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts +++ b/sdk/simulator-service/src/implementation/simulator-engine/simulator.ts @@ -37,7 +37,6 @@ export class Simulator< public async run(): Promise { for (let i = 0; i < this.nextArray.length; i++) { - const processedStepSchema = this.originalSchema[i] const getReference = (path: [string, string]) => { const [stepName, output] = path const step: Maybe = this.state.steps[stepName] @@ -80,8 +79,10 @@ export class Simulator< public next( next: NextFunction, - skip?: boolean - ): Simulator, [...NextArray]> | Simulator, [...NextArray, NextFunction]> { + skip?: boolean, + ): + | Simulator, [...NextArray]> + | Simulator, [...NextArray, NextFunction]> { const schemaHead = head(this.schema) const schemaTail = tail(this.schema) const nextArray = [...this.nextArray, next] as const @@ -92,10 +93,10 @@ export class Simulator< } return new Simulator, [...NextArray]>( - schemaTail, - this.originalSchema, - this.state, - this.nextArray, + schemaTail, + this.originalSchema, + this.state, + this.nextArray, ) } diff --git a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts index e7404f2e6c..83df937175 100644 --- a/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts +++ b/sdk/simulator-service/src/strategies/refinance/RefinanceLendingToLending.ts @@ -89,31 +89,34 @@ export async function refinanceLendingToLending( position: position, }, })) - .next(async () => ({ - name: 'CollateralSwap', - type: SimulationSteps.Swap, - inputs: { - ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: position.pool.protocol.chainInfo, - fromAmount: subtractPercentage( - position.collateralAmount, - Percentage.createFrom({ - value: collateralSwapSummerFee.value, - }), - ), - toToken: targetCollateralConfig.token, - })), - spotPrice: ( - await dependencies.swapManager.getSpotPrice({ + .next( + async () => ({ + name: 'CollateralSwap', + type: SimulationSteps.Swap, + inputs: { + ...(await dependencies.swapManager.getSwapQuoteExactInput({ chainInfo: position.pool.protocol.chainInfo, - baseToken: targetCollateralConfig.token, - quoteToken: position.collateralAmount.token, - }) - ).price, - slippage: Percentage.createFrom({ value: args.slippage.value }), - summerFee: collateralSwapSummerFee, - }, - }), isCollateralSwapSkipped) + fromAmount: subtractPercentage( + position.collateralAmount, + Percentage.createFrom({ + value: collateralSwapSummerFee.value, + }), + ), + toToken: targetCollateralConfig.token, + })), + spotPrice: ( + await dependencies.swapManager.getSpotPrice({ + chainInfo: position.pool.protocol.chainInfo, + baseToken: targetCollateralConfig.token, + quoteToken: position.collateralAmount.token, + }) + ).price, + slippage: Percentage.createFrom({ value: args.slippage.value }), + summerFee: collateralSwapSummerFee, + }, + }), + isCollateralSwapSkipped, + ) .next(async (ctx) => ({ name: 'DepositBorrowToTarget', type: SimulationSteps.DepositBorrow, @@ -138,25 +141,28 @@ export async function refinanceLendingToLending( borrowTargetType: TokenTransferTargetType.PositionsManager, }, })) - .next(async (ctx) => ({ - name: 'DebtSwap', - type: SimulationSteps.Swap, - inputs: { - ...(await dependencies.swapManager.getSwapQuoteExactInput({ - chainInfo: args.sourcePosition.pool.protocol.chainInfo, - fromAmount: subtractPercentage( - getReferencedValue(ctx.getReference(['DepositBorrowToTarget', 'borrowAmount'])), - Percentage.createFrom({ - value: debtSwapSummerFee.value, - }), - ), - toToken: targetDebtConfig.token, - })), - spotPrice: debtSpotPrice, - slippage: Percentage.createFrom({ value: args.slippage.value }), - summerFee: debtSwapSummerFee, - }, - }), isDebtSwapSkipped) + .next( + async (ctx) => ({ + name: 'DebtSwap', + type: SimulationSteps.Swap, + inputs: { + ...(await dependencies.swapManager.getSwapQuoteExactInput({ + chainInfo: args.sourcePosition.pool.protocol.chainInfo, + fromAmount: subtractPercentage( + getReferencedValue(ctx.getReference(['DepositBorrowToTarget', 'borrowAmount'])), + Percentage.createFrom({ + value: debtSwapSummerFee.value, + }), + ), + toToken: targetDebtConfig.token, + })), + spotPrice: debtSpotPrice, + slippage: Percentage.createFrom({ value: args.slippage.value }), + summerFee: debtSwapSummerFee, + }, + }), + isDebtSwapSkipped, + ) .next(async () => ({ name: 'RepayFlashloan', type: SimulationSteps.RepayFlashloan, @@ -164,17 +170,20 @@ export async function refinanceLendingToLending( amount: flashloanAmount, }, })) - .next(async () => ({ - name: 'ReturnFunds', - type: SimulationSteps.ReturnFunds, - inputs: { - /* - * We swap back to the original position's debt in order to repay the flashloan. - * Therefore, the dust amount will be in the original position's debt - * */ - token: position.debtAmount.token, - }, - }), isDebtSwapSkipped) + .next( + async () => ({ + name: 'ReturnFunds', + type: SimulationSteps.ReturnFunds, + inputs: { + /* + * We swap back to the original position's debt in order to repay the flashloan. + * Therefore, the dust amount will be in the original position's debt + * */ + token: position.debtAmount.token, + }, + }), + isDebtSwapSkipped, + ) .run() const targetPosition = Object.values(simulation.positions).find( diff --git a/sdk/simulator-service/tests/mocks/contextMock.ts b/sdk/simulator-service/tests/mocks/contextMock.ts index b0033b1053..3ff683af65 100644 --- a/sdk/simulator-service/tests/mocks/contextMock.ts +++ b/sdk/simulator-service/tests/mocks/contextMock.ts @@ -5,8 +5,8 @@ import { Token, TokenAmount, Price, + CurrencySymbol, } from '@summerfi/sdk-common/common' -import { CurrencySymbol } from '@summerfi/sdk-common/common' import { IPool } from '@summerfi/sdk-common/protocols' import { testTargetLendingPool, testTargetLendingPoolRequiredSwaps } from './testSourcePosition' import { SwapProviderType } from '@summerfi/sdk-common/swap' diff --git a/sdk/swap-service/src/implementation/SwapManagerFactory.ts b/sdk/swap-service/src/implementation/SwapManagerFactory.ts index 98a94fe87e..12d53124ee 100644 --- a/sdk/swap-service/src/implementation/SwapManagerFactory.ts +++ b/sdk/swap-service/src/implementation/SwapManagerFactory.ts @@ -32,15 +32,27 @@ export class SwapManagerFactory { }) const chainIds = configProvider.getConfigurationItem({ name: 'ONE_INCH_SWAP_CHAIN_IDS' }) - if (!apiUrl || !apiKey || !version || !allowedSwapProtocols || !chainIds) { + const apiV6Url = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_V6_URL' }) + const apiV6Key = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_V6_KEY' }) + + if ( + !apiV6Url || + !apiV6Key || + !apiUrl || + !apiKey || + !version || + !allowedSwapProtocols || + !chainIds + ) { throw new Error('OneInch configuration is missing') } return { config: { - apiUrl, - apiKey, - version, + apiUrlV4: apiUrl, + apiKeyV4: apiKey, + apiUrlV6: apiV6Url, + apiKeyV6: apiV6Key, allowedSwapProtocols: allowedSwapProtocols.split(','), }, chainIds: chainIds.split(',').map((id: string) => parseInt(id)), diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index ac7aa23490..6ce9c61b81 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -31,15 +31,25 @@ import { export class OneInchSwapProvider implements ISwapProvider { public type: SwapProviderType = SwapProviderType.OneInch - private readonly _apiUrl: string - private readonly _version: string + /** + * Both endpoints now explicitly versioned + * given incompatible paths between api versions + * */ + private readonly _apiUrlV4: string + private readonly _apiKeyV4: string + + private readonly _apiUrlV6: string + private readonly _apiKeyV6: string + private readonly _allowedSwapProtocols: string[] - private readonly _apiKey: string constructor(params: OneInchSwapProviderConfig) { - this._apiUrl = params.apiUrl - this._version = params.version - this._apiKey = params.apiKey + this._apiUrlV4 = params.apiUrlV4 + this._apiKeyV4 = params.apiKeyV4 + + this._apiUrlV6 = params.apiUrlV6 + this._apiKeyV6 = params.apiKeyV6 + this._allowedSwapProtocols = params.allowedSwapProtocols } @@ -126,7 +136,7 @@ export class OneInchSwapProvider implements ISwapProvider { baseToken: Token quoteToken?: CurrencySymbol | Token }): Promise { - const authHeader = this._getOneInchAuthHeader() + const authHeader = this._getOneInchAuthHeader(OneInchApiVersion.V6) if (params.quoteToken && params.quoteToken instanceof Token) { isTokenType(params.quoteToken) @@ -219,8 +229,19 @@ export class OneInchSwapProvider implements ISwapProvider { } } - private _getOneInchAuthHeader(): OneInchAuthHeader { - return { [OneInchAuthHeaderKey]: this._apiKey } + private _getOneInchAuthHeader(version?: OneInchApiVersion): OneInchAuthHeader { + switch (version) { + case OneInchApiVersion.V4: { + return { [OneInchAuthHeaderKey]: this._apiKeyV4 } + } + case OneInchApiVersion.V6: { + return { [OneInchAuthHeaderKey]: this._apiKeyV6 } + } + default: { + // Fallback to V4 given was previous default behaviour + return { [OneInchAuthHeaderKey]: this._apiKeyV4 } + } + } } private _formatOneInchSwapUrl(params: { @@ -243,7 +264,7 @@ export class OneInchSwapProvider implements ISwapProvider { ? this._allowedSwapProtocols.join(',') : '' - return `${this._apiUrl}/${this._version}/${chainId}/swap?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&fromAddress=${recipient}&slippage=${params.slippage.toString()}&protocols=${protocolsParam}&disableEstimate=${disableEstimate}&allowPartialFill=${allowPartialFill}` + return `${this._apiUrlV4}/${chainId}/swap?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&fromAddress=${recipient}&slippage=${params.slippage.toString()}&protocols=${protocolsParam}&disableEstimate=${disableEstimate}&allowPartialFill=${allowPartialFill}` } private _formatOneInchQuoteUrl(params: { @@ -259,7 +280,7 @@ export class OneInchSwapProvider implements ISwapProvider { ? this._allowedSwapProtocols.join(',') : '' - return `${this._apiUrl}/${this._version}/${chainId}/quote?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&protocols=${protocolsParam}` + return `${this._apiUrlV4}/${chainId}/quote?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&protocols=${protocolsParam}` } private _formatOneInchSpotUrl(params: { @@ -270,7 +291,12 @@ export class OneInchSwapProvider implements ISwapProvider { const chainId = params.chainInfo.chainId const tokenAddresses = params.tokenAddresses.map((address) => address.value.toLowerCase()) - return `${this._apiUrl}/${this._version}/${chainId}/price/${tokenAddresses.join(',')}?currency=${params.quoteCurrency.toUpperCase()}` + /** + * https://portal.1inch.dev/documentation/spot-price/swagger?method=get&path=%2Fv1.1%2F1%2F%7Baddresses%7D + */ + const SPOT_PRICE_API_ENDPOINT_VERSION = 'v1.1' + + return `${this._apiUrlV6}/price/${SPOT_PRICE_API_ENDPOINT_VERSION}/${chainId}/${tokenAddresses.join(',')}?currency=${params.quoteCurrency.toUpperCase()}` } private _extractSwapRoutes(protocols: OneInchSwapRoute[]): SwapRoute[] { @@ -289,6 +315,11 @@ export class OneInchSwapProvider implements ISwapProvider { } } +enum OneInchApiVersion { + V4, + V6, +} + function isTokenType(quoteToken: unknown): asserts quoteToken is Token { if (!quoteToken) { throw new Error('QuoteToken is undefined') diff --git a/sdk/swap-service/src/implementation/oneinch/types.ts b/sdk/swap-service/src/implementation/oneinch/types.ts index 0f161e6403..912b62dcf3 100644 --- a/sdk/swap-service/src/implementation/oneinch/types.ts +++ b/sdk/swap-service/src/implementation/oneinch/types.ts @@ -5,9 +5,10 @@ export type OneInchAuthHeader = { } export type OneInchSwapProviderConfig = { - apiUrl: string - version: string - apiKey: string + apiUrlV4: string + apiKeyV4: string + apiUrlV6: string + apiKeyV6: string allowedSwapProtocols: string[] } From 39689f010341f4b573e7739bda9414d28b270cc9 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 5 Apr 2024 09:35:12 +0100 Subject: [PATCH 24/26] chore: format --- sdk/simulator-service/tests/simulator.test.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sdk/simulator-service/tests/simulator.test.ts b/sdk/simulator-service/tests/simulator.test.ts index c8b63f534b..ac211a1de8 100644 --- a/sdk/simulator-service/tests/simulator.test.ts +++ b/sdk/simulator-service/tests/simulator.test.ts @@ -1,6 +1,13 @@ -import { ISimulation, SimulationSteps, SimulationType, steps } from '@summerfi/sdk-common/simulation' -import { refinanceLendingToLendingAnyPair, - refinanceLendingToLendingSamePair, } from '../src/strategies' +import { + ISimulation, + SimulationSteps, + SimulationType, + steps, +} from '@summerfi/sdk-common/simulation' +import { + refinanceLendingToLendingAnyPair, + refinanceLendingToLendingSamePair, +} from '../src/strategies' import { Percentage, newEmptyPositionFromPool } from '@summerfi/sdk-common/common' import { otherTestCollateral, From 7fafa722cf27b57a918dd141b65e4adf2bd8b12f Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 5 Apr 2024 11:40:32 +0100 Subject: [PATCH 25/26] refactor: try to clarify differences between older 1inch api and newer version --- .github/workflows/build-integration-test.yaml | 5 +- .github/workflows/build-unit-test.yaml | 5 +- .../implementation/ConfigurationProvider.ts | 5 +- .../src/implementation/SwapManagerFactory.ts | 20 +++--- .../oneinch/OneInchSwapProvider.ts | 68 +++++++++---------- .../src/implementation/oneinch/types.ts | 10 +-- 6 files changed, 61 insertions(+), 52 deletions(-) diff --git a/.github/workflows/build-integration-test.yaml b/.github/workflows/build-integration-test.yaml index 33908c769f..a456f48609 100644 --- a/.github/workflows/build-integration-test.yaml +++ b/.github/workflows/build-integration-test.yaml @@ -45,10 +45,11 @@ jobs: run: pnpm test:integration env: ONE_INCH_API_KEY: ${{ secrets.ONE_INCH_API_KEY }} - ONE_INCH_API_V6_KEY: ${{ secrets.ONE_INCH_API_V6_KEY }} ONE_INCH_API_VERSION: ${{ secrets.ONE_INCH_API_VERSION }} ONE_INCH_API_URL: ${{ secrets.ONE_INCH_API_URL }} - ONE_INCH_API_V6_URL: ${{ secrets.ONE_INCH_API_V6_URL }} + ONE_INCH_API_SPOT_KEY: ${{ secrets.ONE_INCH_API_SPOT_KEY }} + ONE_INCH_API_SPOT_VERSION: ${{ secrets.ONE_INCH_API_SPOT_VERSION }} + ONE_INCH_API_SPOT_URL: ${{ secrets.ONE_INCH_API_SPOT_URL }} ONE_INCH_ALLOWED_SWAP_PROTOCOLS: ${{ secrets.ONE_INCH_ALLOWED_SWAP_PROTOCOLS }} ONE_INCH_SWAP_CHAIN_IDS: ${{ secrets.ONE_INCH_SWAP_CHAIN_IDS }} MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} diff --git a/.github/workflows/build-unit-test.yaml b/.github/workflows/build-unit-test.yaml index 5108ddc1cc..514d44e498 100644 --- a/.github/workflows/build-unit-test.yaml +++ b/.github/workflows/build-unit-test.yaml @@ -45,10 +45,11 @@ jobs: run: pnpm test env: ONE_INCH_API_KEY: ${{ secrets.ONE_INCH_API_KEY }} - ONE_INCH_API_V6_KEY: ${{ secrets.ONE_INCH_API_V6_KEY }} ONE_INCH_API_VERSION: ${{ secrets.ONE_INCH_API_VERSION }} ONE_INCH_API_URL: ${{ secrets.ONE_INCH_API_URL }} - ONE_INCH_API_V6_URL: ${{ secrets.ONE_INCH_API_V6_URL }} + ONE_INCH_API_SPOT_KEY: ${{ secrets.ONE_INCH_API_SPOT_KEY }} + ONE_INCH_API_SPOT_VERSION: ${{ secrets.ONE_INCH_API_SPOT_VERSION }} + ONE_INCH_API_SPOT_URL: ${{ secrets.ONE_INCH_API_SPOT_URL }} ONE_INCH_ALLOWED_SWAP_PROTOCOLS: ${{ secrets.ONE_INCH_ALLOWED_SWAP_PROTOCOLS }} ONE_INCH_SWAP_CHAIN_IDS: ${{ secrets.ONE_INCH_SWAP_CHAIN_IDS }} MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} diff --git a/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts b/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts index 7170731c83..1be11333a3 100644 --- a/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts +++ b/sdk/configuration-provider/src/implementation/ConfigurationProvider.ts @@ -7,8 +7,9 @@ const CONFIG_KEYS: ConfigKey[] = [ 'ONE_INCH_API_VERSION', 'ONE_INCH_API_KEY', 'ONE_INCH_API_URL', - 'ONE_INCH_API_V6_KEY', - 'ONE_INCH_API_V6_URL', + 'ONE_INCH_API_SPOT_KEY', + 'ONE_INCH_API_SPOT_URL', + 'ONE_INCH_API_SPOT_VERSION', 'ONE_INCH_ALLOWED_SWAP_PROTOCOLS', 'ONE_INCH_SWAP_CHAIN_IDS', ] diff --git a/sdk/swap-service/src/implementation/SwapManagerFactory.ts b/sdk/swap-service/src/implementation/SwapManagerFactory.ts index 12d53124ee..079975a6ae 100644 --- a/sdk/swap-service/src/implementation/SwapManagerFactory.ts +++ b/sdk/swap-service/src/implementation/SwapManagerFactory.ts @@ -32,15 +32,17 @@ export class SwapManagerFactory { }) const chainIds = configProvider.getConfigurationItem({ name: 'ONE_INCH_SWAP_CHAIN_IDS' }) - const apiV6Url = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_V6_URL' }) - const apiV6Key = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_V6_KEY' }) + const apiSpotUrl = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_SPOT_URL' }) + const spotVersion = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_SPOT_VERSION' }) + const apiSpotKey = configProvider.getConfigurationItem({ name: 'ONE_INCH_API_SPOT_KEY' }) if ( - !apiV6Url || - !apiV6Key || + !apiSpotUrl || + !apiSpotKey || !apiUrl || !apiKey || !version || + !spotVersion || !allowedSwapProtocols || !chainIds ) { @@ -49,10 +51,12 @@ export class SwapManagerFactory { return { config: { - apiUrlV4: apiUrl, - apiKeyV4: apiKey, - apiUrlV6: apiV6Url, - apiKeyV6: apiV6Key, + apiUrl: apiUrl, + apiKey: apiKey, + version: version, + apiSpotUrl: apiSpotUrl, + apiSpotKey: apiSpotKey, + spotVersion: spotVersion, allowedSwapProtocols: allowedSwapProtocols.split(','), }, chainIds: chainIds.split(',').map((id: string) => parseInt(id)), diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index 01d49ba543..9cf9ca1ff5 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -32,23 +32,34 @@ export class OneInchSwapProvider implements ISwapProvider { public type: SwapProviderType = SwapProviderType.OneInch /** - * Both endpoints now explicitly versioned - * given incompatible paths between api versions + * =============== WARNING =============== + * We require 1inch's new Spot Prices endpoint (not available on 1inch.io) + * Once we move everything to 1inch's latest API we can consolidate urls & keys + * + * DO NOT add new url's or key's when modifying this class. + * Try to consolidate + * + * Once we've tested Swap data with DMA & the latest API + * https://portal.1inch.dev/documentation/swap/swagger?method=get&path=%2Fv6.0%2F1%2Fswap * */ - private readonly _apiUrlV4: string - private readonly _apiKeyV4: string + private readonly _apiUrl: string + private readonly _apiKey: string + private readonly _version: string - private readonly _apiUrlV6: string - private readonly _apiKeyV6: string + private readonly _apiSpotUrl: string + private readonly _apiSpotKey: string + private readonly _spotVersion: string private readonly _allowedSwapProtocols: string[] constructor(params: OneInchSwapProviderConfig) { - this._apiUrlV4 = params.apiUrlV4 - this._apiKeyV4 = params.apiKeyV4 + this._apiUrl = params.apiUrl + this._apiKey = params.apiKey + this._version = params.version - this._apiUrlV6 = params.apiUrlV6 - this._apiKeyV6 = params.apiKeyV6 + this._apiSpotUrl = params.apiSpotUrl + this._apiSpotKey = params.apiSpotKey + this._spotVersion = params.spotVersion this._allowedSwapProtocols = params.allowedSwapProtocols } @@ -136,7 +147,7 @@ export class OneInchSwapProvider implements ISwapProvider { baseToken: Token quoteToken?: CurrencySymbol | Token }): Promise { - const authHeader = this._getOneInchAuthHeader(OneInchApiVersion.V6) + const authHeader = this._getOneInchSpotAuthHeader() if (params.quoteToken && params.quoteToken instanceof Token) { isTokenType(params.quoteToken) @@ -229,19 +240,13 @@ export class OneInchSwapProvider implements ISwapProvider { } } - private _getOneInchAuthHeader(version?: OneInchApiVersion): OneInchAuthHeader { - switch (version) { - case OneInchApiVersion.V4: { - return { [OneInchAuthHeaderKey]: this._apiKeyV4 } - } - case OneInchApiVersion.V6: { - return { [OneInchAuthHeaderKey]: this._apiKeyV6 } - } - default: { - // Fallback to V4 given was previous default behaviour - return { [OneInchAuthHeaderKey]: this._apiKeyV4 } - } - } + private _getOneInchAuthHeader(): OneInchAuthHeader { + return { [OneInchAuthHeaderKey]: this._apiKey } + } + + + private _getOneInchSpotAuthHeader(): OneInchAuthHeader { + return { [OneInchAuthHeaderKey]: this._apiSpotKey } } private _formatOneInchSwapUrl(params: { @@ -264,7 +269,7 @@ export class OneInchSwapProvider implements ISwapProvider { ? this._allowedSwapProtocols.join(',') : '' - return `${this._apiUrlV4}/${chainId}/swap?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&fromAddress=${recipient}&slippage=${params.slippage.toString()}&protocols=${protocolsParam}&disableEstimate=${disableEstimate}&allowPartialFill=${allowPartialFill}` + return `${this._apiUrl}/${this._version}/${chainId}/swap?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&fromAddress=${recipient}&slippage=${params.slippage.toString()}&protocols=${protocolsParam}&disableEstimate=${disableEstimate}&allowPartialFill=${allowPartialFill}` } private _formatOneInchQuoteUrl(params: { @@ -280,7 +285,7 @@ export class OneInchSwapProvider implements ISwapProvider { ? this._allowedSwapProtocols.join(',') : '' - return `${this._apiUrlV4}/${chainId}/quote?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&protocols=${protocolsParam}` + return `${this._apiUrl}/${this._version}/${chainId}/quote?fromTokenAddress=${fromTokenAddress}&toTokenAddress=${toTokenAddress}&amount=${fromAmount}&protocols=${protocolsParam}` } private _formatOneInchSpotUrl(params: { @@ -292,11 +297,11 @@ export class OneInchSwapProvider implements ISwapProvider { const tokenAddresses = params.tokenAddresses.map((address) => address.value.toLowerCase()) /** + * apiSpotUrl includes the complete path for the spot price endpoint + * EG /price/v1.1 * https://portal.1inch.dev/documentation/spot-price/swagger?method=get&path=%2Fv1.1%2F1%2F%7Baddresses%7D */ - const SPOT_PRICE_API_ENDPOINT_VERSION = 'v1.1' - - return `${this._apiUrlV6}/price/${SPOT_PRICE_API_ENDPOINT_VERSION}/${chainId}/${tokenAddresses.join(',')}?currency=${params.quoteCurrency.toUpperCase()}` + return `${this._apiSpotUrl}/price/${this._spotVersion}/${chainId}/${tokenAddresses.join(',')}?currency=${params.quoteCurrency.toUpperCase()}` } private _extractSwapRoutes(protocols: OneInchSwapRoute[]): SwapRoute[] { @@ -315,11 +320,6 @@ export class OneInchSwapProvider implements ISwapProvider { } } -enum OneInchApiVersion { - V4, - V6, -} - function isTokenType(quoteToken: unknown): asserts quoteToken is Token { if (!quoteToken) { throw new Error('QuoteToken is undefined') diff --git a/sdk/swap-service/src/implementation/oneinch/types.ts b/sdk/swap-service/src/implementation/oneinch/types.ts index 912b62dcf3..c21078a13f 100644 --- a/sdk/swap-service/src/implementation/oneinch/types.ts +++ b/sdk/swap-service/src/implementation/oneinch/types.ts @@ -5,10 +5,12 @@ export type OneInchAuthHeader = { } export type OneInchSwapProviderConfig = { - apiUrlV4: string - apiKeyV4: string - apiUrlV6: string - apiKeyV6: string + apiUrl: string + apiKey: string + version: string + apiSpotUrl: string + apiSpotKey: string + spotVersion: string allowedSwapProtocols: string[] } From 320cfc08a744e97345e9a0d6ba3cac480bd435c8 Mon Sep 17 00:00:00 2001 From: James Tuckett Date: Fri, 5 Apr 2024 11:48:08 +0100 Subject: [PATCH 26/26] chore: fix formatting --- .../src/implementation/oneinch/OneInchSwapProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts index 9cf9ca1ff5..c1c0daf813 100644 --- a/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts +++ b/sdk/swap-service/src/implementation/oneinch/OneInchSwapProvider.ts @@ -244,7 +244,6 @@ export class OneInchSwapProvider implements ISwapProvider { return { [OneInchAuthHeaderKey]: this._apiKey } } - private _getOneInchSpotAuthHeader(): OneInchAuthHeader { return { [OneInchAuthHeaderKey]: this._apiSpotKey } }