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.700.0 #8056

Merged
merged 2 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .env.dev
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# feature flags
REACT_APP_FEATURE_PUBLIC_TRADE_ROUTE=true
REACT_APP_FEATURE_PUBLIC_TRADE_ROUTE=false

# logging
REACT_APP_REDUX_WINDOW=false
Expand Down
2 changes: 1 addition & 1 deletion .env.develop
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# feature flags
REACT_APP_FEATURE_LIMIT_ORDERS=true
REACT_APP_FEATURE_PUBLIC_TRADE_ROUTE=true
REACT_APP_FEATURE_PUBLIC_TRADE_ROUTE=false

# mixpanel
REACT_APP_MIXPANEL_TOKEN=1c1369f6ea23a6404bac41b42817cc4b
Expand Down
37 changes: 35 additions & 2 deletions packages/swapper/src/swapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import { bnOrZero, isSome, timeoutMonadic } from '@shapeshiftoss/utils'

import { QUOTE_TIMEOUT_ERROR, QUOTE_TIMEOUT_MS, swappers } from './constants'
import type {
GetTradeQuoteInput,
GetTradeQuoteInputWithWallet,
GetTradeRateInput,
QuoteResult,
RateResult,
SwapErrorRight,
SwapperConfig,
SwapperDeps,
SwapperName,
TradeQuote,
TradeRate,
} from './types'

export const getTradeQuotes = async (
getTradeQuoteInput: GetTradeQuoteInput,
getTradeQuoteInput: GetTradeQuoteInputWithWallet,
swapperName: SwapperName,
deps: SwapperDeps,
): Promise<QuoteResult | undefined> => {
Expand Down Expand Up @@ -42,6 +45,36 @@ export const getTradeQuotes = async (
}
}

export const getTradeRates = async (
getTradeRateInput: GetTradeRateInput,
swapperName: SwapperName,
deps: SwapperDeps,
): Promise<RateResult | undefined> => {
if (bnOrZero(getTradeRateInput.affiliateBps).lt(0)) return
if (getTradeRateInput.sellAmountIncludingProtocolFeesCryptoBaseUnit === '0') return

const swapper = swappers[swapperName]

if (swapper === undefined) return

try {
const quote = await timeoutMonadic<TradeRate[], SwapErrorRight>(
swapper.getTradeRate(getTradeRateInput, deps),
QUOTE_TIMEOUT_MS,
QUOTE_TIMEOUT_ERROR,
)

return {
...quote,
swapperName,
}
} catch (e) {
// This should never happen but it may - we should be using monadic error handling all the way through swapper call stack
// in case this logs an error from a rejected promise, it means we throw somewhere and forgot to handle errors the monadic way
console.error(e)
}
}

// TODO: this isn't a pure swapper method, see https://github.com/shapeshift/web/pull/5519
// We currently need to pass assetsById to avoid instantiating AssetService in web
// but will need to remove this second arg once this lives outside of web, to keep things pure and swappery
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ import type { Result } from '@sniptt/monads/build'
import type { InterpolationOptions } from 'node-polyglot'

import type {
CommonTradeQuoteInput,
EvmTransactionRequest,
GetEvmTradeQuoteInput,
GetTradeQuoteInput,
GetEvmTradeQuoteInputBase,
GetEvmTradeRateInput,
GetTradeRateInput,
GetUnsignedEvmTransactionArgs,
SwapErrorRight,
SwapperApi,
SwapperDeps,
TradeQuote,
TradeRate,
} from '../../types'
import { checkEvmSwapStatus, getHopByIndex, isExecutableTradeQuote } from '../../utils'
import { getTradeQuote } from './getTradeQuote/getTradeQuote'
import { fetchArbitrumBridgeSwap } from './utils/fetchArbitrumBridgeSwap'
import { getTradeQuote, getTradeRate } from './getTradeQuote/getTradeQuote'
import { fetchArbitrumBridgeQuote } from './utils/fetchArbitrumBridgeSwap'
import { assertValidTrade } from './utils/helpers'

const tradeQuoteMetadata: Map<string, { sellAssetId: AssetId; chainId: EvmChainId }> = new Map()
Expand Down Expand Up @@ -84,10 +87,10 @@ export const getParentToChildMessageDataFromParentTxHash = async ({

export const arbitrumBridgeApi: SwapperApi = {
getTradeQuote: async (
input: GetTradeQuoteInput,
input: CommonTradeQuoteInput,
deps: SwapperDeps,
): Promise<Result<TradeQuote[], SwapErrorRight>> => {
const tradeQuoteResult = await getTradeQuote(input as GetEvmTradeQuoteInput, deps)
const tradeQuoteResult = await getTradeQuote(input as GetEvmTradeQuoteInputBase, deps)

return tradeQuoteResult.map(tradeQuote => {
const id = tradeQuote.id
Expand All @@ -99,6 +102,22 @@ export const arbitrumBridgeApi: SwapperApi = {
return [tradeQuote]
})
},
getTradeRate: async (
input: GetTradeRateInput,
deps: SwapperDeps,
): Promise<Result<TradeRate[], SwapErrorRight>> => {
const tradeRateResult = await getTradeRate(input as GetEvmTradeRateInput, deps)

return tradeRateResult.map(tradeQuote => {
const id = tradeQuote.id
const firstHop = getHopByIndex(tradeQuote, 0)!
tradeQuoteMetadata.set(id, {
sellAssetId: firstHop.sellAsset.assetId,
chainId: firstHop.sellAsset.chainId as EvmChainId,
})
return [tradeQuote]
})
},

getUnsignedEvmTransaction: async ({
chainId,
Expand All @@ -120,15 +139,14 @@ export const arbitrumBridgeApi: SwapperApi = {
const assertion = await assertValidTrade({ buyAsset, sellAsset })
if (assertion.isErr()) throw new Error(assertion.unwrapErr().message)

const swap = await fetchArbitrumBridgeSwap({
const swap = await fetchArbitrumBridgeQuote({
chainId,
supportsEIP1559,
buyAsset,
receiveAddress,
sellAmountIncludingProtocolFeesCryptoBaseUnit,
sellAsset,
sendAddress: from,
priceOrQuote: 'quote',
assertGetEvmChainAdapter,
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constant
import type {
GetEvmTradeQuoteInput,
GetEvmTradeQuoteInputBase,
GetEvmTradeRateInput,
SingleHopTradeQuoteSteps,
SingleHopTradeRateSteps,
SwapErrorRight,
SwapperDeps,
TradeQuote,
TradeRate,
} from '../../../types'
import { SwapperName, TradeQuoteError } from '../../../types'
import { makeSwapErrorRight } from '../../../utils'
import { fetchArbitrumBridgeSwap } from '../utils/fetchArbitrumBridgeSwap'
import type { FetchArbitrumBridgeQuoteInput } from '../utils/fetchArbitrumBridgeSwap'
import {
fetchArbitrumBridgePrice,
fetchArbitrumBridgeQuote,
} from '../utils/fetchArbitrumBridgeSwap'
import { assertValidTrade } from '../utils/helpers'

export type GetEvmTradeQuoteInputWithWallet = Omit<GetEvmTradeQuoteInputBase, 'supportsEIP1559'> & {
Expand All @@ -27,6 +34,7 @@ type ArbitrumBridgeSpecificMetadata = {
}

export type ArbitrumBridgeTradeQuote = TradeQuote & ArbitrumBridgeSpecificMetadata
export type ArbitrumBridgeTradeRate = TradeRate & ArbitrumBridgeSpecificMetadata

export const isArbitrumBridgeTradeQuote = (
quote: TradeQuote | undefined,
Expand Down Expand Up @@ -61,7 +69,6 @@ export async function getTradeQuote(
receiveAddress,
sellAmountIncludingProtocolFeesCryptoBaseUnit,
sendAddress,
hasWallet,
} = input

const assertion = await assertValidTrade({ buyAsset, sellAsset })
Expand All @@ -72,15 +79,11 @@ export async function getTradeQuote(
// 15 minutes for deposits, 7 days for withdrawals
const estimatedExecutionTimeMs = isDeposit ? 15 * 60 * 1000 : 7 * 24 * 60 * 60 * 1000

if (hasWallet && !(sendAddress && receiveAddress && accountNumber !== undefined)) {
throw new Error('sendAddress and receiveAddress are required when a wallet is provided')
}

// 1/1 when bridging on Arbitrum bridge
const rate = '1'

try {
const swap = await fetchArbitrumBridgeSwap({
const args = {
supportsEIP1559: Boolean(supportsEIP1559),
chainId,
buyAsset,
Expand All @@ -89,8 +92,9 @@ export async function getTradeQuote(
sendAddress,
receiveAddress,
assertGetEvmChainAdapter,
priceOrQuote: hasWallet ? 'quote' : 'price',
})
quoteOrRate: 'quote',
}
const swap = await fetchArbitrumBridgeQuote(args as FetchArbitrumBridgeQuoteInput)

const buyAmountBeforeFeesCryptoBaseUnit = sellAmountIncludingProtocolFeesCryptoBaseUnit
const buyAmountAfterFeesCryptoBaseUnit = sellAmountIncludingProtocolFeesCryptoBaseUnit
Expand Down Expand Up @@ -134,3 +138,86 @@ export async function getTradeQuote(
)
}
}

export async function getTradeRate(
input: GetEvmTradeRateInput,
{ assertGetEvmChainAdapter }: SwapperDeps,
): Promise<Result<ArbitrumBridgeTradeRate, SwapErrorRight>> {
const {
chainId,
sellAsset,
buyAsset,
supportsEIP1559,
receiveAddress,
sellAmountIncludingProtocolFeesCryptoBaseUnit,
sendAddress,
} = input

const assertion = await assertValidTrade({ buyAsset, sellAsset })
if (assertion.isErr()) return Err(assertion.unwrapErr())

const isDeposit = sellAsset.chainId === ethChainId

// 15 minutes for deposits, 7 days for withdrawals
const estimatedExecutionTimeMs = isDeposit ? 15 * 60 * 1000 : 7 * 24 * 60 * 60 * 1000

// 1/1 when bridging on Arbitrum bridge
const rate = '1'

try {
const args = {
supportsEIP1559,
chainId,
buyAsset,
sellAmountIncludingProtocolFeesCryptoBaseUnit,
sellAsset,
sendAddress,
receiveAddress,
assertGetEvmChainAdapter,
quoteOrRate: 'rate',
}
const swap = await fetchArbitrumBridgePrice(args)

const buyAmountBeforeFeesCryptoBaseUnit = sellAmountIncludingProtocolFeesCryptoBaseUnit
const buyAmountAfterFeesCryptoBaseUnit = sellAmountIncludingProtocolFeesCryptoBaseUnit

return Ok({
id: uuid(),
accountNumber: undefined,
receiveAddress,
affiliateBps: '0',
potentialAffiliateBps: '0',
rate,
slippageTolerancePercentageDecimal: getDefaultSlippageDecimalPercentageForSwapper(
SwapperName.ArbitrumBridge,
),
steps: [
{
estimatedExecutionTimeMs,
allowanceContract: swap.allowanceContract,
rate,
buyAsset,
sellAsset,
accountNumber: undefined,
buyAmountBeforeFeesCryptoBaseUnit,
buyAmountAfterFeesCryptoBaseUnit,
sellAmountIncludingProtocolFeesCryptoBaseUnit,
feeData: {
protocolFees: {},
networkFeeCryptoBaseUnit: swap.networkFeeCryptoBaseUnit,
},
source: SwapperName.ArbitrumBridge,
},
] as SingleHopTradeRateSteps,
direction: isDeposit ? ('deposit' as const) : ('withdrawal' as const),
})
} catch (err) {
return Err(
makeSwapErrorRight({
message: '[ArbitrumBridge: tradeQuote] - failed to get fee data',
cause: err,
code: TradeQuoteError.NetworkFeeEstimationFailed,
}),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ChainId } from '@shapeshiftoss/caip'
import { bn } from '@shapeshiftoss/utils'

import type { SupportedChainIds } from '../../../types'
import { BRIDGE_TYPE } from '../types'
import { arbitrumBridgeSupportedChainIds } from './types'

export const ARBITRUM_BRIDGE_SUPPORTED_CHAIN_IDS: SupportedChainIds = {
Expand All @@ -11,23 +12,30 @@ export const ARBITRUM_BRIDGE_SUPPORTED_CHAIN_IDS: SupportedChainIds = {

// Broad estimate calculated by looking at a couple of different ERC-20 deposits
// https://github.com/OffchainLabs/arbitrum-token-bridge/blob/d17c88ef3eef3f4ffc61a04d34d50406039f045d/packages/arb-token-bridge-ui/src/util/TokenDepositUtils.ts#L45-L51
export const fallbackErc20DepositGasLimit = bn(240_000)
const fallbackErc20DepositGasLimit = bn(240_000)
// Broad estimate calculated by looking at a couple of different ERC-20 withdraws
// https://arbiscan.io/tx/0xf27939d382abcb0cce5c202489db457a6cc0d0dd8062468543400c3bf321148f
// https://arbiscan.io/tx/0xa4639374806ecc1e9de7beafbab2567c078483b84b708e862bfbd84fbc2fc1da
// https://arbiscan.io/tx/0xb6c3bce7999b2ae4bbe51a64bc7ab370d21ce9bf6807b805239acbf4c244a6db
// https://arbiscan.io/tx/0x878aa224a65d831c931192550b3d8fe114fa81660c1af8369c0e2ebea682dd5b
// https://arbiscan.io/tx/0xf293bd64f9dabddaffc4c8b97f2a602d4e9f77565f5d24018a0f70a95c1ecd38
export const fallbackErc20WithdrawGasLimit = bn(350_000)
const fallbackErc20WithdrawGasLimit = bn(350_000)
// Actually extremely accurate estimate calculated by looking at a couple of different ETH deposits - gas limit is around 100-100.05k
// https://etherscan.io/tx/0x31f7860fdb79c76d0301b9197f4e00ed2432170f1be38288f87838ce1184643a
// https://etherscan.io/tx/0x933c1b625824abeae6f15b4667e22caa3dc14be4929ae62310e2fbf1d39c7d8b
// https://etherscan.io/tx/0x695090931bb09e60fd378210a0c204ebb01cc137ff00e576074c79a41cc4fa80
// https://etherscan.io/tx/0xaaef3d2391e4b07f6f0a7524dadde735d4a9822efc2a611e5e8dd5e2708438cc
export const fallbackEthDepositGasLimit = bn(100_000)
const fallbackEthDepositGasLimit = bn(100_000)
// Broad estimate calculated by looking at a couple of different ETH withdraws
export const fallbackEthWithdrawGasLimit = bn(115_000)
const fallbackEthWithdrawGasLimit = bn(115_000)
// https://arbiscan.io/tx/0x641f1c0bacced5896e35aa505abe03076323e769e959aa0f7b9c9cd63d1741dd
// https://arbiscan.io/tx/0x060f503fa97b137d4298a6adb87cdae83030e1a43ee265b7d52e3b92493acd62
// https://arbiscan.io/tx/0x162efc48dffb5f7d8c6b454c6df42b4739edb70d2427a311538a0927c5ccdddc
// https://arbiscan.io/tx/0x51e76bc14ffa7a14d13732459ecd1c8fd3211bfb717ac2eb3de4d40fda8a9c4b

export const BRIDGE_TYPE_TO_FALLBACK_GAS_LIMIT = {
[BRIDGE_TYPE.ETH_DEPOSIT]: fallbackEthDepositGasLimit,
[BRIDGE_TYPE.ERC20_DEPOSIT]: fallbackErc20DepositGasLimit,
[BRIDGE_TYPE.ETH_WITHDRAWAL]: fallbackEthWithdrawGasLimit,
[BRIDGE_TYPE.ERC20_WITHDRAWAL]: fallbackErc20WithdrawGasLimit,
}
Loading
Loading