@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
@@ -13,6 +13,8 @@ import { jupiterApi } from './swappers/JupiterSwapper/endpoints.js';
13
13
  import { jupiterSwapper } from './swappers/JupiterSwapper/JupiterSwapper.js';
14
14
  import { mayachainApi } from './swappers/MayachainSwapper/endpoints.js';
15
15
  import { mayachainSwapper } from './swappers/MayachainSwapper/MayachainSwapper.js';
16
+ import { nearIntentsApi } from './swappers/NearIntentsSwapper/endpoints.js';
17
+ import { nearIntentsSwapper } from './swappers/NearIntentsSwapper/NearIntentsSwapper.js';
16
18
  import { portalsApi } from './swappers/PortalsSwapper/endpoints.js';
17
19
  import { portalsSwapper } from './swappers/PortalsSwapper/PortalsSwapper.js';
18
20
  import { relaySwapper } from './swappers/RelaySwapper/index.js';
@@ -75,11 +77,16 @@ export const swappers = {
75
77
  ...bebopSwapper,
76
78
  ...bebopApi,
77
79
  },
80
+ [SwapperName.NearIntents]: {
81
+ ...nearIntentsSwapper,
82
+ ...nearIntentsApi,
83
+ },
78
84
  [SwapperName.Test]: undefined,
79
85
  };
80
86
  // Slippage defaults. Don't export these to ensure the getDefaultSlippageDecimalPercentageForSwapper helper function is used.
81
87
  const DEFAULT_SLIPPAGE_DECIMAL_PERCENTAGE = '0.002'; // .2%
82
88
  const DEFAULT_COWSWAP_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005'; // .5%
89
+ const DEFAULT_NEAR_INTENTS_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005'; // .5%
83
90
  const DEFAULT_PORTALS_SLIPPAGE_DECIMAL_PERCENTAGE = '0.025'; // 2.5%
84
91
  const DEFAULT_THOR_SLIPPAGE_DECIMAL_PERCENTAGE = '0.01'; // 1%
85
92
  const DEFAULT_MAYA_SLIPPAGE_DECIMAL_PERCENTAGE = '0.01'; // 1%
@@ -112,6 +119,8 @@ export const getDefaultSlippageDecimalPercentageForSwapper = (swapperName) => {
112
119
  throw new Error('Default slippage not supported by Relay');
113
120
  case SwapperName.ButterSwap:
114
121
  return DEFAULT_BUTTERSWAP_SLIPPAGE_DECIMAL_PERCENTAGE;
122
+ case SwapperName.NearIntents:
123
+ return DEFAULT_NEAR_INTENTS_SLIPPAGE_DECIMAL_PERCENTAGE;
115
124
  default:
116
125
  return assertUnreachable(swapperName);
117
126
  }
@@ -0,0 +1,2 @@
1
+ import type { Swapper } from '../../types.js';
2
+ export declare const nearIntentsSwapper: Swapper;
@@ -0,0 +1,8 @@
1
+ import { executeEvmTransaction, executeSolanaTransaction } from '../../utils.js';
2
+ export const nearIntentsSwapper = {
3
+ executeEvmTransaction,
4
+ executeSolanaTransaction,
5
+ executeUtxoTransaction: (txToSign, { signAndBroadcastTransaction }) => {
6
+ return signAndBroadcastTransaction(txToSign);
7
+ },
8
+ };
@@ -0,0 +1,3 @@
1
+ export declare const ONE_CLICK_BASE_URL = "https://1click.chaindefuser.com";
2
+ export declare const DEFAULT_SLIPPAGE_BPS = 50;
3
+ export declare const DEFAULT_QUOTE_DEADLINE_MS: number;
@@ -0,0 +1,3 @@
1
+ export const ONE_CLICK_BASE_URL = 'https://1click.chaindefuser.com';
2
+ export const DEFAULT_SLIPPAGE_BPS = 50;
3
+ export const DEFAULT_QUOTE_DEADLINE_MS = 30 * 60 * 1000;
@@ -0,0 +1,2 @@
1
+ import type { SwapperApi } from '../../types.js';
2
+ export declare const nearIntentsApi: SwapperApi;
@@ -0,0 +1,162 @@
1
+ import { evm } from '@shapeshiftoss/chain-adapters';
2
+ import { contractAddressOrUndefined } from '@shapeshiftoss/utils';
3
+ import { createDefaultStatusResponse, getExecutableTradeStep, isExecutableTradeQuote, } from '../../utils.js';
4
+ import { isNativeEvmAsset } from '../utils/helpers/helpers.js';
5
+ import { getTradeQuote } from './swapperApi/getTradeQuote.js';
6
+ import { getTradeRate } from './swapperApi/getTradeRate.js';
7
+ import { getNearIntentsStatusMessage, mapNearIntentsStatus } from './utils/helpers/helpers.js';
8
+ import { initializeOneClickService, OneClickService } from './utils/oneClickService.js';
9
+ export const nearIntentsApi = {
10
+ getTradeQuote,
11
+ getTradeRate: (input, deps) => {
12
+ return getTradeRate(input, deps);
13
+ },
14
+ getUnsignedEvmTransaction: async ({ from, stepIndex, tradeQuote, supportsEIP1559, assertGetEvmChainAdapter, }) => {
15
+ if (!isExecutableTradeQuote(tradeQuote))
16
+ throw new Error('Unable to execute a trade rate quote');
17
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
18
+ const { accountNumber, sellAsset, nearIntentsSpecific } = step;
19
+ if (!nearIntentsSpecific)
20
+ throw new Error('nearIntentsSpecific is required');
21
+ const adapter = assertGetEvmChainAdapter(sellAsset.chainId);
22
+ const to = nearIntentsSpecific.depositAddress;
23
+ const value = step.sellAmountIncludingProtocolFeesCryptoBaseUnit;
24
+ const contractAddress = contractAddressOrUndefined(sellAsset.assetId);
25
+ const data = evm.getErc20Data(to, value, contractAddress);
26
+ const feeData = await evm.getFees({
27
+ adapter,
28
+ data: data || '0x',
29
+ to: contractAddress ?? to,
30
+ value: isNativeEvmAsset(sellAsset.assetId) ? value : '0',
31
+ from,
32
+ supportsEIP1559,
33
+ });
34
+ return adapter.buildSendApiTransaction({
35
+ accountNumber,
36
+ from,
37
+ to,
38
+ value,
39
+ chainSpecific: { contractAddress, ...feeData },
40
+ });
41
+ },
42
+ getEvmTransactionFees: async ({ from, stepIndex, tradeQuote, supportsEIP1559, assertGetEvmChainAdapter, }) => {
43
+ if (!isExecutableTradeQuote(tradeQuote))
44
+ throw new Error('Unable to execute a trade rate quote');
45
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
46
+ const { sellAsset, nearIntentsSpecific } = step;
47
+ if (!nearIntentsSpecific)
48
+ throw new Error('nearIntentsSpecific is required');
49
+ const adapter = assertGetEvmChainAdapter(sellAsset.chainId);
50
+ const to = nearIntentsSpecific.depositAddress;
51
+ const value = step.sellAmountIncludingProtocolFeesCryptoBaseUnit;
52
+ const contractAddress = contractAddressOrUndefined(sellAsset.assetId);
53
+ const data = evm.getErc20Data(to, value, contractAddress);
54
+ const feeData = await evm.getFees({
55
+ adapter,
56
+ data: data || '0x',
57
+ to: contractAddress ?? to,
58
+ value: isNativeEvmAsset(sellAsset.assetId) ? value : '0',
59
+ from,
60
+ supportsEIP1559,
61
+ });
62
+ return feeData.networkFeeCryptoBaseUnit;
63
+ },
64
+ getUnsignedUtxoTransaction: ({ stepIndex, tradeQuote, assertGetUtxoChainAdapter, xpub, accountType, }) => {
65
+ if (!isExecutableTradeQuote(tradeQuote))
66
+ throw new Error('Unable to execute a trade rate quote');
67
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
68
+ const { sellAsset, accountNumber, nearIntentsSpecific, feeData } = step;
69
+ if (!nearIntentsSpecific)
70
+ throw new Error('nearIntentsSpecific is required');
71
+ if (!xpub)
72
+ throw new Error('xpub is required for UTXO transactions');
73
+ const adapter = assertGetUtxoChainAdapter(sellAsset.chainId);
74
+ const satoshiPerByte = feeData.chainSpecific?.satsPerByte ?? '0';
75
+ return adapter.buildSendApiTransaction({
76
+ accountNumber,
77
+ to: nearIntentsSpecific.depositAddress,
78
+ value: step.sellAmountIncludingProtocolFeesCryptoBaseUnit,
79
+ sendMax: false,
80
+ chainSpecific: {
81
+ satoshiPerByte,
82
+ accountType,
83
+ },
84
+ xpub,
85
+ });
86
+ },
87
+ getUtxoTransactionFees: ({ tradeQuote, stepIndex }) => {
88
+ if (!isExecutableTradeQuote(tradeQuote))
89
+ throw new Error('Unable to execute a trade rate quote');
90
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
91
+ if (!step.feeData.networkFeeCryptoBaseUnit) {
92
+ throw new Error('Missing network fee in quote');
93
+ }
94
+ return Promise.resolve(step.feeData.networkFeeCryptoBaseUnit);
95
+ },
96
+ getUnsignedSolanaTransaction: async ({ stepIndex, tradeQuote, from, assertGetSolanaChainAdapter, }) => {
97
+ if (!isExecutableTradeQuote(tradeQuote))
98
+ throw new Error('Unable to execute a trade rate quote');
99
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
100
+ const { accountNumber, sellAsset, nearIntentsSpecific } = step;
101
+ if (!nearIntentsSpecific)
102
+ throw new Error('nearIntentsSpecific is required');
103
+ const adapter = assertGetSolanaChainAdapter(sellAsset.chainId);
104
+ const to = nearIntentsSpecific.depositAddress;
105
+ const value = step.sellAmountIncludingProtocolFeesCryptoBaseUnit;
106
+ const tokenId = contractAddressOrUndefined(sellAsset.assetId);
107
+ const { fast } = await adapter.getFeeData({
108
+ to,
109
+ value,
110
+ chainSpecific: { from, tokenId },
111
+ });
112
+ return adapter.buildSendApiTransaction({
113
+ to,
114
+ from,
115
+ value,
116
+ accountNumber,
117
+ chainSpecific: tokenId
118
+ ? {
119
+ // For SPL tokens: include compute budget parameters
120
+ tokenId,
121
+ computeUnitLimit: fast.chainSpecific.computeUnits,
122
+ computeUnitPrice: fast.chainSpecific.priorityFee,
123
+ }
124
+ : {
125
+ // For native SOL: no compute budget needed for simple transfers
126
+ tokenId,
127
+ },
128
+ });
129
+ },
130
+ getSolanaTransactionFees: ({ tradeQuote, stepIndex }) => {
131
+ if (!isExecutableTradeQuote(tradeQuote))
132
+ throw new Error('Unable to execute a trade rate quote');
133
+ const step = getExecutableTradeStep(tradeQuote, stepIndex);
134
+ if (!step.feeData.networkFeeCryptoBaseUnit) {
135
+ throw new Error('Missing network fee in quote');
136
+ }
137
+ return Promise.resolve(step.feeData.networkFeeCryptoBaseUnit);
138
+ },
139
+ checkTradeStatus: async ({ config, swap }) => {
140
+ const { nearIntentsSpecific } = swap?.metadata ?? {};
141
+ if (!nearIntentsSpecific?.depositAddress) {
142
+ return createDefaultStatusResponse(swap?.buyTxHash);
143
+ }
144
+ initializeOneClickService(config.VITE_NEAR_INTENTS_API_KEY);
145
+ try {
146
+ // TODO(gomes): SDK doesn't support depositMemo yet in getExecutionStatus
147
+ const statusResponse = await OneClickService.getExecutionStatus(nearIntentsSpecific.depositAddress);
148
+ const txStatus = mapNearIntentsStatus(statusResponse.status);
149
+ const message = getNearIntentsStatusMessage(statusResponse.status);
150
+ // Extract buyTxHash from destination chain transactions
151
+ const buyTxHash = statusResponse.swapDetails?.destinationChainTxHashes?.[0]?.hash;
152
+ return {
153
+ status: txStatus,
154
+ buyTxHash,
155
+ message,
156
+ };
157
+ }
158
+ catch (error) {
159
+ return createDefaultStatusResponse(undefined);
160
+ }
161
+ },
162
+ };
@@ -0,0 +1,4 @@
1
+ export * from './NearIntentsSwapper.js';
2
+ export * from './endpoints.js';
3
+ export * from './types.js';
4
+ export * from './constants.js';
@@ -0,0 +1,4 @@
1
+ export * from './NearIntentsSwapper.js';
2
+ export * from './endpoints.js';
3
+ export * from './types.js';
4
+ export * from './constants.js';
@@ -0,0 +1,3 @@
1
+ import type { Result } from '@sniptt/monads';
2
+ import type { CommonTradeQuoteInput, SwapErrorRight, SwapperDeps, TradeQuote } from '../../../types.js';
3
+ export declare const getTradeQuote: (input: CommonTradeQuoteInput, deps: SwapperDeps) => Promise<Result<TradeQuote[], SwapErrorRight>>;
@@ -0,0 +1,205 @@
1
+ import { CHAIN_NAMESPACE, fromAssetId } from '@shapeshiftoss/caip';
2
+ import { evm } from '@shapeshiftoss/chain-adapters';
3
+ import { bn, bnOrZero, contractAddressOrUndefined, DAO_TREASURY_NEAR, isToken, } from '@shapeshiftoss/utils';
4
+ import { Err, Ok } from '@sniptt/monads';
5
+ import { v4 as uuid } from 'uuid';
6
+ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants.js';
7
+ import { SwapperName, TradeQuoteError } from '../../../types.js';
8
+ import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
9
+ import { isNativeEvmAsset } from '../../utils/helpers/helpers.js';
10
+ import { DEFAULT_QUOTE_DEADLINE_MS, DEFAULT_SLIPPAGE_BPS } from '../constants.js';
11
+ import { QuoteRequest } from '../types.js';
12
+ import { assetToNearIntentsAsset, calculateAccountCreationCosts } from '../utils/helpers/helpers.js';
13
+ import { ApiError, initializeOneClickService, OneClickService } from '../utils/oneClickService.js';
14
+ export const getTradeQuote = async (input, deps) => {
15
+ const { sellAsset, buyAsset, accountNumber, sendAddress, receiveAddress, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount, slippageTolerancePercentageDecimal, affiliateBps, } = input;
16
+ if (accountNumber === undefined) {
17
+ return Err(makeSwapErrorRight({
18
+ message: `accountNumber is required`,
19
+ code: TradeQuoteError.UnknownError,
20
+ }));
21
+ }
22
+ if (sendAddress === undefined) {
23
+ return Err(makeSwapErrorRight({
24
+ message: `sendAddress is required`,
25
+ code: TradeQuoteError.UnknownError,
26
+ }));
27
+ }
28
+ if (receiveAddress === undefined) {
29
+ return Err(makeSwapErrorRight({
30
+ message: `receiveAddress is required`,
31
+ code: TradeQuoteError.UnknownError,
32
+ }));
33
+ }
34
+ const from = sendAddress;
35
+ try {
36
+ initializeOneClickService(deps.config.VITE_NEAR_INTENTS_API_KEY);
37
+ const originAsset = await assetToNearIntentsAsset(sellAsset);
38
+ const destinationAsset = await assetToNearIntentsAsset(buyAsset);
39
+ // This shouldn't be the case since we already got a rate and know these exist, but...
40
+ if (!(originAsset && destinationAsset)) {
41
+ return Err(makeSwapErrorRight({
42
+ code: TradeQuoteError.UnsupportedTradePair,
43
+ message: 'Unsupported asset',
44
+ }));
45
+ }
46
+ const quoteRequest = {
47
+ dry: false,
48
+ swapType: QuoteRequest.swapType.EXACT_INPUT,
49
+ slippageTolerance: slippageTolerancePercentageDecimal
50
+ ? bnOrZero(slippageTolerancePercentageDecimal).times(10000).toNumber()
51
+ : DEFAULT_SLIPPAGE_BPS,
52
+ originAsset,
53
+ destinationAsset,
54
+ amount: sellAmount,
55
+ depositType: QuoteRequest.depositType.ORIGIN_CHAIN,
56
+ refundTo: sendAddress,
57
+ refundType: QuoteRequest.refundType.ORIGIN_CHAIN,
58
+ recipient: receiveAddress,
59
+ recipientType: QuoteRequest.recipientType.DESTINATION_CHAIN,
60
+ deadline: new Date(Date.now() + DEFAULT_QUOTE_DEADLINE_MS).toISOString(),
61
+ referral: 'shapeshift',
62
+ appFees: [
63
+ {
64
+ recipient: DAO_TREASURY_NEAR,
65
+ fee: Number(affiliateBps),
66
+ },
67
+ ],
68
+ };
69
+ const quoteResponse = await OneClickService.getQuote(quoteRequest);
70
+ const { quote } = quoteResponse;
71
+ if (!quote.depositAddress) {
72
+ throw new Error('Missing deposit address in quote response');
73
+ }
74
+ const { chainNamespace } = fromAssetId(sellAsset.assetId);
75
+ const depositAddress = quote.depositAddress;
76
+ const getFeeData = async () => {
77
+ switch (chainNamespace) {
78
+ case CHAIN_NAMESPACE.Evm: {
79
+ const sellAdapter = deps.assertGetEvmChainAdapter(sellAsset.chainId);
80
+ const contractAddress = contractAddressOrUndefined(sellAsset.assetId);
81
+ const data = evm.getErc20Data(depositAddress, sellAmount, contractAddress);
82
+ const feeData = await sellAdapter.getFeeData({
83
+ to: contractAddress ?? depositAddress,
84
+ value: isNativeEvmAsset(sellAsset.assetId) ? sellAmount : '0',
85
+ chainSpecific: {
86
+ from,
87
+ contractAddress,
88
+ data: data || '0x',
89
+ },
90
+ sendMax: false,
91
+ });
92
+ return { networkFeeCryptoBaseUnit: feeData.fast.txFee };
93
+ }
94
+ case CHAIN_NAMESPACE.Utxo: {
95
+ const sellAdapter = deps.assertGetUtxoChainAdapter(sellAsset.chainId);
96
+ const pubkey = input.xpub;
97
+ if (!pubkey) {
98
+ return {
99
+ networkFeeCryptoBaseUnit: undefined,
100
+ };
101
+ }
102
+ const feeData = await sellAdapter.getFeeData({
103
+ to: depositAddress,
104
+ value: sellAmount,
105
+ chainSpecific: { pubkey },
106
+ sendMax: false,
107
+ });
108
+ return {
109
+ networkFeeCryptoBaseUnit: feeData.fast.txFee,
110
+ chainSpecific: {
111
+ satsPerByte: feeData.fast.chainSpecific.satoshiPerByte,
112
+ },
113
+ };
114
+ }
115
+ case CHAIN_NAMESPACE.Solana: {
116
+ const sellAdapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId);
117
+ const tokenId = isToken(sellAsset.assetId)
118
+ ? fromAssetId(sellAsset.assetId).assetReference
119
+ : undefined;
120
+ const instructions = await sellAdapter.buildEstimationInstructions({
121
+ from,
122
+ to: depositAddress,
123
+ tokenId,
124
+ value: sellAmount,
125
+ });
126
+ const feeData = await sellAdapter.getFeeData({
127
+ to: depositAddress,
128
+ value: sellAmount,
129
+ chainSpecific: {
130
+ from,
131
+ tokenId,
132
+ instructions,
133
+ },
134
+ sendMax: false,
135
+ });
136
+ const txFee = feeData.fast.txFee;
137
+ const ataCreationCost = calculateAccountCreationCosts(instructions);
138
+ const totalFee = bn(txFee).plus(ataCreationCost).toString();
139
+ return { networkFeeCryptoBaseUnit: totalFee };
140
+ }
141
+ default:
142
+ throw new Error(`Unsupported chain namespace: ${chainNamespace}`);
143
+ }
144
+ };
145
+ const { networkFeeCryptoBaseUnit, chainSpecific } = await getFeeData();
146
+ const rate = getInputOutputRate({
147
+ sellAmountCryptoBaseUnit: quote.amountIn,
148
+ buyAmountCryptoBaseUnit: quote.amountOut,
149
+ sellAsset,
150
+ buyAsset,
151
+ });
152
+ const tradeQuote = {
153
+ id: uuid(),
154
+ receiveAddress,
155
+ affiliateBps,
156
+ rate,
157
+ slippageTolerancePercentageDecimal: slippageTolerancePercentageDecimal ??
158
+ getDefaultSlippageDecimalPercentageForSwapper(SwapperName.NearIntents),
159
+ quoteOrRate: 'quote',
160
+ swapperName: SwapperName.NearIntents,
161
+ steps: [
162
+ {
163
+ accountNumber,
164
+ allowanceContract: quote.depositAddress,
165
+ buyAmountBeforeFeesCryptoBaseUnit: quote.amountOut,
166
+ buyAmountAfterFeesCryptoBaseUnit: quote.amountOut,
167
+ buyAsset,
168
+ feeData: {
169
+ protocolFees: {},
170
+ networkFeeCryptoBaseUnit,
171
+ ...(chainSpecific && { chainSpecific }),
172
+ },
173
+ rate,
174
+ sellAmountIncludingProtocolFeesCryptoBaseUnit: quote.amountIn,
175
+ sellAsset,
176
+ source: SwapperName.NearIntents,
177
+ estimatedExecutionTimeMs: quote.timeEstimate ? quote.timeEstimate * 1000 : undefined,
178
+ nearIntentsSpecific: {
179
+ depositAddress: quote.depositAddress ?? '',
180
+ depositMemo: quote.depositMemo,
181
+ timeEstimate: quote.timeEstimate,
182
+ deadline: quote.deadline ?? '',
183
+ },
184
+ },
185
+ ],
186
+ };
187
+ return Ok([tradeQuote]);
188
+ }
189
+ catch (error) {
190
+ console.error('[NEAR Intents] getTradeQuote error:', error);
191
+ if (error instanceof ApiError &&
192
+ (error.body?.message === 'tokenIn is not valid' ||
193
+ error.body?.message === 'tokenOut is not valid')) {
194
+ return Err(makeSwapErrorRight({
195
+ code: TradeQuoteError.UnsupportedTradePair,
196
+ message: 'Unsupported asset',
197
+ }));
198
+ }
199
+ return Err(makeSwapErrorRight({
200
+ message: error instanceof Error ? error.message : 'Unknown error getting NEAR Intents quote',
201
+ code: TradeQuoteError.QueryFailed,
202
+ cause: error,
203
+ }));
204
+ }
205
+ };
@@ -0,0 +1,3 @@
1
+ import type { Result } from '@sniptt/monads';
2
+ import type { GetTradeRateInput, SwapErrorRight, SwapperDeps, TradeRate } from '../../../types.js';
3
+ export declare const getTradeRate: (input: GetTradeRateInput, deps: SwapperDeps) => Promise<Result<TradeRate[], SwapErrorRight>>;
@@ -0,0 +1,204 @@
1
+ import { CHAIN_NAMESPACE, fromAssetId } from '@shapeshiftoss/caip';
2
+ import { evm } from '@shapeshiftoss/chain-adapters';
3
+ import { bn, bnOrZero, contractAddressOrUndefined, DAO_TREASURY_NEAR, isToken, } from '@shapeshiftoss/utils';
4
+ import { Err, Ok } from '@sniptt/monads';
5
+ import { v4 as uuid } from 'uuid';
6
+ import { getAddress } from 'viem';
7
+ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants.js';
8
+ import { SwapperName, TradeQuoteError } from '../../../types.js';
9
+ import { getInputOutputRate, makeSwapErrorRight } from '../../../utils.js';
10
+ import { simulateWithStateOverrides } from '../../../utils/tenderly/index.js';
11
+ import { isNativeEvmAsset } from '../../utils/helpers/helpers.js';
12
+ import { DEFAULT_QUOTE_DEADLINE_MS, DEFAULT_SLIPPAGE_BPS } from '../constants.js';
13
+ import { QuoteRequest } from '../types.js';
14
+ import { assetToNearIntentsAsset, calculateAccountCreationCosts } from '../utils/helpers/helpers.js';
15
+ import { ApiError, initializeOneClickService, OneClickService } from '../utils/oneClickService.js';
16
+ export const getTradeRate = async (input, deps) => {
17
+ const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount, slippageTolerancePercentageDecimal, affiliateBps, sendAddress, receiveAddress, } = input;
18
+ try {
19
+ initializeOneClickService(deps.config.VITE_NEAR_INTENTS_API_KEY);
20
+ const originAsset = await assetToNearIntentsAsset(sellAsset);
21
+ const destinationAsset = await assetToNearIntentsAsset(buyAsset);
22
+ if (!(originAsset && destinationAsset)) {
23
+ return Err(makeSwapErrorRight({
24
+ code: TradeQuoteError.UnsupportedTradePair,
25
+ message: 'Unsupported asset',
26
+ }));
27
+ }
28
+ // Wallet connected: use actual addresses
29
+ // No wallet: use "check-price" sentinel with INTENTS types
30
+ const hasWallet = sendAddress !== undefined;
31
+ const quoteRequest = {
32
+ dry: false,
33
+ swapType: QuoteRequest.swapType.EXACT_INPUT,
34
+ slippageTolerance: slippageTolerancePercentageDecimal
35
+ ? bnOrZero(slippageTolerancePercentageDecimal).times(10000).toNumber()
36
+ : DEFAULT_SLIPPAGE_BPS,
37
+ originAsset,
38
+ destinationAsset,
39
+ amount: sellAmount,
40
+ depositType: QuoteRequest.depositType.ORIGIN_CHAIN,
41
+ refundTo: sendAddress ?? 'check-price',
42
+ refundType: hasWallet
43
+ ? QuoteRequest.refundType.ORIGIN_CHAIN
44
+ : QuoteRequest.refundType.INTENTS,
45
+ recipient: receiveAddress ?? 'check-price',
46
+ recipientType: hasWallet
47
+ ? QuoteRequest.recipientType.DESTINATION_CHAIN
48
+ : QuoteRequest.recipientType.INTENTS,
49
+ deadline: new Date(Date.now() + DEFAULT_QUOTE_DEADLINE_MS).toISOString(),
50
+ referral: 'shapeshift',
51
+ appFees: [
52
+ {
53
+ recipient: DAO_TREASURY_NEAR,
54
+ fee: Number(affiliateBps),
55
+ },
56
+ ],
57
+ };
58
+ const quoteResponse = await OneClickService.getQuote(quoteRequest);
59
+ const { quote } = quoteResponse;
60
+ if (!quote.depositAddress) {
61
+ throw new Error('Missing deposit address in quote response');
62
+ }
63
+ const { chainNamespace } = fromAssetId(sellAsset.assetId);
64
+ const depositAddress = quote.depositAddress;
65
+ const getFeeData = async () => {
66
+ switch (chainNamespace) {
67
+ case CHAIN_NAMESPACE.Evm: {
68
+ try {
69
+ const contractAddress = contractAddressOrUndefined(sellAsset.assetId);
70
+ const data = evm.getErc20Data(depositAddress, sellAmount, contractAddress);
71
+ const simulationResult = await simulateWithStateOverrides({
72
+ chainId: sellAsset.chainId,
73
+ from: getAddress(sendAddress || depositAddress),
74
+ to: getAddress(contractAddress ?? depositAddress),
75
+ data: (data || '0x'),
76
+ value: isNativeEvmAsset(sellAsset.assetId) ? sellAmount : '0',
77
+ sellAsset,
78
+ }, {
79
+ apiKey: deps.config.VITE_TENDERLY_API_KEY,
80
+ accountSlug: deps.config.VITE_TENDERLY_ACCOUNT_SLUG,
81
+ projectSlug: deps.config.VITE_TENDERLY_PROJECT_SLUG,
82
+ });
83
+ if (!simulationResult.success) {
84
+ return '0';
85
+ }
86
+ // Calculate network fee using the simulated gas limit
87
+ const sellAdapter = deps.assertGetEvmChainAdapter(sellAsset.chainId);
88
+ const { average } = await sellAdapter.getGasFeeData();
89
+ const supportsEIP1559 = 'maxFeePerGas' in average;
90
+ const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({
91
+ ...average,
92
+ supportsEIP1559,
93
+ gasLimit: simulationResult.gasLimit.toString(),
94
+ });
95
+ return networkFeeCryptoBaseUnit;
96
+ }
97
+ catch (error) {
98
+ return '0';
99
+ }
100
+ }
101
+ case CHAIN_NAMESPACE.Utxo: {
102
+ const sellAdapter = deps.assertGetUtxoChainAdapter(sellAsset.chainId);
103
+ const pubkey = input.xpub;
104
+ if (!pubkey) {
105
+ return undefined;
106
+ }
107
+ const feeData = await sellAdapter.getFeeData({
108
+ to: depositAddress,
109
+ value: sellAmount,
110
+ chainSpecific: { pubkey },
111
+ sendMax: false,
112
+ });
113
+ return feeData.fast.txFee;
114
+ }
115
+ case CHAIN_NAMESPACE.Solana: {
116
+ try {
117
+ const sellAdapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId);
118
+ const tokenId = isToken(sellAsset.assetId)
119
+ ? fromAssetId(sellAsset.assetId).assetReference
120
+ : undefined;
121
+ if (!sendAddress) {
122
+ return '0';
123
+ }
124
+ const instructions = await sellAdapter.buildEstimationInstructions({
125
+ from: sendAddress,
126
+ to: depositAddress,
127
+ tokenId,
128
+ value: sellAmount,
129
+ });
130
+ const feeData = await sellAdapter.getFeeData({
131
+ to: depositAddress,
132
+ value: sellAmount,
133
+ chainSpecific: {
134
+ from: sendAddress,
135
+ tokenId,
136
+ instructions,
137
+ },
138
+ sendMax: false,
139
+ });
140
+ const txFee = feeData.fast.txFee;
141
+ const ataCreationCost = calculateAccountCreationCosts(instructions);
142
+ return bn(txFee).plus(ataCreationCost).toString();
143
+ }
144
+ catch (error) {
145
+ return '0';
146
+ }
147
+ }
148
+ default:
149
+ return undefined;
150
+ }
151
+ };
152
+ const networkFeeCryptoBaseUnit = (await getFeeData()) || '0';
153
+ const rate = getInputOutputRate({
154
+ sellAmountCryptoBaseUnit: quote.amountIn,
155
+ buyAmountCryptoBaseUnit: quote.amountOut,
156
+ sellAsset,
157
+ buyAsset,
158
+ });
159
+ const tradeRate = {
160
+ id: uuid(),
161
+ receiveAddress: receiveAddress ?? undefined,
162
+ affiliateBps,
163
+ rate,
164
+ slippageTolerancePercentageDecimal: slippageTolerancePercentageDecimal ??
165
+ getDefaultSlippageDecimalPercentageForSwapper(SwapperName.NearIntents),
166
+ quoteOrRate: 'rate',
167
+ swapperName: SwapperName.NearIntents,
168
+ steps: [
169
+ {
170
+ accountNumber: undefined,
171
+ allowanceContract: '',
172
+ buyAmountBeforeFeesCryptoBaseUnit: quote.amountOut,
173
+ buyAmountAfterFeesCryptoBaseUnit: quote.amountOut,
174
+ buyAsset,
175
+ feeData: {
176
+ protocolFees: {},
177
+ networkFeeCryptoBaseUnit,
178
+ },
179
+ rate,
180
+ sellAmountIncludingProtocolFeesCryptoBaseUnit: quote.amountIn,
181
+ sellAsset,
182
+ source: SwapperName.NearIntents,
183
+ estimatedExecutionTimeMs: quote.timeEstimate ? quote.timeEstimate * 1000 : undefined,
184
+ },
185
+ ],
186
+ };
187
+ return Ok([tradeRate]);
188
+ }
189
+ catch (error) {
190
+ if (error instanceof ApiError &&
191
+ (error.body?.message === 'tokenIn is not valid' ||
192
+ error.body?.message === 'tokenOut is not valid')) {
193
+ return Err(makeSwapErrorRight({
194
+ code: TradeQuoteError.UnsupportedTradePair,
195
+ message: 'Unsupported asset',
196
+ }));
197
+ }
198
+ return Err(makeSwapErrorRight({
199
+ message: error instanceof Error ? error.message : 'Unknown error getting NEAR Intents rate',
200
+ code: TradeQuoteError.QueryFailed,
201
+ cause: error,
202
+ }));
203
+ }
204
+ };
@@ -0,0 +1,6 @@
1
+ import { KnownChainIds } from '@shapeshiftoss/types';
2
+ export { QuoteRequest } from '@defuse-protocol/one-click-sdk-typescript';
3
+ export type { QuoteResponse, GetExecutionStatusResponse, TokenResponse, } from '@defuse-protocol/one-click-sdk-typescript';
4
+ export declare const nearIntentsSupportedChainIds: readonly [KnownChainIds.EthereumMainnet, KnownChainIds.ArbitrumMainnet, KnownChainIds.BaseMainnet, KnownChainIds.GnosisMainnet, KnownChainIds.BnbSmartChainMainnet, KnownChainIds.PolygonMainnet, KnownChainIds.AvalancheMainnet, KnownChainIds.OptimismMainnet, KnownChainIds.BitcoinMainnet, KnownChainIds.DogecoinMainnet, KnownChainIds.SolanaMainnet];
5
+ export type NearIntentsSupportedChainId = (typeof nearIntentsSupportedChainIds)[number];
6
+ export declare const chainIdToNearIntentsChain: Record<NearIntentsSupportedChainId, string>;