Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: release v1.717.0 #8298

Merged
merged 8 commits into from
Dec 6, 2024
1 change: 0 additions & 1 deletion .env.base
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ REACT_APP_FEATURE_FOX_PAGE_FOX_SECTION=true
REACT_APP_FEATURE_FOX_PAGE_FOX_FARMING_SECTION=true
REACT_APP_FEATURE_FOX_PAGE_GOVERNANCE=true
REACT_APP_FEATURE_PHANTOM_WALLET=true
REACT_APP_FEATURE_ZRX_PERMIT2=true
REACT_APP_FEATURE_THOR_FREE_FEES=true
REACT_APP_FEATURE_LIMIT_ORDERS=false

Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/swapper/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const swappers: Record<
// Slippage defaults. Don't export these to ensure the getDefaultSlippageDecimalPercentageForSwapper helper function is used.
const DEFAULT_SLIPPAGE_DECIMAL_PERCENTAGE = '0.002' // .2%
const DEFAULT_COWSWAP_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005' // .5%
const DEFAULT_PORTALS_SLIPPAGE_DECIMAL_PERCENTAGE = '0.01' // 1%
const DEFAULT_PORTALS_SLIPPAGE_DECIMAL_PERCENTAGE = '0.025' // 2.5%
const DEFAULT_LIFI_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005' // .5%
const DEFAULT_THOR_SLIPPAGE_DECIMAL_PERCENTAGE = '0.01' // 1%
const DEFAULT_ARBITRUM_BRIDGE_SLIPPAGE_DECIMAL_PERCENTAGE = '0' // no slippage for Arbitrum Bridge, so no slippage tolerance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import type {
TradeQuote,
} from '../../../types'
import { SwapperName, TradeQuoteError } from '../../../types'
import { getInputOutputRate, makeSwapErrorRight } from '../../../utils'
import {
createTradeAmountTooSmallErr,
getInputOutputRate,
makeSwapErrorRight,
} from '../../../utils'
import {
CHAINFLIP_BAAS_COMMISSION,
CHAINFLIP_BOOST_SWAP_SOURCE,
Expand Down Expand Up @@ -122,9 +126,9 @@ export const _getTradeQuote = async (
cause.response!.data.detail.includes('Amount outside asset bounds')
) {
return Err(
makeSwapErrorRight({
message: cause.response!.data.detail,
code: TradeQuoteError.SellAmountBelowMinimum,
createTradeAmountTooSmallErr({
assetId: sellAsset.assetId,
minAmountCryptoBaseUnit: cause.response!.data.errors.minimalAmountNative[0],
}),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Result } from '@sniptt/monads'
import { Err, Ok } from '@sniptt/monads'
import { zeroAddress } from 'viem'

import { getDefaultSlippageDecimalPercentageForSwapper } from '../../..'
import type {
GetEvmTradeQuoteInputBase,
SingleHopTradeQuoteSteps,
Expand Down Expand Up @@ -78,8 +79,10 @@ export async function getPortalsTradeQuote(
.toNumber()

const userSlippageTolerancePercentageDecimalOrDefault = input.slippageTolerancePercentageDecimal
? Number(input.slippageTolerancePercentageDecimal)
: undefined // Use auto slippage if no user preference is provided
? bnOrZero(input.slippageTolerancePercentageDecimal).times(100).toNumber()
: bnOrZero(getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Portals))
.times(100)
.toNumber()

if (!sendAddress) return Err(makeSwapErrorRight({ message: 'missing sendAddress' }))

Expand Down Expand Up @@ -111,9 +114,7 @@ export async function getPortalsTradeQuote(
inputToken,
outputToken,
inputAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault
? userSlippageTolerancePercentageDecimalOrDefault * 100
: undefined,
slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault,
partner: getTreasuryAddressFromChainId(sellAsset.chainId),
feePercentage: affiliateBpsPercentage,
validate: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,16 @@ export async function getPortalsTradeRate(
const outputToken = `${portalsNetwork}:${buyAssetAddress}`

const userSlippageTolerancePercentageDecimalOrDefault = input.slippageTolerancePercentageDecimal
? Number(input.slippageTolerancePercentageDecimal)
: undefined // Use auto slippage if no user preference is provided
? bnOrZero(input.slippageTolerancePercentageDecimal).times(100).toNumber()
: bnOrZero(getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Portals))
.times(100)
.toNumber()

const quoteEstimateResponse = await fetchPortalsTradeEstimate({
inputToken,
outputToken,
inputAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault
? userSlippageTolerancePercentageDecimalOrDefault * 100
: bnOrZero(getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Portals))
.times(100)
.toNumber(),
slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault,
swapperConfig,
})
// Use the quote estimate endpoint to get a quote without a wallet
Expand Down Expand Up @@ -158,12 +156,11 @@ export async function getPortalsTradeRate(
input.sellAmountIncludingProtocolFeesCryptoBaseUnit,
feeData: {
networkFeeCryptoBaseUnit: undefined,
// Protocol fees are always denominated in sell asset here
protocolFees: {},
protocolFees: undefined, // We don't have protocol fees on Portals during the estimate step
},
source: SwapperName.Portals,
},
] as unknown as SingleHopTradeRateSteps,
] as SingleHopTradeRateSteps,
}

return Ok(tradeRate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type PortalsTradeOrderParams = {
inputToken: string
inputAmount: string
outputToken: string
slippageTolerancePercentage?: number
slippageTolerancePercentage: number
// Technically optional, but we always want to use an affiliate addy
partner: string
feePercentage?: number
Expand Down Expand Up @@ -97,9 +97,7 @@ export const fetchPortalsTradeOrder = async ({
validate: validate.toString(),
})

if (slippageTolerancePercentage !== undefined) {
params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2)) // Portals API expects a string with at most 2 decimal places
}
params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2)) // Portals API expects a string with at most 2 decimal places

if (feePercentage) {
params.append('feePercentage', feePercentage.toString())
Expand Down
19 changes: 5 additions & 14 deletions packages/swapper/src/swappers/ZrxSwapper/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,23 @@ export const zrxApi: SwapperApi = {
const tradeQuoteResult = await getZrxTradeQuote(
input as GetEvmTradeQuoteInputBase,
assertGetEvmChainAdapter,
config.REACT_APP_FEATURE_ZRX_PERMIT2,
assetsById,
config.REACT_APP_ZRX_BASE_URL,
)

return tradeQuoteResult.map(tradeQuote => {
return [tradeQuote]
})
return tradeQuoteResult.map(tradeQuote => [tradeQuote])
},
getTradeRate: async (
input: GetTradeQuoteInput,
{ assertGetEvmChainAdapter, assetsById, config }: SwapperDeps,
{ assetsById, config }: SwapperDeps,
): Promise<Result<TradeRate[], SwapErrorRight>> => {
const tradeRateResult = await getZrxTradeRate(
input as GetEvmTradeRateInput,
assertGetEvmChainAdapter,
config.REACT_APP_FEATURE_ZRX_PERMIT2,
assetsById,
config.REACT_APP_ZRX_BASE_URL,
)

return tradeRateResult.map(tradeQuote => {
return [tradeQuote]
})
return tradeRateResult.map(tradeQuote => [tradeQuote])
},
getUnsignedEvmTransaction: async ({
chainId,
Expand All @@ -76,16 +69,15 @@ export const zrxApi: SwapperApi = {
if (!permit2Signature) return data

// Append the signature to the calldata
// For details, see
// https://0x.org/docs/0x-swap-api/guides/swap-tokens-with-0x-swap-api#5-append-signature-length-and-signature-data-to-transactiondata
const signatureLengthInHex = numberToHex(size(permit2Signature as Hex), {
signed: false,
size: 32,
})

return concat([data, signatureLengthInHex, permit2Signature] as Hex[])
})()

// Gas estimation
const { gasLimit, ...feeData } = await evm.getFees({
adapter: assertGetEvmChainAdapter(chainId),
data: calldataWithSignature,
Expand All @@ -101,8 +93,7 @@ export const zrxApi: SwapperApi = {
value,
data: calldataWithSignature,
chainId: Number(fromChainId(chainId).chainReference),
// Use the higher amount of the node or the API, as the node doesn't always provide enough gas padding for
// total gas used.
// Use the higher amount of the node or the API, as the node doesn't always provide enough gas padding for total gas used.
gasLimit: BigNumber.max(gasLimit, estimatedGas ?? '0').toFixed(),
...feeData,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ vi.mock('../utils/zrxService', () => {

vi.mock('../utils/helpers/helpers', async () => {
const actual = await vi.importActual('../utils/helpers/helpers')

return {
...actual,
baseUrlFromChainId: vi.fn(() => 'https://0x.shapeshift.com/ethereum/'),
}
})

vi.mock('@shapeshiftoss/chain-adapters', async () => {
const { KnownChainIds } = require('@shapeshiftoss/types')

Expand All @@ -59,44 +61,73 @@ vi.mock('@shapeshiftoss/chain-adapters', async () => {

const mockOk = Ok
const mockErr = Err

describe('getZrxTradeQuote', () => {
const zrxBaseUrl = 'https://0x.shapeshift.com/ethereum/'

const assertGetChainAdapter = (_chainId: ChainId) =>
({
const assertGetChainAdapter = (_chainId: ChainId) => {
return {
getChainId: () => KnownChainIds.EthereumMainnet,
getGasFeeData: () => Promise.resolve(gasFeeData),
}) as unknown as EvmChainAdapter
} as EvmChainAdapter
}

const zrxService = zrxServiceFactory({ baseUrl: zrxBaseUrl })

it('returns quote with fee data', async () => {
const { quoteInput } = setupQuote()

vi.mocked(zrxService.get).mockReturnValue(
Promise.resolve(
mockOk({
data: { price: '100', estimatedGas: '4.2', gasPrice: '10', auxiliaryChainData: {} },
} as AxiosResponse<unknown, any>),
data: {
buyAmount: quoteInput.sellAmountIncludingProtocolFeesCryptoBaseUnit,
sellAmount: quoteInput.sellAmountIncludingProtocolFeesCryptoBaseUnit,
fees: {
integratorFee: null,
zeroExFee: {
amount: '3978501710063',
token: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
},
},
transaction: {
gas: '237315',
},
permit2: { eip712: {} },
},
} as unknown as AxiosResponse<unknown, any>),
),
)

const maybeQuote = await getZrxTradeQuote(
quoteInput,
assertGetChainAdapter,
false,
{},
{ 'eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': quoteInput.buyAsset },
zrxBaseUrl,
)
if (maybeQuote.isErr()) console.log(maybeQuote.unwrapErr())

expect(maybeQuote.isErr()).toBe(false)

const quote = maybeQuote.unwrap()

expect(quote.steps[0].feeData).toStrictEqual({
protocolFees: {},
networkFeeCryptoBaseUnit: '42',
protocolFees: {
'eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': {
amountCryptoBaseUnit: '3978501710063',
asset: quoteInput.buyAsset,
requiresBalance: false,
},
},
networkFeeCryptoBaseUnit: '22507856397000000',
})
expect(quote.steps[0].rate).toBe('100')

expect(quote.steps[0].rate).toBe('1')
})

it('bubbles up the zrxService Err from a bad zrx response', async () => {
const { quoteInput } = setupQuote()

vi.mocked(zrxService.get).mockReturnValue(
Promise.resolve(
mockErr({ some: 'error' }) as unknown as Result<
Expand All @@ -108,7 +139,6 @@ describe('getZrxTradeQuote', () => {
const maybeTradeQuote = await getZrxTradeQuote(
quoteInput,
assertGetChainAdapter,
false,
{},
zrxBaseUrl,
)
Expand All @@ -121,6 +151,7 @@ describe('getZrxTradeQuote', () => {

it('returns an Err on errored zrx response', async () => {
const { quoteInput } = setupQuote()

vi.mocked(zrxService.get).mockResolvedValue(
Err({
response: { data: { code: 502, reason: 'Failed to do some stuff' } },
Expand All @@ -130,7 +161,6 @@ describe('getZrxTradeQuote', () => {
const maybeTradeQuote = await getZrxTradeQuote(
quoteInput,
assertGetChainAdapter,
false,
{},
zrxBaseUrl,
)
Expand All @@ -141,42 +171,14 @@ describe('getZrxTradeQuote', () => {
})
})

it('returns quote without gas limit', async () => {
const { quoteInput } = setupQuote()
vi.mocked(zrxService.get).mockReturnValue(
Promise.resolve(
Ok({
data: { price: '100', estimatedGas: '4.2', gasPrice: '10', auxiliaryChainData: {} },
} as AxiosResponse<unknown>),
),
)
const maybeQuote = await getZrxTradeQuote(
quoteInput,
assertGetChainAdapter,
false,
{},
zrxBaseUrl,
)
expect(maybeQuote.isErr()).toBe(false)
const quote = maybeQuote.unwrap()

expect(quote?.steps[0].feeData).toStrictEqual({
protocolFees: {},
networkFeeCryptoBaseUnit: '42',
})
})

it('returns an Err on non ethereum chain for buyAsset', async () => {
const { quoteInput } = setupQuote()

vi.mocked(zrxService.get).mockReturnValue(Promise.resolve(Ok({} as AxiosResponse<unknown>)))

const maybeTradeQuote = await getZrxTradeQuote(
{
...quoteInput,
buyAsset: BTC,
},
{ ...quoteInput, buyAsset: BTC },
assertGetChainAdapter,
false,
{},
zrxBaseUrl,
)
Expand All @@ -192,17 +194,14 @@ describe('getZrxTradeQuote', () => {

it('returns an Err on non ethereum chain for sellAsset', async () => {
const { quoteInput, sellAsset } = setupQuote()

vi.mocked(zrxService.get).mockReturnValue(
Promise.resolve(Ok({} as AxiosResponse<unknown, any>)),
)

const maybeTradeQuote = await getZrxTradeQuote(
{
...quoteInput,
sellAsset: { ...sellAsset, chainId: btcChainId },
},
{ ...quoteInput, sellAsset: { ...sellAsset, chainId: btcChainId } },
assertGetChainAdapter,
false,
{},
zrxBaseUrl,
)
Expand Down
Loading
Loading