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.523.0 #6057

Merged
merged 4 commits into from
Jan 23, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"@keepkey/hdwallet-keepkey-rest": "1.40.40",
"@keepkey/keepkey-sdk": "0.2.52",
"@lifi/sdk": "^2.2.3",
"@lukemorales/query-key-factory": "^1.3.2",
"@metamask/detect-provider": "^1.2.0",
"@react-spring/web": "^9.5.2",
"@reduxjs/toolkit": "^1.9.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { getChainAdapterManager } from 'context/PluginProvider/chainAdapterSingl
import { useLocaleFormatter } from 'hooks/useLocaleFormatter/useLocaleFormatter'
import { getTxLink } from 'lib/getTxLink'
import { fromBaseUnit } from 'lib/math'
import { THORCHAIN_STREAM_SWAP_SOURCE } from 'lib/swapper/swappers/ThorchainSwapper/constants'
import {
THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE,
THORCHAIN_STREAM_SWAP_SOURCE,
} from 'lib/swapper/swappers/ThorchainSwapper/constants'
import { selectHopExecutionMetadata } from 'state/slices/tradeQuoteSlice/selectors'
import { TransactionExecutionState } from 'state/slices/tradeQuoteSlice/types'
import { useAppSelector } from 'state/store'
Expand Down Expand Up @@ -110,7 +113,9 @@ export const HopTransactionStep = ({
)
}

const isThorStreamingSwap = tradeQuoteStep.source === THORCHAIN_STREAM_SWAP_SOURCE
const isThorStreamingSwap =
tradeQuoteStep.source === THORCHAIN_STREAM_SWAP_SOURCE ||
THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE

if (sellTxHash !== undefined && isThorStreamingSwap) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import type { TextPropTypes } from 'components/Text/Text'
import { getChainAdapterManager } from 'context/PluginProvider/chainAdapterSingleton'
import { bnOrZero } from 'lib/bignumber/bignumber'
import { fromBaseUnit } from 'lib/math'
import { THORCHAIN_STREAM_SWAP_SOURCE } from 'lib/swapper/swappers/ThorchainSwapper/constants'
import {
THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE,
THORCHAIN_STREAM_SWAP_SOURCE,
} from 'lib/swapper/swappers/ThorchainSwapper/constants'
import { isSome } from 'lib/utils'
import {
selectActiveQuoteAffiliateBps,
Expand Down Expand Up @@ -248,43 +251,44 @@ export const ReceiveSummary: FC<ReceiveSummaryProps> = memo(
</Skeleton>
</Row.Value>
</Row>
{swapSource !== THORCHAIN_STREAM_SWAP_SOURCE && (
<>
<Divider borderColor='border.base' />
<Row>
<Row.Label>
<Text translation={minAmountAfterSlippageTranslation} />
</Row.Label>
<Row.Value whiteSpace='nowrap'>
<Stack spacing={0} alignItems='flex-end'>
<Skeleton isLoaded={!isLoading}>
<Amount.Crypto value={amountAfterSlippage} symbol={symbol} />
</Skeleton>
{isAmountPositive &&
hasIntermediaryTransactionOutputs &&
intermediaryTransactionOutputsParsed?.map(
({ amountCryptoPrecision, symbol, chainName }) => (
<Skeleton isLoaded={!isLoading} key={`${symbol}_${chainName}`}>
<Amount.Crypto
value={amountCryptoPrecision}
symbol={symbol}
prefix={translate('trade.or')}
suffix={
chainName
? translate('trade.onChainName', {
chainName,
})
: undefined
}
/>
</Skeleton>
),
)}
</Stack>
</Row.Value>
</Row>
</>
)}
{swapSource !== THORCHAIN_STREAM_SWAP_SOURCE &&
swapSource !== THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE && (
<>
<Divider borderColor='border.base' />
<Row>
<Row.Label>
<Text translation={minAmountAfterSlippageTranslation} />
</Row.Label>
<Row.Value whiteSpace='nowrap'>
<Stack spacing={0} alignItems='flex-end'>
<Skeleton isLoaded={!isLoading}>
<Amount.Crypto value={amountAfterSlippage} symbol={symbol} />
</Skeleton>
{isAmountPositive &&
hasIntermediaryTransactionOutputs &&
intermediaryTransactionOutputsParsed?.map(
({ amountCryptoPrecision, symbol, chainName }) => (
<Skeleton isLoaded={!isLoading} key={`${symbol}_${chainName}`}>
<Amount.Crypto
value={amountCryptoPrecision}
symbol={symbol}
prefix={translate('trade.or')}
suffix={
chainName
? translate('trade.onChainName', {
chainName,
})
: undefined
}
/>
</Skeleton>
),
)}
</Stack>
</Row.Value>
</Row>
</>
)}
</Stack>
</Collapse>
<FeeModal isOpen={showFeeModal} onClose={handleFeeModal} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ import { getTxLink } from 'lib/getTxLink'
import { firstNonZeroDecimal } from 'lib/math'
import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton'
import { MixPanelEvent } from 'lib/mixpanel/types'
import { THORCHAIN_STREAM_SWAP_SOURCE } from 'lib/swapper/swappers/ThorchainSwapper/constants'
import {
THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE,
THORCHAIN_STREAM_SWAP_SOURCE,
} from 'lib/swapper/swappers/ThorchainSwapper/constants'
import { assertUnreachable } from 'lib/utils'
import { selectManualReceiveAddress } from 'state/slices/tradeInputSlice/selectors'
import {
Expand Down Expand Up @@ -156,7 +159,9 @@ export const TradeConfirm = () => {
const txHash = buyTxHash ?? sellTxHash

const isThorStreamingSwap = useMemo(
() => tradeQuoteStep?.source === THORCHAIN_STREAM_SWAP_SOURCE,
() =>
tradeQuoteStep?.source === THORCHAIN_STREAM_SWAP_SOURCE ||
tradeQuoteStep?.source === THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE,
[tradeQuoteStep?.source],
)

Expand Down
4 changes: 4 additions & 0 deletions src/context/QueryClientProvider/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // default: true
// This will ensure the cache is used if available (the query hasn't gone stale, and the cache isn't expired)
refetchOnMount: false, // default: true
// We will most likely want this as a default but would need to audit all usages to ensure this isn't going to be a rug anywhere
// staleTime: Infinity, // default: 0
},
},
})
8 changes: 7 additions & 1 deletion src/lib/getTxLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import type { SwapSource } from '@shapeshiftoss/swapper'
import { SwapperName } from '@shapeshiftoss/swapper'
import { Dex } from '@shapeshiftoss/unchained-client'

import { THORCHAIN_STREAM_SWAP_SOURCE } from './swapper/swappers/ThorchainSwapper/constants'
import {
THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE,
THORCHAIN_LONGTAIL_SWAP_SOURCE,
THORCHAIN_STREAM_SWAP_SOURCE,
} from './swapper/swappers/ThorchainSwapper/constants'

type GetBaseUrl = {
name: SwapSource | Dex | undefined
Expand All @@ -21,6 +25,8 @@ export const getTxBaseUrl = ({ name, defaultExplorerBaseUrl, isOrder }: GetBaseU
case Dex.Thor:
case SwapperName.Thorchain:
case THORCHAIN_STREAM_SWAP_SOURCE:
case THORCHAIN_LONGTAIL_SWAP_SOURCE:
case THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE:
return 'https://viewblock.io/thorchain/tx/'
default:
return defaultExplorerBaseUrl
Expand Down
2 changes: 2 additions & 0 deletions src/lib/swapper/swappers/ThorchainSwapper/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const buySupportedChainIds: Record<ChainId, boolean> = {
}

export const THORCHAIN_STREAM_SWAP_SOURCE: SwapSource = `${SwapperName.Thorchain} • Streaming`
export const THORCHAIN_LONGTAIL_SWAP_SOURCE: SwapSource = `${SwapperName.Thorchain} • Long-tail`
export const THORCHAIN_LONGTAIL_STREAMING_SWAP_SOURCE: SwapSource = `${SwapperName.Thorchain} • Long-tail streaming`

// https://dev.thorchain.org/thorchain-dev/interface-guide/fees#thorchain-native-rune
// static automatic outbound fee as defined by: https://daemon.thorchain.shapeshift.com/lcd/thorchain/constants
Expand Down
64 changes: 49 additions & 15 deletions src/lib/swapper/swappers/ThorchainSwapper/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,30 @@ import { cosmosAssetId, fromAssetId, fromChainId, thorchainAssetId } from '@shap
import type { EvmChainId } from '@shapeshiftoss/chain-adapters'
import { cosmossdk as cosmossdkChainAdapter } from '@shapeshiftoss/chain-adapters'
import type { BTCSignTx } from '@shapeshiftoss/hdwallet-core'
import type {
CosmosSdkFeeData,
EvmTransactionRequest,
GetTradeQuoteInput,
GetUnsignedCosmosSdkTransactionArgs,
GetUnsignedEvmTransactionArgs,
GetUnsignedUtxoTransactionArgs,
SwapErrorRight,
SwapperApi,
TradeQuote,
UtxoFeeData,
import {
type CosmosSdkFeeData,
type EvmTransactionRequest,
type GetTradeQuoteInput,
type GetUnsignedCosmosSdkTransactionArgs,
type GetUnsignedEvmTransactionArgs,
type GetUnsignedUtxoTransactionArgs,
makeSwapErrorRight,
type SwapErrorRight,
type SwapperApi,
type TradeQuote,
TradeQuoteError,
type UtxoFeeData,
} from '@shapeshiftoss/swapper'
import type { AssetsByIdPartial } from '@shapeshiftoss/types'
import { KnownChainIds } from '@shapeshiftoss/types'
import { cosmossdk, evm, TxStatus } from '@shapeshiftoss/unchained-client'
import { type Result } from '@sniptt/monads/build'
import { Err, type Result } from '@sniptt/monads/build'
import assert from 'assert'
import axios from 'axios'
import { getConfig } from 'config'
import type { Address } from 'viem'
import { encodeFunctionData, parseAbiItem } from 'viem'
import { bnOrZero } from 'lib/bignumber/bignumber'
import { BigNumber, bn, bnOrZero } from 'lib/bignumber/bignumber'
import { getThorTxInfo as getUtxoThorTxInfo } from 'lib/swapper/swappers/ThorchainSwapper/utxo/utils/getThorTxData'
import { assertUnreachable } from 'lib/utils'
import { assertGetEvmChainAdapter } from 'lib/utils/evm'
Expand Down Expand Up @@ -73,7 +75,15 @@ export const thorchainApi: SwapperApi = {
supportsEIP1559,
}: GetUnsignedEvmTransactionArgs): Promise<EvmTransactionRequest> => {
// TODO: pull these from db using id so we don't have type zoo and casting hell
const { router: to, data, steps, memo: tcMemo, tradeType } = tradeQuote as ThorEvmTradeQuote
const {
router: to,
data,
steps,
memo: tcMemo,
tradeType,
longtailData,
slippageTolerancePercentageDecimal,
} = tradeQuote as ThorEvmTradeQuote
const { sellAmountIncludingProtocolFeesCryptoBaseUnit, sellAsset } = steps[0]

const value = isNativeEvmAsset(sellAsset.assetId)
Expand Down Expand Up @@ -146,9 +156,33 @@ export const thorchainApi: SwapperApi = {
const publicClient = viemClientByChainId[chainId as EvmChainId]
assert(publicClient !== undefined, `no public client found for chainId '${chainId}'`)

const expectedAmountOut = BigInt(longtailData?.longtailToL1ExpectedAmountOut ?? 0)
// Paranoia assertion - expectedAmountOut should never be 0 as it would likely lead to a loss of funds.
assert(
expectedAmountOut !== undefined && expectedAmountOut > 0n,
'expected expectedAmountOut to be a positive amount',
)

const amountOutMin = BigInt(
bnOrZero(expectedAmountOut.toString())
.times(bn(1).minus(slippageTolerancePercentageDecimal ?? 0))
.toFixed(0, BigNumber.ROUND_UP),
)

if (amountOutMin <= 0n) {
throw Err(
makeSwapErrorRight({
code: TradeQuoteError.SellAmountBelowMinimum,
message: 'Sell amount is too small',
}),
)
}

// Paranoia: ensure we have this to prevent sandwich attacks on the first step of a LongtailToL1 trade.
assert(amountOutMin > 0n, 'expected expectedAmountOut to be a positive amount')

const token: Address = fromAssetId(sellAsset.assetId).assetReference as Address
const amount: bigint = BigInt(sellAmountIncludingProtocolFeesCryptoBaseUnit)
const amountOutMin: bigint = BigInt(0) // todo: the buy amount
const currentTimestamp = BigInt(Math.floor(Date.now() / 1000))
const tenMinutes = BigInt(600)
const deadline = currentTimestamp + tenMinutes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type ThorTradeQuoteSpecificMetadata = {
isStreaming: boolean
memo: string
recommendedMinimumCryptoBaseUnit: string
longtailData?: {
longtailToL1ExpectedAmountOut?: bigint
}
}
export type ThorEvmTradeQuote = TradeQuote &
ThorTradeQuoteSpecificMetadata & {
Expand Down Expand Up @@ -58,15 +61,6 @@ export const getThorTradeQuote = async (
const buyPoolId = assetIdToPoolAssetId({ assetId: buyAsset.assetId })
const sellPoolId = assetIdToPoolAssetId({ assetId: sellAsset.assetId })

if (!buyPoolId || !sellPoolId) {
return Err(
makeSwapErrorRight({
message: 'Unsupported trade pair',
code: TradeQuoteError.UnsupportedTradePair,
}),
)
}

// If one or both of these are undefined it means we are tradeing one or more long-tail ERC20 tokens
const sellAssetPool = poolsResponse.find(pool => pool.asset === sellPoolId)
const buyAssetPool = poolsResponse.find(pool => pool.asset === buyPoolId)
Expand All @@ -83,6 +77,18 @@ export const getThorTradeQuote = async (
)
}

if (
(!buyPoolId && tradeType !== TradeType.L1ToLongTail) ||
(!sellPoolId && tradeType !== TradeType.LongTailToL1)
) {
return Err(
makeSwapErrorRight({
message: 'Unsupported trade pair',
code: TradeQuoteError.UnsupportedTradePair,
}),
)
}

const streamingInterval =
sellAssetPool && buyAssetPool
? (() => {
Expand Down