Skip to content
This repository has been archived by the owner on Apr 25, 2024. It is now read-only.

feat: add router trade adapter #168

Merged
merged 28 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@uniswap/universal-router-sdk",
"version": "1.8.2",
"version": "2.0.0",
"description": "sdk for integrating with the Universal Router contracts",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -53,7 +53,7 @@
"@uniswap/router-sdk": "^1.9.0",
"@uniswap/sdk-core": "^4.2.0",
"@uniswap/universal-router": "1.6.0",
"@uniswap/v2-sdk": "^4.2.0",
"@uniswap/v2-sdk": "^4.3.0",
"@uniswap/v3-sdk": "^3.11.0",
"bignumber.js": "^9.0.2",
"ethers": "^5.3.1"
Expand Down
33 changes: 17 additions & 16 deletions src/entities/protocols/uniswap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export type FlatFeeOptions = {
// so we extend swap options with the permit2 permit
// when safe mode is enabled, the SDK will add an extra ETH sweep for security
export type SwapOptions = Omit<RouterSwapOptions, 'inputTokenPermit'> & {
payerIsUser?: boolean
inputTokenPermit?: Permit2Permit
flatFee?: FlatFeeOptions
safeMode?: boolean
Expand All @@ -53,7 +54,10 @@ export class UniswapTrade implements Command {
}

encode(planner: RoutePlanner, _config: TradeConfig): void {
let payerIsUser = true
if (this.options.payerIsUser === undefined) {
// default to user as payer
Object.assign(this.options, { payerIsUser: true })
}

// If the input currency is the native currency, we need to wrap it with the router as the recipient
if (this.trade.inputAmount.currency.isNative) {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -63,7 +67,7 @@ export class UniswapTrade implements Command {
this.trade.maximumAmountIn(this.options.slippageTolerance).quotient.toString(),
])
// since WETH is now owned by the router, the router pays for inputs
payerIsUser = false
Object.assign(this.options, { payerIsUser: false })
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
}
// The overall recipient at the end of the trade, SENDER_AS_RECIPIENT uses the msg.sender
this.options.recipient = this.options.recipient ?? SENDER_AS_RECIPIENT
Expand All @@ -81,13 +85,13 @@ export class UniswapTrade implements Command {
for (const swap of this.trade.swaps) {
switch (swap.route.protocol) {
case Protocol.V2:
addV2Swap(planner, swap, this.trade.tradeType, this.options, payerIsUser, routerMustCustody)
addV2Swap(planner, swap, this.trade.tradeType, this.options, routerMustCustody)
break
case Protocol.V3:
addV3Swap(planner, swap, this.trade.tradeType, this.options, payerIsUser, routerMustCustody)
addV3Swap(planner, swap, this.trade.tradeType, this.options, routerMustCustody)
break
case Protocol.MIXED:
addMixedSwap(planner, swap, this.trade.tradeType, this.options, payerIsUser, routerMustCustody)
addMixedSwap(planner, swap, this.trade.tradeType, this.options, routerMustCustody)
break
default:
throw new Error('UNSUPPORTED_TRADE_PROTOCOL')
Expand Down Expand Up @@ -165,7 +169,6 @@ function addV2Swap<TInput extends Currency, TOutput extends Currency>(
{ route, inputAmount, outputAmount }: Swap<TInput, TOutput>,
tradeType: TradeType,
options: SwapOptions,
payerIsUser: boolean,
routerMustCustody: boolean
): void {
const trade = new V2Trade(
Expand All @@ -181,15 +184,15 @@ function addV2Swap<TInput extends Currency, TOutput extends Currency>(
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
route.path.map((pool) => pool.address),
payerIsUser,
options.payerIsUser,
])
} else if (tradeType == TradeType.EXACT_OUTPUT) {
planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, [
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
route.path.map((pool) => pool.address),
payerIsUser,
options.payerIsUser,
])
}
}
Expand All @@ -200,7 +203,6 @@ function addV3Swap<TInput extends Currency, TOutput extends Currency>(
{ route, inputAmount, outputAmount }: Swap<TInput, TOutput>,
tradeType: TradeType,
options: SwapOptions,
payerIsUser: boolean,
routerMustCustody: boolean
): void {
const trade = V3Trade.createUncheckedTrade({
Expand All @@ -217,15 +219,15 @@ function addV3Swap<TInput extends Currency, TOutput extends Currency>(
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
path,
payerIsUser,
options.payerIsUser,
])
} else if (tradeType == TradeType.EXACT_OUTPUT) {
planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, [
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
path,
payerIsUser,
options.payerIsUser,
])
}
}
Expand All @@ -236,7 +238,6 @@ function addMixedSwap<TInput extends Currency, TOutput extends Currency>(
swap: Swap<TInput, TOutput>,
tradeType: TradeType,
options: SwapOptions,
payerIsUser: boolean,
routerMustCustody: boolean
): void {
const { route, inputAmount, outputAmount } = swap
Expand All @@ -245,9 +246,9 @@ function addMixedSwap<TInput extends Currency, TOutput extends Currency>(
// single hop, so it can be reduced to plain v2 or v3 swap logic
if (route.pools.length === 1) {
if (route.pools[0] instanceof Pool) {
return addV3Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody)
return addV3Swap(planner, swap, tradeType, options, routerMustCustody)
} else if (route.pools[0] instanceof Pair) {
return addV2Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody)
return addV2Swap(planner, swap, tradeType, options, routerMustCustody)
} else {
throw new Error('Invalid route type')
}
Expand Down Expand Up @@ -302,15 +303,15 @@ function addMixedSwap<TInput extends Currency, TOutput extends Currency>(
i == 0 ? amountIn : CONTRACT_BALANCE, // amountIn
!isLastSectionInRoute(i) ? 0 : amountOut, // amountOut
path, // path
payerIsUser && i === 0, // payerIsUser
options.payerIsUser && i === 0, // payerIsUser
])
} else {
planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [
isLastSectionInRoute(i) ? tradeRecipient : ROUTER_AS_RECIPIENT, // recipient
i === 0 ? amountIn : CONTRACT_BALANCE, // amountIn
!isLastSectionInRoute(i) ? 0 : amountOut, // amountOutMin
newRoute.path.map((pool) => pool.address), // path
payerIsUser && i === 0,
options.payerIsUser && i === 0,
])
}
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { SwapRouter } from './swapRouter'
export * from './entities'
export * from './utils/routerTradeAdapter'
export { RoutePlanner, CommandType } from './utils/routerCommands'
export {
UNIVERSAL_ROUTER_ADDRESS,
Expand Down
125 changes: 125 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ChainId, Token } from '@uniswap/sdk-core'
import { BigNumber } from 'ethers'

type ChainConfig = {
Expand Down Expand Up @@ -106,6 +107,130 @@ const CHAIN_CONFIGS: { [key: number]: ChainConfig } = {
},
}

export const WRAPPED_NATIVE_CURRENCY: { [chainId in ChainId]: Token } = {
[ChainId.MAINNET]: new Token(1, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 18, 'WETH', 'Wrapped Ether'),
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
[ChainId.GOERLI]: new Token(5, '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', 18, 'WETH', 'Wrapped Ether'),
[ChainId.SEPOLIA]: new Token(11155111, '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14', 18, 'WETH', 'Wrapped Ether'),
[ChainId.BNB]: new Token(56, '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', 18, 'WBNB', 'Wrapped BNB'),
[ChainId.OPTIMISM]: new Token(
ChainId.OPTIMISM,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.OPTIMISM_GOERLI]: new Token(
ChainId.OPTIMISM_GOERLI,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.OPTIMISM_SEPOLIA]: new Token(
ChainId.OPTIMISM_SEPOLIA,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.ARBITRUM_ONE]: new Token(
ChainId.ARBITRUM_ONE,
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.ARBITRUM_GOERLI]: new Token(
ChainId.ARBITRUM_GOERLI,
'0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.ARBITRUM_SEPOLIA]: new Token(
ChainId.ARBITRUM_SEPOLIA,
'0xc556bAe1e86B2aE9c22eA5E036b07E55E7596074',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.POLYGON]: new Token(
ChainId.POLYGON,
'0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
18,
'WMATIC',
'Wrapped MATIC'
),
[ChainId.POLYGON_MUMBAI]: new Token(
ChainId.POLYGON_MUMBAI,
'0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889',
18,
'WMATIC',
'Wrapped MATIC'
),

// The Celo native currency 'CELO' implements the erc-20 token standard
[ChainId.CELO]: new Token(
ChainId.CELO,
'0x471EcE3750Da237f93B8E339c536989b8978a438',
18,
'CELO',
'Celo native asset'
),
[ChainId.CELO_ALFAJORES]: new Token(
ChainId.CELO_ALFAJORES,
'0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9',
18,
'CELO',
'Celo native asset'
),
[ChainId.GNOSIS]: new Token(
ChainId.GNOSIS,
'0xe91d153e0b41518a2ce8dd3d7944fa863463a97d',
18,
'WXDAI',
'Wrapped XDAI on Gnosis'
),
[ChainId.MOONBEAM]: new Token(
ChainId.MOONBEAM,
'0xAcc15dC74880C9944775448304B263D191c6077F',
18,
'WGLMR',
'Wrapped GLMR'
),
[ChainId.AVALANCHE]: new Token(
ChainId.AVALANCHE,
'0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7',
18,
'WAVAX',
'Wrapped AVAX'
),
[ChainId.BASE]: new Token(ChainId.BASE, '0x4200000000000000000000000000000000000006', 18, 'WETH', 'Wrapped Ether'),
[ChainId.BASE_GOERLI]: new Token(
ChainId.BASE_GOERLI,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.ROOTSTOCK]: new Token(
ChainId.ROOTSTOCK,
'0x542fDA317318eBF1d3DEAf76E0b632741A7e677d',
18,
'WRBTC',
'Wrapped BTC'
),
[ChainId.ZORA]: new Token(ChainId.ZORA, '0x4200000000000000000000000000000000000006', 18, 'WETH', 'Wrapped Ether'),
[ChainId.ZORA_SEPOLIA]: new Token(
ChainId.ZORA_SEPOLIA,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[ChainId.BLAST]: new Token(ChainId.BLAST, '0x4300000000000000000000000000000000000004', 18, 'WETH', 'Wrapped Ether'),
}

export const UNIVERSAL_ROUTER_ADDRESS = (chainId: number): string => {
if (!(chainId in CHAIN_CONFIGS)) throw new Error(`Universal Router not deployed on chain ${chainId}`)
return CHAIN_CONFIGS[chainId].router
Expand Down
Loading
Loading