@shapeshiftoss/swapper 20.0.0 → 20.1.1

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 (99) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +5 -1
  3. package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeRate.js +11 -2
  4. package/dist/cjs/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.d.ts +2 -1
  5. package/dist/cjs/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.js +2 -1
  6. package/dist/cjs/swappers/BobGatewaySwapper/utils/helpers.d.ts +2 -1
  7. package/dist/cjs/swappers/BobGatewaySwapper/utils/helpers.js +2 -1
  8. package/dist/cjs/swappers/ChainflipSwapper/ChainflipSwapper.js +1 -0
  9. package/dist/cjs/swappers/ChainflipSwapper/checkTradeStatus.test.d.ts +1 -0
  10. package/dist/cjs/swappers/ChainflipSwapper/checkTradeStatus.test.js +48 -0
  11. package/dist/cjs/swappers/ChainflipSwapper/endpoints.js +5 -2
  12. package/dist/cjs/swappers/ChainflipSwapper/types.d.ts +3 -0
  13. package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +1 -0
  14. package/dist/cjs/swappers/NearIntentsSwapper/checkTradeStatus.test.d.ts +1 -0
  15. package/dist/cjs/swappers/NearIntentsSwapper/checkTradeStatus.test.js +52 -0
  16. package/dist/cjs/swappers/NearIntentsSwapper/constants.d.ts +1 -1
  17. package/dist/cjs/swappers/NearIntentsSwapper/constants.js +4 -3
  18. package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +2 -1
  19. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers.d.ts +2 -2
  20. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers.js +3 -3
  21. package/dist/cjs/swappers/NearIntentsSwapper/utils/quoteDeadline.test.d.ts +1 -0
  22. package/dist/cjs/swappers/NearIntentsSwapper/utils/quoteDeadline.test.js +28 -0
  23. package/dist/cjs/swappers/PortalsSwapper/endpoints.js +51 -47
  24. package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.js +1 -1
  25. package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +1 -1
  26. package/dist/cjs/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.d.ts +0 -1
  27. package/dist/cjs/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.js +3 -6
  28. package/dist/cjs/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.js +6 -3
  29. package/dist/cjs/swappers/PortalsSwapper/utils/fetchSquidStatus.d.ts +0 -1
  30. package/dist/cjs/swappers/PortalsSwapper/utils/fetchSquidStatus.js +1 -8
  31. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.js +13 -5
  32. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.test.d.ts +1 -0
  33. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.test.js +70 -0
  34. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.d.ts +2 -2
  35. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.js +27 -15
  36. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.d.ts +1 -0
  37. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.js +93 -0
  38. package/dist/cjs/swappers/RelaySwapper/endpoints.js +3 -18
  39. package/dist/cjs/swappers/RelaySwapper/utils/relayStatus.d.ts +8 -0
  40. package/dist/cjs/swappers/RelaySwapper/utils/relayStatus.js +39 -0
  41. package/dist/cjs/swappers/RelaySwapper/utils/types.d.ts +14 -0
  42. package/dist/cjs/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +1 -0
  43. package/dist/cjs/swappers/ZrxSwapper/utils/fetchFromZrx.js +1 -1
  44. package/dist/cjs/types.d.ts +2 -0
  45. package/dist/cjs/utils/cowswap/index.d.ts +4 -1
  46. package/dist/cjs/utils/cowswap/index.js +5 -1
  47. package/dist/cjs/utils/helpers.d.ts +4 -1
  48. package/dist/cjs/utils/helpers.js +19 -1
  49. package/dist/cjs/utils/helpers.test.js +41 -0
  50. package/dist/esm/.tsbuildinfo +1 -1
  51. package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +5 -1
  52. package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeRate.js +11 -2
  53. package/dist/esm/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.d.ts +2 -1
  54. package/dist/esm/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.js +2 -1
  55. package/dist/esm/swappers/BobGatewaySwapper/utils/helpers.d.ts +2 -1
  56. package/dist/esm/swappers/BobGatewaySwapper/utils/helpers.js +2 -1
  57. package/dist/esm/swappers/ChainflipSwapper/ChainflipSwapper.js +1 -0
  58. package/dist/esm/swappers/ChainflipSwapper/checkTradeStatus.test.d.ts +1 -0
  59. package/dist/esm/swappers/ChainflipSwapper/checkTradeStatus.test.js +46 -0
  60. package/dist/esm/swappers/ChainflipSwapper/endpoints.js +5 -2
  61. package/dist/esm/swappers/ChainflipSwapper/types.d.ts +3 -0
  62. package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +1 -0
  63. package/dist/esm/swappers/NearIntentsSwapper/checkTradeStatus.test.d.ts +1 -0
  64. package/dist/esm/swappers/NearIntentsSwapper/checkTradeStatus.test.js +50 -0
  65. package/dist/esm/swappers/NearIntentsSwapper/constants.d.ts +1 -1
  66. package/dist/esm/swappers/NearIntentsSwapper/constants.js +3 -2
  67. package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +2 -1
  68. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers.d.ts +2 -2
  69. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers.js +5 -5
  70. package/dist/esm/swappers/NearIntentsSwapper/utils/quoteDeadline.test.d.ts +1 -0
  71. package/dist/esm/swappers/NearIntentsSwapper/utils/quoteDeadline.test.js +26 -0
  72. package/dist/esm/swappers/PortalsSwapper/endpoints.js +53 -49
  73. package/dist/esm/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.js +1 -1
  74. package/dist/esm/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +1 -1
  75. package/dist/esm/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.d.ts +0 -1
  76. package/dist/esm/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.js +2 -4
  77. package/dist/esm/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.js +6 -3
  78. package/dist/esm/swappers/PortalsSwapper/utils/fetchSquidStatus.d.ts +0 -1
  79. package/dist/esm/swappers/PortalsSwapper/utils/fetchSquidStatus.js +0 -6
  80. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.js +10 -2
  81. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.test.d.ts +1 -0
  82. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.test.js +68 -0
  83. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.d.ts +2 -2
  84. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.js +28 -16
  85. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.d.ts +1 -0
  86. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.js +91 -0
  87. package/dist/esm/swappers/RelaySwapper/endpoints.js +3 -18
  88. package/dist/esm/swappers/RelaySwapper/utils/relayStatus.d.ts +8 -0
  89. package/dist/esm/swappers/RelaySwapper/utils/relayStatus.js +32 -0
  90. package/dist/esm/swappers/RelaySwapper/utils/types.d.ts +14 -0
  91. package/dist/esm/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +1 -0
  92. package/dist/esm/swappers/ZrxSwapper/utils/fetchFromZrx.js +1 -1
  93. package/dist/esm/types.d.ts +2 -0
  94. package/dist/esm/utils/cowswap/index.d.ts +4 -1
  95. package/dist/esm/utils/cowswap/index.js +3 -0
  96. package/dist/esm/utils/helpers.d.ts +4 -1
  97. package/dist/esm/utils/helpers.js +17 -0
  98. package/dist/esm/utils/helpers.test.js +42 -1
  99. package/package.json +6 -6
@@ -13,14 +13,18 @@ const getBobGatewayTradeQuote = async (input, deps) => {
13
13
  if (maybeAddresses.isErr())
14
14
  return (0, monads_1.Err)(maybeAddresses.unwrapErr());
15
15
  const { sendAddress, receiveAddress } = maybeAddresses.unwrap();
16
+ const isEvmSell = (0, chain_adapters_1.isEvmChainId)(sellAsset.chainId);
16
17
  // omit the sender for utxo sells so order creation does not enforce a per-address confirmed
17
18
  // funds check (deposits are matched via op_return, not the sending address)
18
- const sender = (0, chain_adapters_1.isEvmChainId)(sellAsset.chainId) ? sendAddress : undefined;
19
+ const sender = isEvmSell ? sendAddress : undefined;
20
+ // utxo deposits are refunded on the sell chain, so refunds go to the sending address
21
+ const refundAddress = isEvmSell ? undefined : sendAddress;
19
22
  const maybeContext = await (0, getBobGatewayTradeContext_1.getBobGatewayTradeContext)({
20
23
  input,
21
24
  deps,
22
25
  sender,
23
26
  recipient: receiveAddress,
27
+ refundAddress,
24
28
  });
25
29
  if (maybeContext.isErr())
26
30
  return (0, monads_1.Err)(maybeContext.unwrapErr());
@@ -8,9 +8,18 @@ const getBobGatewayTradeContext_1 = require("../utils/getBobGatewayTradeContext"
8
8
  const helpers_1 = require("../utils/helpers");
9
9
  const getBobGatewayTradeRate = async (input, deps) => {
10
10
  const { accountNumber, sellAsset, buyAsset, receiveAddress } = input;
11
+ const isBtcSell = sellAsset.chainId === caip_1.btcChainId;
11
12
  const recipient = receiveAddress ?? (0, helpers_1.dummyAddressForChainId)(buyAsset.chainId);
12
- const sender = sellAsset.chainId === caip_1.btcChainId ? undefined : (0, helpers_1.dummyAddressForChainId)(sellAsset.chainId);
13
- const maybeContext = await (0, getBobGatewayTradeContext_1.getBobGatewayTradeContext)({ input, deps, sender, recipient });
13
+ const sender = isBtcSell ? undefined : (0, helpers_1.dummyAddressForChainId)(sellAsset.chainId);
14
+ // utxo deposits are refunded on the sell chain, so the refund address must be a btc address
15
+ const refundAddress = isBtcSell ? (0, helpers_1.dummyAddressForChainId)(sellAsset.chainId) : undefined;
16
+ const maybeContext = await (0, getBobGatewayTradeContext_1.getBobGatewayTradeContext)({
17
+ input,
18
+ deps,
19
+ sender,
20
+ recipient,
21
+ refundAddress,
22
+ });
14
23
  if (maybeContext.isErr())
15
24
  return (0, monads_1.Err)(maybeContext.unwrapErr());
16
25
  const { tradeCommon, stepCommon, protocolFees, stepDataArgs } = maybeContext.unwrap();
@@ -8,10 +8,11 @@ type BobGatewayTradeContext = {
8
8
  protocolFees: QuoteFeeData['protocolFees'];
9
9
  stepDataArgs: Omit<GetBobGatewayStepDataArgs, 'type' | 'input' | 'from'>;
10
10
  };
11
- export declare const getBobGatewayTradeContext: ({ input, deps, sender, recipient, }: {
11
+ export declare const getBobGatewayTradeContext: ({ input, deps, sender, recipient, refundAddress, }: {
12
12
  input: BobGatewayTradeQuoteInput | BobGatewayTradeRateInput;
13
13
  deps: SwapperDeps;
14
14
  sender: string | undefined;
15
15
  recipient: string;
16
+ refundAddress: string | undefined;
16
17
  }) => Promise<Result<BobGatewayTradeContext, SwapErrorRight>>;
17
18
  export {};
@@ -7,7 +7,7 @@ const uuid_1 = require("uuid");
7
7
  const types_1 = require("../../../types");
8
8
  const utils_1 = require("../../../utils");
9
9
  const helpers_1 = require("./helpers");
10
- const getBobGatewayTradeContext = async ({ input, deps, sender, recipient, }) => {
10
+ const getBobGatewayTradeContext = async ({ input, deps, sender, recipient, refundAddress, }) => {
11
11
  const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit, affiliateBps, slippageTolerancePercentageDecimal, } = input;
12
12
  const assertion = (0, helpers_1.assertValidTrade)({ sellAsset, buyAsset });
13
13
  if (assertion.isErr())
@@ -21,6 +21,7 @@ const getBobGatewayTradeContext = async ({ input, deps, sender, recipient, }) =>
21
21
  buyChainName,
22
22
  sender,
23
23
  recipient,
24
+ refundAddress,
24
25
  amount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
25
26
  affiliateBps,
26
27
  slippageTolerancePercentageDecimal,
@@ -11,7 +11,7 @@ export declare const getBobGatewayClient: (config: SwapperConfig) => GatewaySDK;
11
11
  export declare const toTronBase58: (address: string) => string;
12
12
  export declare const assetIdToBobGatewayToken: (assetId: string) => string;
13
13
  export declare const getBobGatewayAffiliates: (affiliateBps: string) => GetQuoteParams['affiliates'];
14
- export declare const getBobGatewayQuote: ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, amount, affiliateBps, slippageTolerancePercentageDecimal, }: {
14
+ export declare const getBobGatewayQuote: ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, refundAddress, amount, affiliateBps, slippageTolerancePercentageDecimal, }: {
15
15
  config: SwapperConfig;
16
16
  sellAsset: Asset;
17
17
  buyAsset: Asset;
@@ -19,6 +19,7 @@ export declare const getBobGatewayQuote: ({ config, sellAsset, buyAsset, sellCha
19
19
  buyChainName: BobGatewayChainName;
20
20
  sender: string | undefined;
21
21
  recipient: string;
22
+ refundAddress: string | undefined;
22
23
  amount: string;
23
24
  affiliateBps: string;
24
25
  slippageTolerancePercentageDecimal: string | undefined;
@@ -49,7 +49,7 @@ const getBobGatewayAffiliates = (affiliateBps) => {
49
49
  return [{ address: (0, viem_1.getAddress)(affiliateAddress), bps: bps.toNumber() }];
50
50
  };
51
51
  exports.getBobGatewayAffiliates = getBobGatewayAffiliates;
52
- const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, amount, affiliateBps, slippageTolerancePercentageDecimal, }) => {
52
+ const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, refundAddress, amount, affiliateBps, slippageTolerancePercentageDecimal, }) => {
53
53
  const slippage = (0, constants_2.decimalSlippageToBobBps)(slippageTolerancePercentageDecimal ??
54
54
  (0, constants_1.getDefaultSlippageDecimalPercentageForSwapper)(types_1.SwapperName.BobGateway));
55
55
  try {
@@ -63,6 +63,7 @@ const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChainName,
63
63
  amount,
64
64
  maxSlippage: Number(slippage),
65
65
  ownerAddress: sellChainName === 'bitcoin' ? recipient : sender,
66
+ refundAddress,
66
67
  affiliates: (0, exports.getBobGatewayAffiliates)(affiliateBps),
67
68
  });
68
69
  return (0, monads_1.Ok)(quote);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.chainflipSwapper = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.chainflipSwapper = {
6
+ supportsExternalPayment: true,
6
7
  executeEvmTransaction: utils_1.executeEvmTransaction,
7
8
  executeSolanaTransaction: utils_1.executeSolanaTransaction,
8
9
  executeTronTransaction: utils_1.executeTronTransaction,
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const monads_1 = require("@sniptt/monads");
4
+ const vitest_1 = require("vitest");
5
+ const endpoints_1 = require("./endpoints");
6
+ const chainflipService_1 = require("./utils/chainflipService");
7
+ vitest_1.vi.mock('./utils/chainflipService', () => ({
8
+ chainflipService: { get: vitest_1.vi.fn() },
9
+ }));
10
+ const makeInput = () => ({
11
+ config: { VITE_CHAINFLIP_API_URL: 'https://broker', VITE_CHAINFLIP_API_KEY: 'key' },
12
+ swap: { metadata: { swapperMetadata: { name: 'chainflip', swapId: 1234 } } },
13
+ });
14
+ const mockStatus = (status) => vitest_1.vi.mocked(chainflipService_1.chainflipService.get).mockResolvedValue((0, monads_1.Ok)({ data: { status } }));
15
+ (0, vitest_1.describe)('chainflip checkTradeStatus', () => {
16
+ (0, vitest_1.beforeEach)(() => {
17
+ vitest_1.vi.clearAllMocks();
18
+ });
19
+ (0, vitest_1.it)('reports the deposit tx hash while the swap is still pending', async () => {
20
+ mockStatus({
21
+ state: 'swapping',
22
+ swapId: '1234',
23
+ deposit: { transactionReference: '0xdeposit' },
24
+ });
25
+ const result = await endpoints_1.chainflipApi.checkTradeStatus(makeInput());
26
+ (0, vitest_1.expect)(result.sellTxHash).toBe('0xdeposit');
27
+ (0, vitest_1.expect)(result.buyTxHash).toBeUndefined();
28
+ });
29
+ (0, vitest_1.it)('reports the deposit tx hash alongside a completed egress', async () => {
30
+ mockStatus({
31
+ state: 'completed',
32
+ swapId: '1234',
33
+ deposit: { transactionReference: '0xdeposit' },
34
+ swapEgress: { transactionReference: '0xegress' },
35
+ });
36
+ const result = await endpoints_1.chainflipApi.checkTradeStatus(makeInput());
37
+ (0, vitest_1.expect)(result.sellTxHash).toBe('0xdeposit');
38
+ (0, vitest_1.expect)(result.buyTxHash).toBe('0xegress');
39
+ });
40
+ (0, vitest_1.it)('has no deposit tx hash before the deposit is witnessed', async () => {
41
+ mockStatus({ state: 'waiting', swapId: '1234' });
42
+ (0, vitest_1.expect)((await endpoints_1.chainflipApi.checkTradeStatus(makeInput())).sellTxHash).toBeUndefined();
43
+ });
44
+ (0, vitest_1.it)('treats a null transaction reference as no deposit yet', async () => {
45
+ mockStatus({ state: 'receiving', swapId: '1234', deposit: { transactionReference: null } });
46
+ (0, vitest_1.expect)((await endpoints_1.chainflipApi.checkTradeStatus(makeInput())).sellTxHash).toBeUndefined();
47
+ });
48
+ });
@@ -55,13 +55,15 @@ exports.chainflipApi = {
55
55
  };
56
56
  }
57
57
  const { data: statusResponse } = maybeStatusResponse.unwrap();
58
- const buyTxHash = statusResponse.status.swapEgress?.transactionReference;
58
+ const buyTxHash = statusResponse.status.swapEgress?.transactionReference ?? undefined;
59
+ const sellTxHash = statusResponse.status.deposit?.transactionReference ?? undefined;
59
60
  const swapperTxId = statusResponse.status.swapId;
60
61
  const swapperTxLink = swapperTxId ? `https://scan.chainflip.io/swaps/${swapperTxId}` : undefined;
61
62
  // Assume no outbound Tx is a pending Tx
62
63
  if (!buyTxHash) {
63
64
  return {
64
- buyTxHash: undefined,
65
+ buyTxHash,
66
+ sellTxHash,
65
67
  status: unchained_client_1.TxStatus.Pending,
66
68
  swapperTxId,
67
69
  swapperTxLink,
@@ -72,6 +74,7 @@ exports.chainflipApi = {
72
74
  // Chainflip waits for 3 confirmations to assume complete (vs. 1 for us), which is turbo long.
73
75
  return {
74
76
  buyTxHash,
77
+ sellTxHash,
75
78
  status: unchained_client_1.TxStatus.Confirmed,
76
79
  swapperTxId,
77
80
  swapperTxLink,
@@ -11,6 +11,9 @@ export type ChainFlipStatus = {
11
11
  status: {
12
12
  state: 'waiting' | 'receiving' | 'swapping' | 'sending' | 'sent' | 'completed' | 'failed';
13
13
  swapId?: string;
14
+ deposit?: {
15
+ transactionReference?: string | null;
16
+ };
14
17
  swap?: ChainflipBaasStatusSwap;
15
18
  swapEgress?: ChainflipBaasStatusEgress;
16
19
  };
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.nearIntentsSwapper = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.nearIntentsSwapper = {
6
+ supportsExternalPayment: true,
6
7
  executeEvmTransaction: utils_1.executeEvmTransaction,
7
8
  executeSolanaTransaction: utils_1.executeSolanaTransaction,
8
9
  executeStarknetTransaction: utils_1.executeStarknetTransaction,
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const endpoints_1 = require("./endpoints");
5
+ const oneClickService_1 = require("./utils/oneClickService");
6
+ vitest_1.vi.mock('./utils/oneClickService', () => ({
7
+ initializeOneClickService: vitest_1.vi.fn(),
8
+ OneClickService: { getExecutionStatus: vitest_1.vi.fn() },
9
+ }));
10
+ const makeInput = () => ({
11
+ config: { VITE_NEAR_INTENTS_API_KEY: 'key' },
12
+ swap: {
13
+ metadata: { swapperMetadata: { name: 'nearIntents', depositAddress: '0xdepositaddress' } },
14
+ },
15
+ });
16
+ const mockExecutionStatus = (response) => vitest_1.vi.mocked(oneClickService_1.OneClickService.getExecutionStatus).mockResolvedValue(response);
17
+ (0, vitest_1.describe)('near intents checkTradeStatus', () => {
18
+ (0, vitest_1.beforeEach)(() => {
19
+ vitest_1.vi.clearAllMocks();
20
+ });
21
+ (0, vitest_1.it)('reports the origin chain deposit tx hash', async () => {
22
+ mockExecutionStatus({
23
+ status: 'PROCESSING',
24
+ swapDetails: {
25
+ originChainTxHashes: [{ hash: '0xdeposit', explorerUrl: '' }],
26
+ destinationChainTxHashes: [],
27
+ },
28
+ });
29
+ const result = await endpoints_1.nearIntentsApi.checkTradeStatus(makeInput());
30
+ (0, vitest_1.expect)(result.sellTxHash).toBe('0xdeposit');
31
+ (0, vitest_1.expect)(result.buyTxHash).toBeUndefined();
32
+ });
33
+ (0, vitest_1.it)('reports both sides once the swap settles', async () => {
34
+ mockExecutionStatus({
35
+ status: 'SUCCESS',
36
+ swapDetails: {
37
+ originChainTxHashes: [{ hash: '0xdeposit', explorerUrl: '' }],
38
+ destinationChainTxHashes: [{ hash: '0xsettlement', explorerUrl: '' }],
39
+ },
40
+ });
41
+ const result = await endpoints_1.nearIntentsApi.checkTradeStatus(makeInput());
42
+ (0, vitest_1.expect)(result.sellTxHash).toBe('0xdeposit');
43
+ (0, vitest_1.expect)(result.buyTxHash).toBe('0xsettlement');
44
+ });
45
+ (0, vitest_1.it)('has no deposit tx hash before the deposit lands', async () => {
46
+ mockExecutionStatus({
47
+ status: 'PENDING_DEPOSIT',
48
+ swapDetails: { originChainTxHashes: [], destinationChainTxHashes: [] },
49
+ });
50
+ (0, vitest_1.expect)((await endpoints_1.nearIntentsApi.checkTradeStatus(makeInput())).sellTxHash).toBeUndefined();
51
+ });
52
+ });
@@ -1,4 +1,4 @@
1
1
  export declare const ONE_CLICK_BASE_URL = "https://1click.chaindefuser.com";
2
2
  export declare const DEFAULT_SLIPPAGE_BPS = 50;
3
3
  export declare const DEFAULT_QUOTE_DEADLINE_MS: number;
4
- export declare const BTC_QUOTE_DEADLINE_MS: number;
4
+ export declare const UTXO_QUOTE_DEADLINE_MS: number;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BTC_QUOTE_DEADLINE_MS = exports.DEFAULT_QUOTE_DEADLINE_MS = exports.DEFAULT_SLIPPAGE_BPS = exports.ONE_CLICK_BASE_URL = void 0;
3
+ exports.UTXO_QUOTE_DEADLINE_MS = exports.DEFAULT_QUOTE_DEADLINE_MS = exports.DEFAULT_SLIPPAGE_BPS = exports.ONE_CLICK_BASE_URL = void 0;
4
4
  exports.ONE_CLICK_BASE_URL = 'https://1click.chaindefuser.com';
5
5
  exports.DEFAULT_SLIPPAGE_BPS = 50;
6
- exports.DEFAULT_QUOTE_DEADLINE_MS = 30 * 60 * 1000;
7
- exports.BTC_QUOTE_DEADLINE_MS = 60 * 60 * 1000;
6
+ // 1Click refunds a deposit it cannot settle by the requested deadline, and prices a quote the same at any length
7
+ exports.DEFAULT_QUOTE_DEADLINE_MS = 60 * 60 * 1000;
8
+ exports.UTXO_QUOTE_DEADLINE_MS = 4 * 60 * 60 * 1000;
@@ -166,14 +166,15 @@ exports.nearIntentsApi = {
166
166
  const statusResponse = await oneClickService_1.OneClickService.getExecutionStatus(depositAddress);
167
167
  const txStatus = (0, helpers_1.mapNearIntentsStatus)(statusResponse.status);
168
168
  const message = (0, helpers_1.getNearIntentsStatusMessage)(statusResponse.status);
169
- // Extract buyTxHash from destination chain transactions
170
169
  const buyTxHash = statusResponse.swapDetails?.destinationChainTxHashes?.[0]?.hash;
170
+ const sellTxHash = statusResponse.swapDetails?.originChainTxHashes?.[0]?.hash;
171
171
  // amountOut is only meaningful destination-denominated on terminal success - in-flight and
172
172
  // refund states may carry settlement-internal or refund values
173
173
  const actualBuyAmountCryptoBaseUnit = statusResponse.status === 'SUCCESS' ? statusResponse.swapDetails?.amountOut : undefined;
174
174
  return {
175
175
  status: txStatus,
176
176
  buyTxHash,
177
+ sellTxHash,
177
178
  swapperTxLink: `https://explorer.near-intents.org/transactions/${depositAddress}`,
178
179
  message,
179
180
  actualBuyAmountCryptoBaseUnit,
@@ -17,8 +17,8 @@ export declare const resolveNearIntentsAssets: ({ sellAsset, buyAsset, }: {
17
17
  destinationAsset: string;
18
18
  }, SwapErrorRight>>;
19
19
  export declare const getNearIntentsQuoteDeadline: ({ sellAsset, buyAsset, }: {
20
- sellAsset: Asset;
21
- buyAsset: Asset;
20
+ sellAsset: Pick<Asset, 'chainId'>;
21
+ buyAsset: Pick<Asset, 'chainId'>;
22
22
  }) => string;
23
23
  export declare const buildNearIntentsQuoteRequest: ({ originAsset, destinationAsset, amount, slippageTolerancePercentageDecimal, affiliateBps, refundTo, recipient, refundType, recipientType, deadline, }: {
24
24
  originAsset: string;
@@ -126,10 +126,10 @@ const resolveNearIntentsAssets = async ({ sellAsset, buyAsset, }) => {
126
126
  }
127
127
  };
128
128
  exports.resolveNearIntentsAssets = resolveNearIntentsAssets;
129
- // BTC deposits confirm slowly, so BTC pairs get a longer deadline
129
+ const isUtxoChainId = (chainId) => (0, caip_1.fromChainId)(chainId).chainNamespace === caip_1.CHAIN_NAMESPACE.Utxo;
130
130
  const getNearIntentsQuoteDeadline = ({ sellAsset, buyAsset, }) => {
131
- const deadlineMs = sellAsset.chainId === caip_1.btcChainId || buyAsset.chainId === caip_1.btcChainId
132
- ? constants_1.BTC_QUOTE_DEADLINE_MS
131
+ const deadlineMs = isUtxoChainId(sellAsset.chainId) || isUtxoChainId(buyAsset.chainId)
132
+ ? constants_1.UTXO_QUOTE_DEADLINE_MS
133
133
  : constants_1.DEFAULT_QUOTE_DEADLINE_MS;
134
134
  return new Date(Date.now() + deadlineMs).toISOString();
135
135
  };
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const caip_1 = require("@shapeshiftoss/caip");
4
+ const vitest_1 = require("vitest");
5
+ const constants_1 = require("../constants");
6
+ const helpers_1 = require("./helpers");
7
+ const asset = (chainId) => ({ chainId });
8
+ (0, vitest_1.describe)('getNearIntentsQuoteDeadline', () => {
9
+ (0, vitest_1.beforeEach)(() => {
10
+ vitest_1.vi.useFakeTimers();
11
+ vitest_1.vi.setSystemTime(new Date('2026-09-10T22:00:00.000Z'));
12
+ });
13
+ (0, vitest_1.afterEach)(() => {
14
+ vitest_1.vi.useRealTimers();
15
+ });
16
+ (0, vitest_1.it)('gives a utxo sell leg the long deadline', () => {
17
+ (0, vitest_1.expect)(Date.parse((0, helpers_1.getNearIntentsQuoteDeadline)({ sellAsset: asset(caip_1.btcChainId), buyAsset: asset(caip_1.ethChainId) }))).toBe(Date.now() + constants_1.UTXO_QUOTE_DEADLINE_MS);
18
+ });
19
+ (0, vitest_1.it)('gives a utxo buy leg the long deadline, on any utxo chain', () => {
20
+ (0, vitest_1.expect)(Date.parse((0, helpers_1.getNearIntentsQuoteDeadline)({ sellAsset: asset(caip_1.ethChainId), buyAsset: asset(caip_1.dogeChainId) }))).toBe(Date.now() + constants_1.UTXO_QUOTE_DEADLINE_MS);
21
+ });
22
+ (0, vitest_1.it)('gives everything else the default deadline', () => {
23
+ (0, vitest_1.expect)(Date.parse((0, helpers_1.getNearIntentsQuoteDeadline)({
24
+ sellAsset: asset(caip_1.ethChainId),
25
+ buyAsset: asset(caip_1.solanaChainId),
26
+ }))).toBe(Date.now() + constants_1.DEFAULT_QUOTE_DEADLINE_MS);
27
+ });
28
+ });
@@ -4,17 +4,29 @@ exports.portalsApi = void 0;
4
4
  const unchained_client_1 = require("@shapeshiftoss/unchained-client");
5
5
  const utils_1 = require("../../utils");
6
6
  const evm_1 = require("../../utils/evm");
7
+ const relayStatus_1 = require("../RelaySwapper/utils/relayStatus");
7
8
  const getPortalsTradeQuote_1 = require("./getPortalsTradeQuote/getPortalsTradeQuote");
8
9
  const getPortalsTradeRate_1 = require("./getPortalsTradeRate/getPortalsTradeRate");
9
10
  const fetchAxelarscanStatus_1 = require("./utils/fetchAxelarscanStatus");
10
11
  const fetchSquidStatus_1 = require("./utils/fetchSquidStatus");
12
+ const bridgeStatusToTxStatus = (status) => {
13
+ switch (status) {
14
+ case 'confirmed':
15
+ return unchained_client_1.TxStatus.Confirmed;
16
+ case 'failed':
17
+ return unchained_client_1.TxStatus.Failed;
18
+ case 'pending':
19
+ default:
20
+ return unchained_client_1.TxStatus.Pending;
21
+ }
22
+ };
11
23
  exports.portalsApi = {
12
24
  getTradeQuote: (input, deps) => (0, getPortalsTradeQuote_1.getPortalsTradeQuote)(input, deps),
13
25
  getTradeRate: (input, deps) => (0, getPortalsTradeRate_1.getPortalsTradeRate)(input, deps),
14
26
  getEvmTransactionFees: evm_1.getEvmTransactionFees,
15
27
  getUnsignedEvmTransaction: evm_1.getUnsignedEvmTransaction,
16
28
  checkTradeStatus: async (input) => {
17
- const { txHash, chainId, swap, assertGetEvmChainAdapter, address, fetchIsSmartContractAddressQuery, } = input;
29
+ const { txHash, chainId, swap, config, assertGetEvmChainAdapter, address, fetchIsSmartContractAddressQuery, } = input;
18
30
  const isCrossChain = Boolean(swap && swap.sellAsset.chainId !== swap.buyAsset?.chainId);
19
31
  if (!isCrossChain) {
20
32
  return (0, utils_1.checkEvmSwapStatus)({
@@ -42,64 +54,56 @@ exports.portalsApi = {
42
54
  if (sourceTxStatus.status === unchained_client_1.TxStatus.Failed) {
43
55
  return sourceTxStatus;
44
56
  }
57
+ const relayResult = await (0, relayStatus_1.fetchRelayRequestByTxHash)(txHash, config);
58
+ if (relayResult.isOk()) {
59
+ const relayRequest = relayResult.unwrap();
60
+ if (relayRequest) {
61
+ const relayTxStatus = (0, relayStatus_1.relayStatusToTxStatus)(relayRequest.status);
62
+ const txStatus = relayTxStatus === unchained_client_1.TxStatus.Unknown ? unchained_client_1.TxStatus.Pending : relayTxStatus;
63
+ return {
64
+ status: txStatus,
65
+ buyTxHash: relayRequest.data?.outTxs?.[0]?.hash,
66
+ message: txStatus === unchained_client_1.TxStatus.Failed
67
+ ? (0, relayStatus_1.getRelayRequestFailureMessage)(relayRequest)
68
+ : txStatus === unchained_client_1.TxStatus.Confirmed
69
+ ? undefined
70
+ : 'Bridge in progress',
71
+ };
72
+ }
73
+ }
45
74
  const axelarscanResult = await (0, fetchAxelarscanStatus_1.fetchAxelarscanBridgeStatus)(txHash);
46
- if ((axelarscanResult.isErr() || !axelarscanResult.unwrap()) && swap) {
75
+ if (axelarscanResult.isOk()) {
76
+ const bridgeStatus = axelarscanResult.unwrap();
77
+ if (bridgeStatus) {
78
+ const txStatus = bridgeStatusToTxStatus(bridgeStatus.status);
79
+ return {
80
+ status: txStatus,
81
+ buyTxHash: bridgeStatus.destinationTxHash,
82
+ message: txStatus === unchained_client_1.TxStatus.Pending
83
+ ? 'Bridge in progress'
84
+ : txStatus === unchained_client_1.TxStatus.Failed
85
+ ? bridgeStatus.errorMessage
86
+ : undefined,
87
+ };
88
+ }
89
+ }
90
+ if (swap) {
47
91
  const squidResult = await (0, fetchSquidStatus_1.fetchSquidBridgeStatus)(txHash, swap.sellAsset.chainId, swap.buyAsset.chainId);
48
92
  if (squidResult.isOk()) {
49
93
  const squidStatus = squidResult.unwrap();
50
- const squidTxStatus = (() => {
51
- switch (squidStatus.status) {
52
- case 'confirmed':
53
- return unchained_client_1.TxStatus.Confirmed;
54
- case 'failed':
55
- return unchained_client_1.TxStatus.Failed;
56
- case 'pending':
57
- default:
58
- return unchained_client_1.TxStatus.Pending;
59
- }
60
- })();
94
+ const squidTxStatus = bridgeStatusToTxStatus(squidStatus.status);
61
95
  return {
62
96
  status: squidTxStatus,
63
97
  buyTxHash: squidStatus.destinationTxHash,
64
- swapperTxLink: (0, fetchSquidStatus_1.getSquidTrackingLink)(txHash, squidStatus, swap.sellAsset.explorerTxLink, swap.buyAsset.explorerTxLink),
65
98
  message: squidTxStatus === unchained_client_1.TxStatus.Pending ? 'Cross-chain swap in progress' : undefined,
66
99
  };
67
100
  }
68
- return {
69
- status: unchained_client_1.TxStatus.Pending,
70
- buyTxHash: undefined,
71
- swapperTxLink: (0, fetchAxelarscanStatus_1.getAxelarscanTrackingLink)(txHash),
72
- message: 'Bridge status check failed - track manually',
73
- };
74
101
  }
75
- const bridgeStatus = axelarscanResult.unwrap();
76
- if (!bridgeStatus) {
77
- return {
78
- status: unchained_client_1.TxStatus.Pending,
79
- buyTxHash: undefined,
80
- message: 'Cross-chain swap in progress',
81
- };
82
- }
83
- const txStatus = (() => {
84
- switch (bridgeStatus.status) {
85
- case 'confirmed':
86
- return unchained_client_1.TxStatus.Confirmed;
87
- case 'failed':
88
- return unchained_client_1.TxStatus.Failed;
89
- case 'pending':
90
- default:
91
- return unchained_client_1.TxStatus.Pending;
92
- }
93
- })();
102
+ // No indexer knows the tx yet - the bridge may not have picked it up, so keep polling
94
103
  return {
95
- status: txStatus,
96
- buyTxHash: bridgeStatus.destinationTxHash,
97
- swapperTxLink: (0, fetchAxelarscanStatus_1.getAxelarscanTrackingLink)(txHash),
98
- message: txStatus === unchained_client_1.TxStatus.Pending
99
- ? 'Bridge in progress'
100
- : txStatus === unchained_client_1.TxStatus.Failed
101
- ? bridgeStatus.errorMessage
102
- : undefined,
104
+ status: unchained_client_1.TxStatus.Pending,
105
+ buyTxHash: undefined,
106
+ message: 'Cross-chain swap in progress',
103
107
  };
104
108
  },
105
109
  };
@@ -112,8 +112,8 @@ const getPortalsTradeQuote = async (input, deps) => {
112
112
  input,
113
113
  deps,
114
114
  sellChainId,
115
- inputToken,
116
115
  orderContext,
116
+ outputToken,
117
117
  tx,
118
118
  });
119
119
  if (maybeContext.isErr())
@@ -47,8 +47,8 @@ const getPortalsTradeRate = async (input, deps) => {
47
47
  input,
48
48
  deps,
49
49
  sellChainId,
50
- inputToken,
51
50
  orderContext,
51
+ outputToken,
52
52
  tx,
53
53
  });
54
54
  if (maybeContext.isErr())
@@ -6,4 +6,3 @@ export type AxelarscanBridgeStatus = {
6
6
  errorMessage?: string;
7
7
  };
8
8
  export declare const fetchAxelarscanBridgeStatus: (sourceTxHash: string) => Promise<Result<AxelarscanBridgeStatus | undefined, SwapErrorRight>>;
9
- export declare const getAxelarscanTrackingLink: (sourceTxHash: string) => string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAxelarscanTrackingLink = exports.fetchAxelarscanBridgeStatus = void 0;
3
+ exports.fetchAxelarscanBridgeStatus = void 0;
4
4
  const monads_1 = require("@sniptt/monads");
5
5
  const utils_1 = require("../../../utils");
6
6
  const fetchAxelarscanBridgeStatus = async (sourceTxHash) => {
@@ -16,9 +16,8 @@ const fetchAxelarscanBridgeStatus = async (sourceTxHash) => {
16
16
  return (0, monads_1.Ok)(undefined);
17
17
  }
18
18
  const bridgeData = data.data[0];
19
- const destinationTxHash = bridgeData.call?.transaction_hash ||
20
- bridgeData.executed?.transaction_hash ||
21
- bridgeData.express_executed?.transactionHash;
19
+ // `call` is the origin call, so only the executed hashes identify the destination leg
20
+ const destinationTxHash = bridgeData.executed?.transaction_hash || bridgeData.express_executed?.transactionHash;
22
21
  const isCompleted = bridgeData.status === 'executed' ||
23
22
  bridgeData.status === 'destination_executed' ||
24
23
  bridgeData.status === 'express_executed' ||
@@ -48,5 +47,3 @@ const fetchAxelarscanBridgeStatus = async (sourceTxHash) => {
48
47
  }
49
48
  };
50
49
  exports.fetchAxelarscanBridgeStatus = fetchAxelarscanBridgeStatus;
51
- const getAxelarscanTrackingLink = (sourceTxHash) => `https://axelarscan.io/gmp/${sourceTxHash}`;
52
- exports.getAxelarscanTrackingLink = getAxelarscanTrackingLink;
@@ -23,9 +23,11 @@ const fetchPortalsTradeOrder = async ({ sender, inputToken, inputAmount, outputT
23
23
  validate: validate.toString(),
24
24
  });
25
25
  if (!autoSlippage) {
26
- params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2)); // Portals API expects a string with at most 2 decimal places
26
+ // Portals API expects a string with at most 2 decimal places
27
+ params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2));
27
28
  }
28
- if (feePercentage) {
29
+ // Portals defaults to their own partner fee when this is omitted - always send it, zero included
30
+ if (feePercentage !== undefined) {
29
31
  params.append('feePercentage', feePercentage.toString());
30
32
  }
31
33
  try {
@@ -52,7 +54,8 @@ const fetchPortalsTradeEstimate = async ({ inputToken, inputAmount, outputToken,
52
54
  outputToken,
53
55
  });
54
56
  if (slippageTolerancePercentage !== undefined) {
55
- params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2)); // Portals API expects a string with at most 2 decimal places
57
+ // Portals API expects a string with at most 2 decimal places
58
+ params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2));
56
59
  }
57
60
  try {
58
61
  const response = await axios_1.default.get(url, { params });
@@ -6,4 +6,3 @@ export type SquidBridgeStatus = {
6
6
  destinationTxHash?: string;
7
7
  };
8
8
  export declare const fetchSquidBridgeStatus: (sourceTxHash: string, sourceChainId: ChainId, destinationChainId: ChainId) => Promise<Result<SquidBridgeStatus, SwapErrorRight>>;
9
- export declare const getSquidTrackingLink: (sourceTxHash: string, squidStatus: SquidBridgeStatus, sellAssetExplorerTxLink: string, buyAssetExplorerTxLink: string) => string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSquidTrackingLink = exports.fetchSquidBridgeStatus = void 0;
3
+ exports.fetchSquidBridgeStatus = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
5
  const monads_1 = require("@sniptt/monads");
6
6
  const utils_1 = require("../../../utils");
@@ -48,10 +48,3 @@ const fetchSquidBridgeStatus = async (sourceTxHash, sourceChainId, destinationCh
48
48
  }
49
49
  };
50
50
  exports.fetchSquidBridgeStatus = fetchSquidBridgeStatus;
51
- const getSquidTrackingLink = (sourceTxHash, squidStatus, sellAssetExplorerTxLink, buyAssetExplorerTxLink) => {
52
- if (squidStatus.status === 'confirmed' && squidStatus.destinationTxHash) {
53
- return `${buyAssetExplorerTxLink}${squidStatus.destinationTxHash}`;
54
- }
55
- return `${sellAssetExplorerTxLink}${sourceTxHash}`;
56
- };
57
- exports.getSquidTrackingLink = getSquidTrackingLink;