@shapeshiftoss/swapper 17.6.4 → 17.6.6-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/constants.js +17 -8
  3. package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.d.ts +2 -0
  4. package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +11 -0
  5. package/dist/cjs/swappers/NearIntentsSwapper/constants.d.ts +3 -0
  6. package/dist/cjs/swappers/NearIntentsSwapper/constants.js +6 -0
  7. package/dist/cjs/swappers/NearIntentsSwapper/endpoints.d.ts +2 -0
  8. package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +165 -0
  9. package/dist/cjs/swappers/NearIntentsSwapper/index.d.ts +4 -0
  10. package/dist/cjs/swappers/NearIntentsSwapper/index.js +20 -0
  11. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.d.ts +3 -0
  12. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +209 -0
  13. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.d.ts +3 -0
  14. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +208 -0
  15. package/dist/cjs/swappers/NearIntentsSwapper/types.d.ts +6 -0
  16. package/dist/cjs/swappers/NearIntentsSwapper/types.js +32 -0
  17. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers/helpers.d.ts +12 -0
  18. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers/helpers.js +99 -0
  19. package/dist/cjs/swappers/NearIntentsSwapper/utils/oneClickService.d.ts +3 -0
  20. package/dist/cjs/swappers/NearIntentsSwapper/utils/oneClickService.js +16 -0
  21. package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +55 -12
  22. package/dist/cjs/swappers/RelaySwapper/constant.js +1 -0
  23. package/dist/cjs/swappers/RelaySwapper/utils/fetchRelayTrade.d.ts +1 -1
  24. package/dist/cjs/swappers/RelaySwapper/utils/getRelayPsbtRelayer.js +2 -2
  25. package/dist/cjs/swappers/RelaySwapper/utils/getTrade.js +45 -0
  26. package/dist/cjs/swappers/RelaySwapper/utils/notifyTransactionIndexing.d.ts +1 -1
  27. package/dist/cjs/swappers/RelaySwapper/utils/relayTokenToAssetId.js +0 -2
  28. package/dist/cjs/swappers/RelaySwapper/utils/types.d.ts +1 -0
  29. package/dist/cjs/swappers/RelaySwapper/utils/types.js +1 -0
  30. package/dist/cjs/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +3153 -3153
  31. package/dist/cjs/swappers/ThorchainSwapper/generated/generatedTradableAssetMap.json +6 -6
  32. package/dist/cjs/swappers/ZrxSwapper/utils/helpers/helpers.js +1 -3
  33. package/dist/cjs/thorchain-utils/getL1RateOrQuote.js +20 -17
  34. package/dist/cjs/types.d.ts +18 -1
  35. package/dist/cjs/types.js +1 -0
  36. package/dist/cjs/utils/tenderly/index.d.ts +3 -0
  37. package/dist/cjs/utils/tenderly/index.js +19 -0
  38. package/dist/cjs/utils/tenderly/simulate.d.ts +20 -0
  39. package/dist/cjs/utils/tenderly/simulate.js +109 -0
  40. package/dist/cjs/utils/tenderly/storageSlots.d.ts +8 -0
  41. package/dist/cjs/utils/tenderly/storageSlots.js +94 -0
  42. package/dist/cjs/utils/tenderly/types.d.ts +56 -0
  43. package/dist/cjs/utils/tenderly/types.js +2 -0
  44. package/dist/esm/.tsbuildinfo +1 -1
  45. package/dist/esm/constants.js +9 -0
  46. package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.d.ts +2 -0
  47. package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +8 -0
  48. package/dist/esm/swappers/NearIntentsSwapper/constants.d.ts +3 -0
  49. package/dist/esm/swappers/NearIntentsSwapper/constants.js +3 -0
  50. package/dist/esm/swappers/NearIntentsSwapper/endpoints.d.ts +2 -0
  51. package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +162 -0
  52. package/dist/esm/swappers/NearIntentsSwapper/index.d.ts +4 -0
  53. package/dist/esm/swappers/NearIntentsSwapper/index.js +4 -0
  54. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.d.ts +3 -0
  55. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +205 -0
  56. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.d.ts +3 -0
  57. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +204 -0
  58. package/dist/esm/swappers/NearIntentsSwapper/types.d.ts +6 -0
  59. package/dist/esm/swappers/NearIntentsSwapper/types.js +28 -0
  60. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers/helpers.d.ts +12 -0
  61. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers/helpers.js +91 -0
  62. package/dist/esm/swappers/NearIntentsSwapper/utils/oneClickService.d.ts +3 -0
  63. package/dist/esm/swappers/NearIntentsSwapper/utils/oneClickService.js +12 -0
  64. package/dist/esm/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +59 -16
  65. package/dist/esm/swappers/RelaySwapper/constant.js +1 -0
  66. package/dist/esm/swappers/RelaySwapper/utils/fetchRelayTrade.d.ts +1 -1
  67. package/dist/esm/swappers/RelaySwapper/utils/getRelayPsbtRelayer.js +2 -2
  68. package/dist/esm/swappers/RelaySwapper/utils/getTrade.js +47 -2
  69. package/dist/esm/swappers/RelaySwapper/utils/notifyTransactionIndexing.d.ts +1 -1
  70. package/dist/esm/swappers/RelaySwapper/utils/relayTokenToAssetId.js +0 -2
  71. package/dist/esm/swappers/RelaySwapper/utils/types.d.ts +1 -0
  72. package/dist/esm/swappers/RelaySwapper/utils/types.js +1 -0
  73. package/dist/esm/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +3153 -3153
  74. package/dist/esm/swappers/ThorchainSwapper/generated/generatedTradableAssetMap.json +6 -6
  75. package/dist/esm/swappers/ZrxSwapper/utils/helpers/helpers.js +1 -3
  76. package/dist/esm/thorchain-utils/getL1RateOrQuote.js +20 -17
  77. package/dist/esm/types.d.ts +18 -1
  78. package/dist/esm/types.js +1 -0
  79. package/dist/esm/utils/tenderly/index.d.ts +3 -0
  80. package/dist/esm/utils/tenderly/index.js +3 -0
  81. package/dist/esm/utils/tenderly/simulate.d.ts +20 -0
  82. package/dist/esm/utils/tenderly/simulate.js +102 -0
  83. package/dist/esm/utils/tenderly/storageSlots.d.ts +8 -0
  84. package/dist/esm/utils/tenderly/storageSlots.js +86 -0
  85. package/dist/esm/utils/tenderly/types.d.ts +56 -0
  86. package/dist/esm/utils/tenderly/types.js +1 -0
  87. package/package.json +28 -2
@@ -0,0 +1,28 @@
1
+ import { KnownChainIds } from '@shapeshiftoss/types';
2
+ export { QuoteRequest } from '@defuse-protocol/one-click-sdk-typescript';
3
+ export const nearIntentsSupportedChainIds = [
4
+ KnownChainIds.EthereumMainnet,
5
+ KnownChainIds.ArbitrumMainnet,
6
+ KnownChainIds.BaseMainnet,
7
+ KnownChainIds.GnosisMainnet,
8
+ KnownChainIds.BnbSmartChainMainnet,
9
+ KnownChainIds.PolygonMainnet,
10
+ KnownChainIds.AvalancheMainnet,
11
+ KnownChainIds.OptimismMainnet,
12
+ KnownChainIds.BitcoinMainnet,
13
+ KnownChainIds.DogecoinMainnet,
14
+ KnownChainIds.SolanaMainnet,
15
+ ];
16
+ export const chainIdToNearIntentsChain = {
17
+ [KnownChainIds.EthereumMainnet]: 'eth',
18
+ [KnownChainIds.ArbitrumMainnet]: 'arb',
19
+ [KnownChainIds.BaseMainnet]: 'base',
20
+ [KnownChainIds.GnosisMainnet]: 'gnosis',
21
+ [KnownChainIds.BnbSmartChainMainnet]: 'bsc',
22
+ [KnownChainIds.PolygonMainnet]: 'pol',
23
+ [KnownChainIds.AvalancheMainnet]: 'avax',
24
+ [KnownChainIds.OptimismMainnet]: 'op',
25
+ [KnownChainIds.BitcoinMainnet]: 'btc',
26
+ [KnownChainIds.DogecoinMainnet]: 'doge',
27
+ [KnownChainIds.SolanaMainnet]: 'sol',
28
+ };
@@ -0,0 +1,12 @@
1
+ import type { Asset } from '@shapeshiftoss/types';
2
+ import { TxStatus } from '@shapeshiftoss/unchained-client';
3
+ import type { TransactionInstruction } from '@solana/web3.js';
4
+ import type { GetExecutionStatusResponse } from '../../types.js';
5
+ export declare const calculateAccountCreationCosts: (instructions: TransactionInstruction[]) => string;
6
+ export declare const getNearIntentsAsset: ({ nearNetwork, contractAddress, }: {
7
+ nearNetwork: string;
8
+ contractAddress: string;
9
+ }) => string;
10
+ export declare const assetToNearIntentsAsset: (asset: Asset) => Promise<string | null>;
11
+ export declare const mapNearIntentsStatus: (status: GetExecutionStatusResponse['status']) => TxStatus;
12
+ export declare const getNearIntentsStatusMessage: (status: GetExecutionStatusResponse['status']) => string | undefined;
@@ -0,0 +1,91 @@
1
+ import { ASSOCIATED_PROGRAM_ID } from '@coral-xyz/anchor/dist/cjs/utils/token';
2
+ import { fromAssetId, solanaChainId } from '@shapeshiftoss/caip';
3
+ import { TxStatus } from '@shapeshiftoss/unchained-client';
4
+ import { bnOrZero, isToken } from '@shapeshiftoss/utils';
5
+ import { zeroAddress } from 'viem';
6
+ import { chainIdToNearIntentsChain } from '../../types.js';
7
+ import { OneClickService } from '../oneClickService.js';
8
+ const ATA_RENT_LAMPORTS = 2040000;
9
+ export const calculateAccountCreationCosts = (instructions) => {
10
+ let totalCost = bnOrZero(0);
11
+ for (const ix of instructions) {
12
+ if (ix.programId.toString() === ASSOCIATED_PROGRAM_ID.toString()) {
13
+ totalCost = totalCost.plus(ATA_RENT_LAMPORTS);
14
+ }
15
+ }
16
+ return totalCost.toString();
17
+ };
18
+ export const getNearIntentsAsset = ({ nearNetwork, contractAddress, }) => {
19
+ // Native EVM assets: "nep141:eth.omft.near"
20
+ if (contractAddress === zeroAddress) {
21
+ return `nep141:${nearNetwork}.omft.near`;
22
+ }
23
+ // ERC20 tokens: "nep141:eth-0x{address}.omft.near"
24
+ return `nep141:${nearNetwork}-${contractAddress.toLowerCase()}.omft.near`;
25
+ };
26
+ const NEP245_CHAINS = ['bsc', 'pol', 'avax', 'op'];
27
+ export const assetToNearIntentsAsset = async (asset) => {
28
+ const nearNetwork = chainIdToNearIntentsChain[asset.chainId];
29
+ if (!nearNetwork)
30
+ return null;
31
+ // NEP-245 chains (BSC, Polygon, Avalanche, Optimism) and Solana require token lookup
32
+ // Asset IDs use hashed format that can't be generated from contract addresses
33
+ const requiresLookup = NEP245_CHAINS.includes(nearNetwork) || asset.chainId === solanaChainId;
34
+ if (requiresLookup) {
35
+ const tokens = await OneClickService.getTokens();
36
+ const contractAddress = isToken(asset.assetId)
37
+ ? fromAssetId(asset.assetId).assetReference.toLowerCase()
38
+ : null;
39
+ const match = tokens.find((t) => {
40
+ if (t.blockchain !== nearNetwork)
41
+ return false;
42
+ return contractAddress
43
+ ? t.contractAddress?.toLowerCase() === contractAddress
44
+ : !t.contractAddress;
45
+ });
46
+ if (!match)
47
+ return null;
48
+ return match.assetId;
49
+ }
50
+ // NEP-141 chains (ETH, ARB, BASE, GNOSIS, BTC, DOGE): use predictable format
51
+ const contractAddress = isToken(asset.assetId)
52
+ ? fromAssetId(asset.assetId).assetReference
53
+ : zeroAddress;
54
+ return getNearIntentsAsset({ nearNetwork, contractAddress });
55
+ };
56
+ export const mapNearIntentsStatus = (status) => {
57
+ switch (status) {
58
+ case 'PENDING_DEPOSIT':
59
+ case 'KNOWN_DEPOSIT_TX':
60
+ case 'PROCESSING':
61
+ return TxStatus.Pending;
62
+ case 'SUCCESS':
63
+ return TxStatus.Confirmed;
64
+ case 'INCOMPLETE_DEPOSIT':
65
+ case 'REFUNDED':
66
+ case 'FAILED':
67
+ return TxStatus.Failed;
68
+ default:
69
+ return TxStatus.Unknown;
70
+ }
71
+ };
72
+ export const getNearIntentsStatusMessage = (status) => {
73
+ switch (status) {
74
+ case 'PENDING_DEPOSIT':
75
+ return 'Waiting for deposit...';
76
+ case 'KNOWN_DEPOSIT_TX':
77
+ return 'Deposit detected, waiting for confirmation...';
78
+ case 'PROCESSING':
79
+ return 'Processing swap...';
80
+ case 'SUCCESS':
81
+ return undefined;
82
+ case 'INCOMPLETE_DEPOSIT':
83
+ return 'Insufficient deposit amount';
84
+ case 'REFUNDED':
85
+ return 'Swap failed, funds refunded';
86
+ case 'FAILED':
87
+ return 'Swap failed';
88
+ default:
89
+ return 'Unknown status';
90
+ }
91
+ };
@@ -0,0 +1,3 @@
1
+ import { ApiError, OneClickService } from '@defuse-protocol/one-click-sdk-typescript';
2
+ export declare const initializeOneClickService: (apiKey: string) => void;
3
+ export { ApiError, OneClickService };
@@ -0,0 +1,12 @@
1
+ import { ApiError, OneClickService, OpenAPI } from '@defuse-protocol/one-click-sdk-typescript';
2
+ import { ONE_CLICK_BASE_URL } from '../constants.js';
3
+ let isInitialized = false;
4
+ export const initializeOneClickService = (apiKey) => {
5
+ if (isInitialized)
6
+ return;
7
+ OpenAPI.BASE = ONE_CLICK_BASE_URL;
8
+ OpenAPI.TOKEN = apiKey;
9
+ isInitialized = true;
10
+ };
11
+ // Re-export the service and types for convenience
12
+ export { ApiError, OneClickService };
@@ -1,18 +1,19 @@
1
1
  import { fromAssetId } from '@shapeshiftoss/caip';
2
2
  import { evm } from '@shapeshiftoss/chain-adapters';
3
- import { bn, bnOrZero } from '@shapeshiftoss/utils';
3
+ import { bn, bnOrZero, convertBasisPointsToDecimalPercentage } from '@shapeshiftoss/utils';
4
4
  import { Err, Ok } from '@sniptt/monads';
5
5
  import { v4 as uuid } from 'uuid';
6
- import { zeroAddress } from 'viem';
6
+ import { getAddress, zeroAddress } from 'viem';
7
7
  import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../index.js';
8
8
  import { SwapperName, TradeQuoteError } from '../../../types.js';
9
9
  import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
10
- import { isNativeEvmAsset } from '../../utils/helpers/helpers.js';
10
+ import { simulateWithStateOverrides } from '../../../utils/tenderly/index.js';
11
+ import { getTreasuryAddressFromChainId, isNativeEvmAsset } from '../../utils/helpers/helpers.js';
11
12
  import { chainIdToPortalsNetwork } from '../constants.js';
12
- import { fetchPortalsTradeEstimate } from '../utils/fetchPortalsTradeOrder.js';
13
+ import { fetchPortalsTradeEstimate, fetchPortalsTradeOrder } from '../utils/fetchPortalsTradeOrder.js';
13
14
  import { getPortalsRouterAddressByChainId, isSupportedChainId } from '../utils/helpers.js';
14
15
  export async function getPortalsTradeRate(input, assertGetEvmChainAdapter, swapperConfig) {
15
- const { sellAsset, buyAsset, accountNumber, affiliateBps, chainId, sellAmountIncludingProtocolFeesCryptoBaseUnit, receiveAddress, } = input;
16
+ const { sellAsset, buyAsset, accountNumber, affiliateBps, chainId, sellAmountIncludingProtocolFeesCryptoBaseUnit, receiveAddress, supportsEIP1559, } = input;
16
17
  const adapter = assertGetEvmChainAdapter(chainId);
17
18
  const sellAssetChainId = sellAsset.chainId;
18
19
  const buyAssetChainId = buyAsset.chainId;
@@ -61,16 +62,32 @@ export async function getPortalsTradeRate(input, assertGetEvmChainAdapter, swapp
61
62
  : bnOrZero(getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Portals))
62
63
  .times(100)
63
64
  .toNumber();
64
- const quoteEstimateResponse = await fetchPortalsTradeEstimate({
65
+ // Calculate affiliate fee percentage (e.g., 55 bps = 0.55%)
66
+ const affiliateBpsPercentage = convertBasisPointsToDecimalPercentage(affiliateBps)
67
+ .times(100)
68
+ .toNumber();
69
+ // Dummy address for rates (no wallet connected yet)
70
+ // Using Vitalik's address as a realistic test address (same approach as Bebop)
71
+ const RATE_DUMMY_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
72
+ // Use full quote endpoint (not estimate) to get transaction data for Tenderly simulation
73
+ // Skip validation since this is just a rate quote
74
+ const quoteResponse = await fetchPortalsTradeOrder({
75
+ sender: RATE_DUMMY_ADDRESS,
65
76
  inputToken,
66
77
  outputToken,
67
78
  inputAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
68
79
  slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault,
80
+ partner: getTreasuryAddressFromChainId(chainId),
81
+ feePercentage: affiliateBpsPercentage,
82
+ validate: false,
69
83
  swapperConfig,
70
84
  });
71
- // Estimate has no protocol fees, so after-fees = expected output amount
72
- const buyAmountAfterFeesCryptoBaseUnit = quoteEstimateResponse.outputAmount;
73
- // Use the quote estimate endpoint to get a quote without a wallet
85
+ if (!quoteResponse.tx) {
86
+ throw new Error('Portals quote response missing transaction data');
87
+ }
88
+ const { context, tx } = quoteResponse;
89
+ // Use outputAmount from context, not estimate
90
+ const buyAmountAfterFeesCryptoBaseUnit = context.outputAmount;
74
91
  const inputOutputRate = getInputOutputRate({
75
92
  sellAmountCryptoBaseUnit: input.sellAmountIncludingProtocolFeesCryptoBaseUnit,
76
93
  buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
@@ -80,21 +97,47 @@ export async function getPortalsTradeRate(input, assertGetEvmChainAdapter, swapp
80
97
  const allowanceContract = getPortalsRouterAddressByChainId(chainId);
81
98
  // Don't use Portals' slippageTolerancePercentage field (it's a price indicator, not actual buffer)
82
99
  // Instead, calculate the actual buffer Portals applied from the amounts
83
- const actualBufferDecimal = bnOrZero(quoteEstimateResponse.outputAmount)
84
- .minus(quoteEstimateResponse.minOutputAmount)
85
- .div(quoteEstimateResponse.outputAmount)
100
+ const actualBufferDecimal = bnOrZero(context.outputAmount)
101
+ .minus(context.minOutputAmount)
102
+ .div(context.outputAmount)
86
103
  .toString();
87
104
  // Reverse the buffer to recover the expected output (minOutput / (1 - buffer) = output)
88
- const buyAmountBeforeSlippageCryptoBaseUnit = bnOrZero(quoteEstimateResponse.minOutputAmount)
105
+ const buyAmountBeforeSlippageCryptoBaseUnit = bnOrZero(context.minOutputAmount)
89
106
  .div(bn(1).minus(actualBufferDecimal))
90
107
  .toFixed(0);
91
108
  const slippageTolerancePercentageDecimal = actualBufferDecimal;
92
- const gasLimit = quoteEstimateResponse.context.gasLimit;
109
+ const gasLimit = await (async () => {
110
+ const tenderlySimulation = await simulateWithStateOverrides({
111
+ chainId: sellAsset.chainId,
112
+ from: tx.from,
113
+ to: tx.to,
114
+ data: tx.data,
115
+ value: tx.value,
116
+ sellAsset,
117
+ spenderAddress: getAddress(context.target),
118
+ }, {
119
+ apiKey: swapperConfig.VITE_TENDERLY_API_KEY,
120
+ accountSlug: swapperConfig.VITE_TENDERLY_ACCOUNT_SLUG,
121
+ projectSlug: swapperConfig.VITE_TENDERLY_PROJECT_SLUG,
122
+ });
123
+ if (tenderlySimulation.success) {
124
+ return tenderlySimulation.gasLimit.toString();
125
+ }
126
+ // Fallback to estimate endpoint (i.e simulation with overrides failed, but Portals still able to do their magic here)
127
+ const quoteEstimateResponse = await fetchPortalsTradeEstimate({
128
+ inputToken,
129
+ outputToken,
130
+ inputAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
131
+ slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault,
132
+ swapperConfig,
133
+ });
134
+ return quoteEstimateResponse.context.gasLimit.toString();
135
+ })();
93
136
  const { average } = await adapter.getGasFeeData();
94
137
  const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({
95
138
  ...average,
96
- supportsEIP1559: Boolean(input.supportsEIP1559),
97
- gasLimit: gasLimit.toString(),
139
+ supportsEIP1559: Boolean(supportsEIP1559),
140
+ gasLimit,
98
141
  });
99
142
  const tradeRate = {
100
143
  id: uuid(),
@@ -49,6 +49,7 @@ export const DEFAULT_RELAY_SOLANA_USER_ADDRESS = 'CbKGgVKLJFb8bBrf58DnAkdryX6ube
49
49
  export const MAXIMUM_SUPPORTED_RELAY_STEPS = 2;
50
50
  export const relayErrorCodeToTradeQuoteError = {
51
51
  [RelayErrorCode.AmountTooLow]: TradeQuoteError.SellAmountBelowMinimum,
52
+ [RelayErrorCode.ChainDisabled]: TradeQuoteError.TradingHalted,
52
53
  [RelayErrorCode.Erc20RouterAddressNotFound]: TradeQuoteError.UnsupportedTradePair,
53
54
  [RelayErrorCode.ExtraTransactionsNotSupported]: TradeQuoteError.UnsupportedTradePair,
54
55
  [RelayErrorCode.InsufficientFunds]: TradeQuoteError.SellAmountBelowTradeFee,
@@ -1,5 +1,5 @@
1
1
  import type { Result } from '@sniptt/monads';
2
2
  import type { AxiosResponse } from 'axios';
3
- import type { SwapErrorRight, SwapperConfig } from 'packages/swapper/src/types';
3
+ import type { SwapErrorRight, SwapperConfig } from '../../../types.js';
4
4
  import type { RelayFetchQuoteParams, RelayQuote } from './types.js';
5
5
  export declare const fetchRelayTrade: <T extends "rate" | "quote">(params: RelayFetchQuoteParams<T>, config: SwapperConfig) => Promise<Result<AxiosResponse<RelayQuote, any>, SwapErrorRight>>;
@@ -1,7 +1,7 @@
1
1
  import * as bitcoin from '@shapeshiftoss/bitcoinjs-lib';
2
2
  export const getRelayPsbtRelayer = (psbtHex, sellAmountCryptoBaseUnit) => {
3
3
  const psbtBuffer = Buffer.from(psbtHex, 'hex');
4
- const psbt = bitcoin.Psbt.fromBuffer(new Uint8Array(psbtBuffer));
4
+ const psbt = bitcoin.Psbt.fromBuffer(psbtBuffer);
5
5
  const outputs = psbt.txOutputs;
6
6
  for (let i = 0; i < outputs.length; i++) {
7
7
  const output = outputs[i];
@@ -9,7 +9,7 @@ export const getRelayPsbtRelayer = (psbtHex, sellAmountCryptoBaseUnit) => {
9
9
  try {
10
10
  const address = bitcoin.address.fromOutputScript(script);
11
11
  // Assuming the destination address is the one with the exact amount of sellAmountCryptoBaseUnit
12
- if (output.value === BigInt(sellAmountCryptoBaseUnit)) {
12
+ if (output.value === Number(sellAmountCryptoBaseUnit)) {
13
13
  return address;
14
14
  }
15
15
  }
@@ -1,12 +1,13 @@
1
1
  import { btcChainId, fromChainId, solanaChainId } from '@shapeshiftoss/caip';
2
- import { isEvmChainId } from '@shapeshiftoss/chain-adapters';
2
+ import { evm, isEvmChainId } from '@shapeshiftoss/chain-adapters';
3
3
  import { bnOrZero, convertBasisPointsToPercentage, convertDecimalPercentageToBasisPoints, convertPrecision, DAO_TREASURY_BASE, } from '@shapeshiftoss/utils';
4
4
  import { Err, Ok } from '@sniptt/monads';
5
5
  import { PublicKey } from '@solana/web3.js';
6
6
  import axios from 'axios';
7
- import { zeroAddress } from 'viem';
7
+ import { getAddress, zeroAddress } from 'viem';
8
8
  import { MixPanelEvent, SwapperName, TradeQuoteError } from '../../../types.js';
9
9
  import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
10
+ import { simulateWithStateOverrides } from '../../../utils/tenderly/index.js';
10
11
  import { isNativeEvmAsset } from '../../utils/helpers/helpers.js';
11
12
  import { MAXIMUM_SUPPORTED_RELAY_STEPS, relayErrorCodeToTradeQuoteError } from '../constant.js';
12
13
  import { getRelayAssetAddress } from '../utils/getRelayAssetAddress.js';
@@ -126,6 +127,11 @@ export async function getTrade({ input, deps, relayChainMap, }) {
126
127
  code: tradeQuoteErrorCode,
127
128
  }));
128
129
  }
130
+ // Fallback for unmapped error codes (shouldn't happen, but prevents crashes)
131
+ return Err(makeSwapErrorRight({
132
+ message: relayError.message || 'Unknown Relay error',
133
+ code: TradeQuoteError.UnknownError,
134
+ }));
129
135
  }
130
136
  const { data: quote } = maybeQuote.unwrap();
131
137
  const { slippageTolerance, currencyOut, timeEstimate } = quote.details;
@@ -327,6 +333,45 @@ export async function getTrade({ input, deps, relayChainMap, }) {
327
333
  const feeData = await adapter.getFeeData(getFeeDataInput);
328
334
  return feeData.fast.txFee;
329
335
  }
336
+ // Use Tenderly simulation with state overrides for EVM chains
337
+ if (isEvmChainId(sellAsset.chainId)) {
338
+ const firstStep = swapSteps[0];
339
+ const selectedItem = firstStep?.items?.[0];
340
+ if (selectedItem?.data && isRelayQuoteEvmItemData(selectedItem.data)) {
341
+ try {
342
+ const tenderlySimulation = await simulateWithStateOverrides({
343
+ chainId: sellAsset.chainId,
344
+ // NOTE: zeroAddress is here purely for the purpose of satisfying the type sistem
345
+ // That should never happen
346
+ from: getAddress(sendAddress ?? zeroAddress),
347
+ to: getAddress(selectedItem.data.to ?? zeroAddress),
348
+ data: (selectedItem.data.data ?? '0x'),
349
+ value: selectedItem.data.value ?? '0',
350
+ sellAsset,
351
+ }, {
352
+ apiKey: deps.config.VITE_TENDERLY_API_KEY,
353
+ accountSlug: deps.config.VITE_TENDERLY_ACCOUNT_SLUG,
354
+ projectSlug: deps.config.VITE_TENDERLY_PROJECT_SLUG,
355
+ });
356
+ if (tenderlySimulation.success) {
357
+ // Use Tenderly's gas estimate instead of Relay's
358
+ const adapter = deps.assertGetEvmChainAdapter(sellAsset.chainId);
359
+ const { average } = await adapter.getGasFeeData();
360
+ const supportsEIP1559 = 'maxFeePerGas' in average;
361
+ const tenderlyNetworkFee = evm.calcNetworkFeeCryptoBaseUnit({
362
+ ...average,
363
+ supportsEIP1559,
364
+ gasLimit: tenderlySimulation.gasLimit.toString(),
365
+ });
366
+ return tenderlyNetworkFee;
367
+ }
368
+ }
369
+ catch (error) {
370
+ // Silently fall through to use Relay's gas estimate
371
+ }
372
+ }
373
+ }
374
+ // Fallback to Relay's gas amount
330
375
  return quote.fees.gas.amount;
331
376
  })();
332
377
  const steps = swapSteps.map((quoteStep) => {
@@ -1,6 +1,6 @@
1
1
  import type { Result } from '@sniptt/monads';
2
2
  import type { AxiosResponse } from 'axios';
3
- import type { SwapErrorRight, SwapperConfig } from 'packages/swapper/src/types';
3
+ import type { SwapErrorRight, SwapperConfig } from '../../../types.js';
4
4
  import type { RelayQuote } from './types.js';
5
5
  type NotifyTransactionIndexingParams = {
6
6
  requestId: string;
@@ -20,8 +20,6 @@ export const relayTokenToAssetId = (relayToken) => {
20
20
  if (!isNativeAsset) {
21
21
  const assetNamespace = (() => {
22
22
  switch (true) {
23
- case CHAIN_REFERENCE.BnbSmartChainMainnet === chainReference:
24
- return ASSET_NAMESPACE.bep20;
25
23
  case isEvmChainId(chainId):
26
24
  return ASSET_NAMESPACE.erc20;
27
25
  case CHAIN_REFERENCE.SolanaMainnet === chainReference:
@@ -131,6 +131,7 @@ export type RelaySolanaInstruction = {
131
131
  };
132
132
  export declare enum RelayErrorCode {
133
133
  AmountTooLow = "AMOUNT_TOO_LOW",
134
+ ChainDisabled = "CHAIN_DISABLED",
134
135
  Erc20RouterAddressNotFound = "ERC20_ROUTER_ADDRESS_NOT_FOUND",
135
136
  ExtraTransactionsNotSupported = "EXTRA_TXS_NOT_SUPPORTED",
136
137
  InsufficientFunds = "INSUFFICIENT_FUNDS",
@@ -10,6 +10,7 @@ export const isRelayQuoteSolanaItemData = (item) => {
10
10
  export var RelayErrorCode;
11
11
  (function (RelayErrorCode) {
12
12
  RelayErrorCode["AmountTooLow"] = "AMOUNT_TOO_LOW";
13
+ RelayErrorCode["ChainDisabled"] = "CHAIN_DISABLED";
13
14
  RelayErrorCode["Erc20RouterAddressNotFound"] = "ERC20_ROUTER_ADDRESS_NOT_FOUND";
14
15
  RelayErrorCode["ExtraTransactionsNotSupported"] = "EXTRA_TXS_NOT_SUPPORTED";
15
16
  RelayErrorCode["InsufficientFunds"] = "INSUFFICIENT_FUNDS";