From 210d614ff64e6efd3cf86db56992461e6ec64bee Mon Sep 17 00:00:00 2001 From: Eric Zhong Date: Thu, 6 Feb 2025 12:07:18 -0500 Subject: [PATCH] feat(router-sdk): add getter for amounts which returns native amounts in a trade (#284) --- sdks/router-sdk/src/entities/route.test.ts | 7 + sdks/router-sdk/src/entities/route.ts | 22 ++ sdks/router-sdk/src/entities/trade.test.ts | 253 ++++++++++++++++++++- sdks/router-sdk/src/entities/trade.ts | 48 +++- 4 files changed, 325 insertions(+), 5 deletions(-) diff --git a/sdks/router-sdk/src/entities/route.test.ts b/sdks/router-sdk/src/entities/route.test.ts index 07104929f..ee9306134 100644 --- a/sdks/router-sdk/src/entities/route.test.ts +++ b/sdks/router-sdk/src/entities/route.test.ts @@ -237,4 +237,11 @@ describe('RouteV2', () => { expect(route.input).toEqual(token0) expect(route.output).toEqual(ETHER) }) + + it('assigns pathInput and pathOutput correctly', () => { + const routeOriginal = new V2RouteSDK([pair_0_weth], token0, ETHER) + const route = new RouteV2(routeOriginal) + expect(route.pathInput).toEqual(token0) + expect(route.pathOutput).toEqual(weth) + }) }) diff --git a/sdks/router-sdk/src/entities/route.ts b/sdks/router-sdk/src/entities/route.ts index fe38aa88c..79bcd5d57 100644 --- a/sdks/router-sdk/src/entities/route.ts +++ b/sdks/router-sdk/src/entities/route.ts @@ -7,6 +7,18 @@ import { Protocol } from './protocol' import { Currency, Price, Token } from '@uniswap/sdk-core' import { MixedRouteSDK } from './mixedRoute/route' +// Helper function to get the pathInput and pathOutput for a V2 / V3 route +// currency could be native so we check against the wrapped version as they don't support native ETH in path +export function getPathToken(currency: Currency, pool: Pair | V3Pool): Token { + if (pool.token0.wrapped.equals(currency.wrapped)) { + return pool.token0 + } else if (pool.token1.wrapped.equals(currency.wrapped)) { + return pool.token1 + } else { + throw new Error(`Expected token ${currency.symbol} to be either ${pool.token0.symbol} or ${pool.token1.symbol}`) + } +} + export interface IRoute { protocol: Protocol // array of pools if v3 or pairs if v2 @@ -15,6 +27,8 @@ export interface IRoute input: TInput output: TOutput + pathInput: Currency + pathOutput: Currency } // V2 route wrapper @@ -24,10 +38,14 @@ export class RouteV2 { public readonly protocol: Protocol = Protocol.V2 public readonly pools: Pair[] + public pathInput: Currency + public pathOutput: Currency constructor(v2Route: V2RouteSDK) { super(v2Route.pairs, v2Route.input, v2Route.output) this.pools = this.pairs + this.pathInput = getPathToken(v2Route.input, this.pairs[0]) + this.pathOutput = getPathToken(v2Route.output, this.pairs[this.pairs.length - 1]) } } @@ -38,10 +56,14 @@ export class RouteV3 { public readonly protocol: Protocol = Protocol.V3 public readonly path: Token[] + public pathInput: Currency + public pathOutput: Currency constructor(v3Route: V3RouteSDK) { super(v3Route.pools, v3Route.input, v3Route.output) this.path = v3Route.tokenPath + this.pathInput = getPathToken(v3Route.input, this.pools[0]) + this.pathOutput = getPathToken(v3Route.output, this.pools[this.pools.length - 1]) } } diff --git a/sdks/router-sdk/src/entities/trade.test.ts b/sdks/router-sdk/src/entities/trade.test.ts index 82ea737de..d40642896 100644 --- a/sdks/router-sdk/src/entities/trade.test.ts +++ b/sdks/router-sdk/src/entities/trade.test.ts @@ -1,7 +1,7 @@ import { sqrt, Token, CurrencyAmount, TradeType, WETH9, Ether, Percent, Price } from '@uniswap/sdk-core' import { BigNumber } from '@ethersproject/bignumber' import JSBI from 'jsbi' -import { MixedRoute, RouteV2, RouteV3 } from './route' +import { MixedRoute, RouteV2, RouteV3, RouteV4 } from './route' import { Trade } from './trade' import { Route as V3RouteSDK, @@ -14,6 +14,8 @@ import { } from '@uniswap/v3-sdk' import { Pair, Route as V2RouteSDK } from '@uniswap/v2-sdk' import { MixedRouteSDK } from './mixedRoute/route' +import { Route as V4RouteSDK, Pool as V4Pool } from '@uniswap/v4-sdk' +import { ADDRESS_ZERO } from '../constants' describe('Trade', () => { const ETHER = Ether.onChain(1) @@ -22,6 +24,8 @@ describe('Trade', () => { const token1 = new Token(1, '0x0000000000000000000000000000000000000002', 18, 't1', 'token1') const token2 = new Token(1, '0x0000000000000000000000000000000000000003', 18, 't2', 'token2') const token3 = new Token(1, '0x0000000000000000000000000000000000000004', 18, 't3', 'token3') + const SQRT_RATIO_ONE = encodeSqrtRatioX96(1, 1) + const token4WithTax = new Token( 1, '0x0000000000000000000000000000000000000005', @@ -147,6 +151,29 @@ describe('Trade', () => { CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100000)) ) + const pool_v4_1_eth = new V4Pool( + token1, + ETHER, + FeeAmount.MEDIUM, + 60, + ADDRESS_ZERO, + SQRT_RATIO_ONE, + JSBI.BigInt(10000000000000), + 0, + [ + { + index: nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidityNet: JSBI.BigInt(10000000000000), + liquidityGross: JSBI.BigInt(10000000000000), + }, + { + index: nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidityNet: JSBI.multiply(JSBI.BigInt(10000000000000), JSBI.BigInt(-1)), + liquidityGross: JSBI.BigInt(10000000000000), + }, + ] + ) + describe('#fromRoute', () => { it('can contain only a v3 route', async () => { const routeOriginal = new V3RouteSDK([pool_0_1], token0, token1) @@ -164,6 +191,9 @@ describe('Trade', () => { expect(trade.swaps.length).toEqual(1) expect(trade.routes.length).toEqual(1) expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) + + expect(trade.amounts.inputAmountNative).toEqual(undefined) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can contain only a v2 route', async () => { @@ -182,6 +212,9 @@ describe('Trade', () => { expect(trade.swaps.length).toEqual(1) expect(trade.routes.length).toEqual(1) expect(trade.tradeType).toEqual(TradeType.EXACT_OUTPUT) + + expect(trade.amounts.inputAmountNative).toEqual(undefined) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can contain only a mixed route', async () => { @@ -200,6 +233,9 @@ describe('Trade', () => { expect(trade.swaps.length).toEqual(1) expect(trade.routes.length).toEqual(1) expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) + + expect(trade.amounts.inputAmountNative).toEqual(undefined) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as input for a V3 Route exact input swap', async () => { @@ -210,6 +246,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_INPUT) expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.outputAmount.currency).toEqual(token0) + + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as input for a V3 Route exact output swap', async () => { @@ -220,6 +260,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_OUTPUT) expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.outputAmount.currency).toEqual(token0) + + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as output for a V3 Route exact output swap', async () => { @@ -232,6 +276,10 @@ describe('Trade', () => { expect(trade.outputAmount.currency).toEqual(ETHER) expect(trade.outputAmount).toEqual(amount) expect(trade.inputAmount).toEqual(expectedIn[0]) + + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.inputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as output for a V3 Route exact input swap', async () => { @@ -244,6 +292,10 @@ describe('Trade', () => { expect(trade.outputAmount.currency).toEqual(ETHER) expect(trade.inputAmount).toEqual(amount) expect(trade.outputAmount.wrapped).toEqual(expectedOut[0]) + + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.inputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as input for a V2 Route exact input swap', async () => { @@ -254,6 +306,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_INPUT) expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.outputAmount.currency).toEqual(token2) + + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as input for a V2 Route exact output swap', async () => { @@ -264,6 +320,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_OUTPUT) expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.outputAmount.currency).toEqual(token2) + + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as output for a V2 Route exact output swap', async () => { @@ -274,6 +334,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_OUTPUT) expect(trade.inputAmount.currency).toEqual(token2) expect(trade.outputAmount.currency).toEqual(ETHER) + + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.inputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as output for a V2 Route exact input swap', async () => { @@ -284,6 +348,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_INPUT) expect(trade.inputAmount.currency).toEqual(token2) expect(trade.outputAmount.currency).toEqual(ETHER) + + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.inputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as input for a Mixed Route exact input swap', async () => { @@ -294,6 +362,10 @@ describe('Trade', () => { const trade = await Trade.fromRoute(route, amount, TradeType.EXACT_INPUT) expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.outputAmount.currency).toEqual(token0) + + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.outputAmountNative).toEqual(undefined) }) it('can be constructed with ETHER as output for a Mixed Route exact input swap', async () => { @@ -306,6 +378,10 @@ describe('Trade', () => { expect(trade.outputAmount.currency).toEqual(ETHER) expect(trade.inputAmount).toEqual(amount) expect(trade.outputAmount.wrapped).toEqual(expectedOut[0]) + + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.equalTo(0)).toBe(true) + expect(trade.amounts.inputAmountNative).toEqual(undefined) }) it('throws if input currency does not match for V2 Route', async () => { @@ -552,6 +628,12 @@ describe('Trade', () => { expect(trade.inputAmount.currency).toEqual(ETHER) expect(trade.outputAmount.currency).toEqual(token1) expect(trade.swaps.length).toEqual(3) + // Expect all input amounts to be native + expect(trade.swaps.every((swap) => swap.inputAmount.currency.isNative)).toBe(true) + // Expect all route inputs to be ETH + expect(trade.swaps.every((swap) => swap.route.input.isNative)).toBe(true) + // Expect all route path inputs to be WETH, can't use pathInput because not supported in older SDKs + expect(trade.swaps.every((swap) => swap.route.pools[0].involvesToken(weth))).toBe(true) expect(trade.routes.length).toEqual(3) expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) }) @@ -627,6 +709,175 @@ describe('Trade', () => { expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) }) + it('can be constructed with ETHER as input for exact input swap, with V4 eth route and V2 weth route', async () => { + const routeOriginalV2 = new V2RouteSDK([pair_weth_0, pair_0_1], ETHER, token1) + const routev2 = new RouteV2(routeOriginalV2) + const amountv2 = CurrencyAmount.fromRawAmount(ETHER, JSBI.BigInt(100)) + + const routeOriginalV4 = new V4RouteSDK([pool_v4_1_eth], ETHER, token1) + const routev4 = new RouteV4(routeOriginalV4) + const amountv4 = CurrencyAmount.fromRawAmount(ETHER, JSBI.BigInt(1000)) + + const trade = await Trade.fromRoutes( + [{ routev2, amount: amountv2 }], + [], + TradeType.EXACT_INPUT, + [], + [{ routev4, amount: amountv4 }] + ) + + expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) + expect(trade.inputAmount.currency).toEqual(ETHER) + expect(trade.outputAmount.currency).toEqual(token1) + expect(trade.swaps.length).toEqual(2) + expect(trade.routes.length).toEqual(2) + // Expect all swap input amounts to be native + expect(trade.swaps.every((swap) => swap.inputAmount.currency.isNative)).toBe(true) + // Expect all route inputs to be ETH + expect(trade.swaps.every((swap) => swap.route.input.isNative)).toBe(true) + // However, expect the routes to be preserved (v2 using WETH and v4 using ETH) + expect(trade.swaps[0].route.pathInput).toEqual(weth) + expect(trade.swaps[1].route.pathInput).toEqual(ETHER) + + // Expect inputAmount to be the sum of the input amounts of the swaps + expect(trade.amounts.inputAmount.equalTo(trade.amounts.inputAmount)).toBe(true) + expect(trade.amounts.inputAmount.equalTo(amountv2.add(amountv4))).toBe(true) + // Expect inputAmountNative to correctly track only the amount required for the ETH input V4 route + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.equalTo(amountv4)).toBe(true) + // Expect outputAmount to be the sum of the output amounts of the swaps + expect(trade.amounts.outputAmount.equalTo(trade.amounts.outputAmount)).toBe(true) + // Expect outputAmountNative to be undefined because there is no ETH output path + expect(trade.amounts.outputAmountNative).toBeUndefined() + }) + + it('can be constructed with ETHER as input for exact output swap, with V4 eth route and V2 weth route', async () => { + const routeOriginalV2 = new V2RouteSDK([pair_weth_0, pair_0_1], ETHER, token1) + const routev2 = new RouteV2(routeOriginalV2) + const amountv2 = CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100)) + + const routeOriginalV4 = new V4RouteSDK([pool_v4_1_eth], ETHER, token1) + const routev4 = new RouteV4(routeOriginalV4) + const amountv4 = CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(1000)) + + const trade = await Trade.fromRoutes( + [{ routev2, amount: amountv2 }], + [], + TradeType.EXACT_OUTPUT, + [], + [{ routev4, amount: amountv4 }] + ) + + expect(trade.tradeType).toEqual(TradeType.EXACT_OUTPUT) + expect(trade.inputAmount.currency).toEqual(ETHER) + expect(trade.outputAmount.currency).toEqual(token1) + expect(trade.swaps.length).toEqual(2) + expect(trade.routes.length).toEqual(2) + // Expect all swap input amounts to be native + expect(trade.swaps.every((swap) => swap.inputAmount.currency.isNative)).toBe(true) + // Expect all route inputs to be ETH + expect(trade.swaps.every((swap) => swap.route.input.isNative)).toBe(true) + // However, expect the routes to be preserved (v2 using WETH and v4 using ETH) + expect(trade.swaps[0].route.pathInput).toEqual(weth) + expect(trade.swaps[1].route.pathInput).toEqual(ETHER) + + // Expect inputAmount to be the sum of the input amounts of the swaps + expect(trade.amounts.inputAmount.equalTo(trade.amounts.inputAmount)).toBe(true) + // Expect inputAmountNative to correctly track only the amount required for the ETH input V4 route + expect(trade.amounts.inputAmountNative).toBeDefined() + expect(trade.amounts.inputAmountNative?.greaterThan(0)).toBe(true) + // Expect outputAmount to be the sum of the output amounts of the swaps + expect(trade.amounts.outputAmount.equalTo(trade.amounts.outputAmount)).toBe(true) + expect(trade.amounts.outputAmount.equalTo(amountv2.add(amountv4))).toBe(true) + // Expect outputAmountNative to be undefined because there is no ETH output path + expect(trade.amounts.outputAmountNative).toBeUndefined() + }) + + it('can be constructed with ETHER as output for exact input swap, with V4 eth route and V2 weth route', async () => { + const routeOriginalV2 = new V2RouteSDK([pair_0_1, pair_weth_0], token1, ETHER) + const routev2 = new RouteV2(routeOriginalV2) + const amountv2 = CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(100)) + + const routeOriginalV4 = new V4RouteSDK([pool_v4_1_eth], token1, ETHER) + const routev4 = new RouteV4(routeOriginalV4) + const amountv4 = CurrencyAmount.fromRawAmount(token1, JSBI.BigInt(1000)) + + const trade = await Trade.fromRoutes( + [{ routev2, amount: amountv2 }], + [], + TradeType.EXACT_INPUT, + [], + [{ routev4, amount: amountv4 }] + ) + + expect(trade.tradeType).toEqual(TradeType.EXACT_INPUT) + expect(trade.inputAmount.currency).toEqual(token1) + expect(trade.outputAmount.currency).toEqual(ETHER) + expect(trade.swaps.length).toEqual(2) + expect(trade.routes.length).toEqual(2) + // Expect all swap output amounts to be native + expect(trade.swaps.every((swap) => swap.outputAmount.currency.isNative)).toBe(true) + // Expect all route outputs to be ETH + expect(trade.swaps.every((swap) => swap.route.output.isNative)).toBe(true) + // However, expect the routes to be preserved (v2 using WETH and v4 using ETH) + expect(trade.swaps[0].route.pathOutput).toEqual(weth) + expect(trade.swaps[1].route.pathOutput).toEqual(ETHER) + + // Expect inputAmount to be the sum of the input amounts of the swaps + expect(trade.amounts.inputAmount.equalTo(trade.amounts.inputAmount)).toBe(true) + expect(trade.amounts.inputAmount.equalTo(amountv2.add(amountv4))).toBe(true) + // Expect inputAmountNative to be undefined because there is no ETH input path + expect(trade.amounts.inputAmountNative).toBeUndefined() + // Expect outputAmount to be the sum of the output amounts of the swaps + expect(trade.amounts.outputAmount.equalTo(trade.amounts.outputAmount)).toBe(true) + // Expect outputAmountNative to correctly track only the amount required for the ETH output V4 route + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.greaterThan(0)).toBe(true) + }) + + it('can be constructed with ETHER as output for exact output swap, with V4 eth route and V2 weth route', async () => { + const routeOriginalV2 = new V2RouteSDK([pair_0_1, pair_weth_0], token1, ETHER) + const routev2 = new RouteV2(routeOriginalV2) + const amountv2 = CurrencyAmount.fromRawAmount(ETHER, JSBI.BigInt(100)) + + const routeOriginalV4 = new V4RouteSDK([pool_v4_1_eth], token1, ETHER) + const routev4 = new RouteV4(routeOriginalV4) + const amountv4 = CurrencyAmount.fromRawAmount(ETHER, JSBI.BigInt(1000)) + + const trade = await Trade.fromRoutes( + [{ routev2, amount: amountv2 }], + [], + TradeType.EXACT_OUTPUT, + [], + [{ routev4, amount: amountv4 }] + ) + + expect(trade.tradeType).toEqual(TradeType.EXACT_OUTPUT) + expect(trade.inputAmount.currency).toEqual(token1) + expect(trade.outputAmount.currency).toEqual(ETHER) + expect(trade.swaps.length).toEqual(2) + expect(trade.routes.length).toEqual(2) + // Expect all swap output amounts to be native + expect(trade.swaps.every((swap) => swap.outputAmount.currency.isNative)).toBe(true) + // Expect all route outputs to be ETH + expect(trade.swaps.every((swap) => swap.route.output.isNative)).toBe(true) + // However, expect the routes to be preserved (v2 using WETH and v4 using ETH) + expect(trade.swaps[0].route.pathOutput).toEqual(weth) + expect(trade.swaps[1].route.pathOutput).toEqual(ETHER) + + // Expect inputAmount to be the sum of the input amounts of the swaps + expect(trade.amounts.inputAmount.equalTo(trade.amounts.inputAmount)).toBe(true) + expect(trade.amounts.inputAmount.greaterThan(0)).toBe(true) + // Expect inputAmountNative to be undefined because there is no ETH input path + expect(trade.amounts.inputAmountNative).toBeUndefined() + // Expect outputAmount to be the sum of the output amounts of the swaps + expect(trade.amounts.outputAmount.equalTo(trade.amounts.outputAmount)).toBe(true) + expect(trade.amounts.outputAmount.equalTo(amountv2.add(amountv4))).toBe(true) + // Expect outputAmountNative to correctly track only the amount required for the ETH output V4 route + expect(trade.amounts.outputAmountNative).toBeDefined() + expect(trade.amounts.outputAmountNative?.equalTo(amountv4)).toBe(true) + }) + it('throws if pools are re-used between V3 routes', async () => { const routeOriginalV2 = new V2RouteSDK([pair_0_1, pair_1_2], token0, token2) const routev2 = new RouteV2(routeOriginalV2) diff --git a/sdks/router-sdk/src/entities/trade.ts b/sdks/router-sdk/src/entities/trade.ts index c0f64b946..8ceca2d4f 100644 --- a/sdks/router-sdk/src/entities/trade.ts +++ b/sdks/router-sdk/src/entities/trade.ts @@ -139,10 +139,10 @@ export class Trade inputAmount) - .reduce((total, cur) => total.add(cur), CurrencyAmount.fromRawAmount(inputCurrency, 0)) + .map(({ inputAmount: routeInputAmount }) => routeInputAmount) + .reduce((total, cur) => total.add(cur), CurrencyAmount.fromRawAmount(inputAmountCurrency, 0)) this._inputAmount = totalInputFromRoutes return this._inputAmount @@ -155,13 +155,53 @@ export class Trade outputAmount) + .map(({ outputAmount: routeOutputAmount }) => routeOutputAmount) .reduce((total, cur) => total.add(cur), CurrencyAmount.fromRawAmount(outputCurrency, 0)) this._outputAmount = totalOutputFromRoutes return this._outputAmount } + /** + * Returns the sum of all swaps within the trade + * @returns + * inputAmount: total input amount + * inputAmountNative: total amount of native currency required for ETH input paths + * - 0 if inputAmount is native but no native input paths + * - undefined if inputAmount is not native + * outputAmount: total output amount + * outputAmountNative: total amount of native currency returned from ETH output paths + * - 0 if outputAmount is native but no native output paths + * - undefined if outputAmount is not native + */ + public get amounts(): { + inputAmount: CurrencyAmount + inputAmountNative: CurrencyAmount | undefined + outputAmount: CurrencyAmount + outputAmountNative: CurrencyAmount | undefined + } { + // Find native currencies for reduce below + const inputNativeCurrency = this.swaps.find(({ inputAmount }) => inputAmount.currency.isNative)?.inputAmount + .currency + const outputNativeCurrency = this.swaps.find(({ outputAmount }) => outputAmount.currency.isNative)?.outputAmount + .currency + + return { + inputAmount: this.inputAmount, + inputAmountNative: inputNativeCurrency + ? this.swaps.reduce((total, swap) => { + return swap.route.pathInput.isNative ? total.add(swap.inputAmount) : total + }, CurrencyAmount.fromRawAmount(inputNativeCurrency, 0)) + : undefined, + outputAmount: this.outputAmount, + outputAmountNative: outputNativeCurrency + ? this.swaps.reduce((total, swap) => { + return swap.route.pathOutput.isNative ? total.add(swap.outputAmount) : total + }, CurrencyAmount.fromRawAmount(outputNativeCurrency, 0)) + : undefined, + } + } + private _executionPrice: Price | undefined /**