@shapeshiftoss/swapper 19.0.0 → 20.1.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 (165) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/exactOutputFiltering.test.d.ts +1 -0
  3. package/dist/cjs/exactOutputFiltering.test.js +70 -0
  4. package/dist/cjs/swapper.d.ts +3 -3
  5. package/dist/cjs/swapper.js +29 -6
  6. package/dist/cjs/swappers/AcrossSwapper/utils/getAcrossStepData.d.ts +1 -0
  7. package/dist/cjs/swappers/AcrossSwapper/utils/getAcrossStepData.js +2 -2
  8. package/dist/cjs/swappers/AcrossSwapper/utils/getAcrossTradeContext.js +1 -0
  9. package/dist/cjs/swappers/BebopSwapper/utils/getBebopStepData.d.ts +1 -0
  10. package/dist/cjs/swappers/BebopSwapper/utils/getBebopStepData.js +2 -2
  11. package/dist/cjs/swappers/BebopSwapper/utils/getBebopTradeContext.js +1 -0
  12. package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +5 -1
  13. package/dist/cjs/swappers/BobGatewaySwapper/swapperApi/getTradeRate.js +11 -2
  14. package/dist/cjs/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.d.ts +2 -1
  15. package/dist/cjs/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.js +2 -1
  16. package/dist/cjs/swappers/BobGatewaySwapper/utils/helpers.d.ts +2 -1
  17. package/dist/cjs/swappers/BobGatewaySwapper/utils/helpers.js +2 -1
  18. package/dist/cjs/swappers/ChainflipSwapper/ChainflipSwapper.js +1 -0
  19. package/dist/cjs/swappers/ChainflipSwapper/checkTradeStatus.test.d.ts +1 -0
  20. package/dist/cjs/swappers/ChainflipSwapper/checkTradeStatus.test.js +48 -0
  21. package/dist/cjs/swappers/ChainflipSwapper/endpoints.js +5 -2
  22. package/dist/cjs/swappers/ChainflipSwapper/types.d.ts +3 -0
  23. package/dist/cjs/swappers/DebridgeSwapper/utils/getDebridgeStepData.d.ts +1 -0
  24. package/dist/cjs/swappers/DebridgeSwapper/utils/getDebridgeStepData.js +2 -2
  25. package/dist/cjs/swappers/DebridgeSwapper/utils/getDebridgeTradeContext.js +1 -0
  26. package/dist/cjs/swappers/NearIntentsSwapper/NearIntentsSwapper.js +1 -0
  27. package/dist/cjs/swappers/NearIntentsSwapper/checkTradeStatus.test.d.ts +1 -0
  28. package/dist/cjs/swappers/NearIntentsSwapper/checkTradeStatus.test.js +52 -0
  29. package/dist/cjs/swappers/NearIntentsSwapper/constants.d.ts +1 -1
  30. package/dist/cjs/swappers/NearIntentsSwapper/constants.js +4 -3
  31. package/dist/cjs/swappers/NearIntentsSwapper/endpoints.js +4 -1
  32. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.d.ts +2 -1
  33. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +14 -9
  34. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.d.ts +2 -1
  35. package/dist/cjs/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +14 -9
  36. package/dist/cjs/swappers/NearIntentsSwapper/types.d.ts +3 -1
  37. package/dist/cjs/swappers/NearIntentsSwapper/utils/exactOutput.test.d.ts +1 -0
  38. package/dist/cjs/swappers/NearIntentsSwapper/utils/exactOutput.test.js +61 -0
  39. package/dist/cjs/swappers/NearIntentsSwapper/utils/getNearIntentsTradeContext.d.ts +2 -2
  40. package/dist/cjs/swappers/NearIntentsSwapper/utils/getNearIntentsTradeContext.js +4 -2
  41. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers.d.ts +5 -5
  42. package/dist/cjs/swappers/NearIntentsSwapper/utils/helpers.js +30 -26
  43. package/dist/cjs/swappers/NearIntentsSwapper/utils/quoteDeadline.test.d.ts +1 -0
  44. package/dist/cjs/swappers/NearIntentsSwapper/utils/quoteDeadline.test.js +28 -0
  45. package/dist/cjs/swappers/PortalsSwapper/endpoints.js +51 -47
  46. package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.js +1 -1
  47. package/dist/cjs/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +1 -1
  48. package/dist/cjs/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.d.ts +0 -1
  49. package/dist/cjs/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.js +3 -6
  50. package/dist/cjs/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.js +6 -3
  51. package/dist/cjs/swappers/PortalsSwapper/utils/fetchSquidStatus.d.ts +0 -1
  52. package/dist/cjs/swappers/PortalsSwapper/utils/fetchSquidStatus.js +1 -8
  53. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.d.ts +1 -0
  54. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.js +16 -8
  55. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.test.d.ts +1 -0
  56. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsStepData.test.js +70 -0
  57. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.d.ts +3 -2
  58. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.js +34 -16
  59. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.d.ts +1 -0
  60. package/dist/cjs/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.js +93 -0
  61. package/dist/cjs/swappers/RelaySwapper/endpoints.js +9 -18
  62. package/dist/cjs/swappers/RelaySwapper/getTradeQuote/getTradeQuote.d.ts +2 -1
  63. package/dist/cjs/swappers/RelaySwapper/getTradeQuote/getTradeQuote.js +5 -2
  64. package/dist/cjs/swappers/RelaySwapper/getTradeRate/getTradeRate.d.ts +2 -1
  65. package/dist/cjs/swappers/RelaySwapper/getTradeRate/getTradeRate.js +5 -2
  66. package/dist/cjs/swappers/RelaySwapper/utils/getRelayTradeContext.d.ts +2 -2
  67. package/dist/cjs/swappers/RelaySwapper/utils/getRelayTradeContext.js +22 -10
  68. package/dist/cjs/swappers/RelaySwapper/utils/helpers.d.ts +2 -2
  69. package/dist/cjs/swappers/RelaySwapper/utils/relayStatus.d.ts +8 -0
  70. package/dist/cjs/swappers/RelaySwapper/utils/relayStatus.js +39 -0
  71. package/dist/cjs/swappers/RelaySwapper/utils/types.d.ts +18 -1
  72. package/dist/cjs/swappers/SunioSwapper/utils/getSunioStepData.d.ts +1 -0
  73. package/dist/cjs/swappers/SunioSwapper/utils/getSunioStepData.js +2 -2
  74. package/dist/cjs/swappers/SunioSwapper/utils/getSunioTradeContext.js +6 -1
  75. package/dist/cjs/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +1 -0
  76. package/dist/cjs/types.d.ts +21 -2
  77. package/dist/cjs/types.js +2 -0
  78. package/dist/cjs/utils/cowswap/index.d.ts +4 -1
  79. package/dist/cjs/utils/cowswap/index.js +5 -1
  80. package/dist/cjs/utils/helpers.d.ts +9 -0
  81. package/dist/cjs/utils/helpers.js +23 -1
  82. package/dist/cjs/utils/helpers.test.js +41 -0
  83. package/dist/esm/.tsbuildinfo +1 -1
  84. package/dist/esm/exactOutputFiltering.test.d.ts +1 -0
  85. package/dist/esm/exactOutputFiltering.test.js +68 -0
  86. package/dist/esm/swapper.d.ts +3 -3
  87. package/dist/esm/swapper.js +29 -6
  88. package/dist/esm/swappers/AcrossSwapper/utils/getAcrossStepData.d.ts +1 -0
  89. package/dist/esm/swappers/AcrossSwapper/utils/getAcrossStepData.js +2 -2
  90. package/dist/esm/swappers/AcrossSwapper/utils/getAcrossTradeContext.js +1 -0
  91. package/dist/esm/swappers/BebopSwapper/utils/getBebopStepData.d.ts +1 -0
  92. package/dist/esm/swappers/BebopSwapper/utils/getBebopStepData.js +2 -2
  93. package/dist/esm/swappers/BebopSwapper/utils/getBebopTradeContext.js +1 -0
  94. package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.js +5 -1
  95. package/dist/esm/swappers/BobGatewaySwapper/swapperApi/getTradeRate.js +11 -2
  96. package/dist/esm/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.d.ts +2 -1
  97. package/dist/esm/swappers/BobGatewaySwapper/utils/getBobGatewayTradeContext.js +2 -1
  98. package/dist/esm/swappers/BobGatewaySwapper/utils/helpers.d.ts +2 -1
  99. package/dist/esm/swappers/BobGatewaySwapper/utils/helpers.js +2 -1
  100. package/dist/esm/swappers/ChainflipSwapper/ChainflipSwapper.js +1 -0
  101. package/dist/esm/swappers/ChainflipSwapper/checkTradeStatus.test.d.ts +1 -0
  102. package/dist/esm/swappers/ChainflipSwapper/checkTradeStatus.test.js +46 -0
  103. package/dist/esm/swappers/ChainflipSwapper/endpoints.js +5 -2
  104. package/dist/esm/swappers/ChainflipSwapper/types.d.ts +3 -0
  105. package/dist/esm/swappers/DebridgeSwapper/utils/getDebridgeStepData.d.ts +1 -0
  106. package/dist/esm/swappers/DebridgeSwapper/utils/getDebridgeStepData.js +2 -2
  107. package/dist/esm/swappers/DebridgeSwapper/utils/getDebridgeTradeContext.js +1 -0
  108. package/dist/esm/swappers/NearIntentsSwapper/NearIntentsSwapper.js +1 -0
  109. package/dist/esm/swappers/NearIntentsSwapper/checkTradeStatus.test.d.ts +1 -0
  110. package/dist/esm/swappers/NearIntentsSwapper/checkTradeStatus.test.js +50 -0
  111. package/dist/esm/swappers/NearIntentsSwapper/constants.d.ts +1 -1
  112. package/dist/esm/swappers/NearIntentsSwapper/constants.js +3 -2
  113. package/dist/esm/swappers/NearIntentsSwapper/endpoints.js +6 -3
  114. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.d.ts +2 -1
  115. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.js +7 -3
  116. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.d.ts +2 -1
  117. package/dist/esm/swappers/NearIntentsSwapper/swapperApi/getTradeRate.js +7 -3
  118. package/dist/esm/swappers/NearIntentsSwapper/types.d.ts +3 -1
  119. package/dist/esm/swappers/NearIntentsSwapper/utils/exactOutput.test.d.ts +1 -0
  120. package/dist/esm/swappers/NearIntentsSwapper/utils/exactOutput.test.js +59 -0
  121. package/dist/esm/swappers/NearIntentsSwapper/utils/getNearIntentsTradeContext.d.ts +2 -2
  122. package/dist/esm/swappers/NearIntentsSwapper/utils/getNearIntentsTradeContext.js +4 -2
  123. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers.d.ts +5 -5
  124. package/dist/esm/swappers/NearIntentsSwapper/utils/helpers.js +32 -28
  125. package/dist/esm/swappers/NearIntentsSwapper/utils/quoteDeadline.test.d.ts +1 -0
  126. package/dist/esm/swappers/NearIntentsSwapper/utils/quoteDeadline.test.js +26 -0
  127. package/dist/esm/swappers/PortalsSwapper/endpoints.js +53 -49
  128. package/dist/esm/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.js +1 -1
  129. package/dist/esm/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.js +1 -1
  130. package/dist/esm/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.d.ts +0 -1
  131. package/dist/esm/swappers/PortalsSwapper/utils/fetchAxelarscanStatus.js +2 -4
  132. package/dist/esm/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.js +6 -3
  133. package/dist/esm/swappers/PortalsSwapper/utils/fetchSquidStatus.d.ts +0 -1
  134. package/dist/esm/swappers/PortalsSwapper/utils/fetchSquidStatus.js +0 -6
  135. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.d.ts +1 -0
  136. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.js +13 -5
  137. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.test.d.ts +1 -0
  138. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsStepData.test.js +68 -0
  139. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.d.ts +3 -2
  140. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.js +35 -17
  141. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.d.ts +1 -0
  142. package/dist/esm/swappers/PortalsSwapper/utils/getPortalsTradeContext.test.js +91 -0
  143. package/dist/esm/swappers/RelaySwapper/endpoints.js +11 -20
  144. package/dist/esm/swappers/RelaySwapper/getTradeQuote/getTradeQuote.d.ts +2 -1
  145. package/dist/esm/swappers/RelaySwapper/getTradeQuote/getTradeQuote.js +3 -1
  146. package/dist/esm/swappers/RelaySwapper/getTradeRate/getTradeRate.d.ts +2 -1
  147. package/dist/esm/swappers/RelaySwapper/getTradeRate/getTradeRate.js +3 -1
  148. package/dist/esm/swappers/RelaySwapper/utils/getRelayTradeContext.d.ts +2 -2
  149. package/dist/esm/swappers/RelaySwapper/utils/getRelayTradeContext.js +23 -11
  150. package/dist/esm/swappers/RelaySwapper/utils/helpers.d.ts +2 -2
  151. package/dist/esm/swappers/RelaySwapper/utils/relayStatus.d.ts +8 -0
  152. package/dist/esm/swappers/RelaySwapper/utils/relayStatus.js +32 -0
  153. package/dist/esm/swappers/RelaySwapper/utils/types.d.ts +18 -1
  154. package/dist/esm/swappers/SunioSwapper/utils/getSunioStepData.d.ts +1 -0
  155. package/dist/esm/swappers/SunioSwapper/utils/getSunioStepData.js +2 -2
  156. package/dist/esm/swappers/SunioSwapper/utils/getSunioTradeContext.js +6 -1
  157. package/dist/esm/swappers/ThorchainSwapper/generated/generatedThorLongtailTokens.json +1 -0
  158. package/dist/esm/types.d.ts +21 -2
  159. package/dist/esm/types.js +2 -0
  160. package/dist/esm/utils/cowswap/index.d.ts +4 -1
  161. package/dist/esm/utils/cowswap/index.js +3 -0
  162. package/dist/esm/utils/helpers.d.ts +9 -0
  163. package/dist/esm/utils/helpers.js +20 -0
  164. package/dist/esm/utils/helpers.test.js +42 -1
  165. package/package.json +6 -6
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { swappers } from './constants.js';
3
+ import { getTradeQuotes, getTradeRates } from './swapper.js';
4
+ import { SwapperName, TradeQuoteError } from './types.js';
5
+ import { BTC, ETH } from './utils/test-data/assets.js';
6
+ // Exact-output support is structural - a swapper cannot claim it without implementing it
7
+ describe('exact output capability', () => {
8
+ it('is implemented by the swappers whose upstream API can honour an exact buy amount', () => {
9
+ for (const swapperName of [SwapperName.NearIntents, SwapperName.Relay]) {
10
+ expect(swappers[swapperName]?.getExactOutputTradeRate).toBeDefined();
11
+ expect(swappers[swapperName]?.getExactOutputTradeQuote).toBeDefined();
12
+ }
13
+ });
14
+ it('is absent everywhere else', () => {
15
+ const exactOutputCapable = Object.values(SwapperName).filter(swapperName => swappers[swapperName]?.getExactOutputTradeRate !== undefined);
16
+ expect(exactOutputCapable).toEqual([SwapperName.Relay, SwapperName.NearIntents]);
17
+ });
18
+ });
19
+ const exactOutputInput = {
20
+ sellAsset: ETH,
21
+ buyAsset: BTC,
22
+ buyAmountCryptoBaseUnit: '100000',
23
+ affiliateBps: '0',
24
+ allowMultiHop: true,
25
+ receiveAddress: undefined,
26
+ accountNumber: undefined,
27
+ quoteOrRate: 'rate',
28
+ };
29
+ const deps = {};
30
+ describe('getTradeRates on an exact buy amount', () => {
31
+ // Silently omitting these would read as the swapper being unavailable rather than unable
32
+ it('reports an error for a swapper that cannot quote one', async () => {
33
+ const result = await getTradeRates(exactOutputInput, SwapperName.Thorchain, deps);
34
+ expect(result).toBeDefined();
35
+ expect(result?.isErr()).toBe(true);
36
+ expect(result?.unwrapErr().code).toEqual(TradeQuoteError.ExactOutputNotSupported);
37
+ expect(result?.swapperName).toEqual(SwapperName.Thorchain);
38
+ });
39
+ it('skips entirely on a zero buy amount', async () => {
40
+ const result = await getTradeRates({ ...exactOutputInput, buyAmountCryptoBaseUnit: '0' }, SwapperName.Thorchain, deps);
41
+ expect(result).toBeUndefined();
42
+ });
43
+ // The base-unit regex upstream admits padded zeros, so the check can't be a string comparison
44
+ it('skips entirely on a padded zero buy amount', async () => {
45
+ const result = await getTradeRates({ ...exactOutputInput, buyAmountCryptoBaseUnit: '00' }, SwapperName.Thorchain, deps);
46
+ expect(result).toBeUndefined();
47
+ });
48
+ });
49
+ // Quotes dispatch on the same input shape as rates, so they gate the same way
50
+ describe('getTradeQuotes on an exact buy amount', () => {
51
+ const exactOutputQuoteInput = {
52
+ ...exactOutputInput,
53
+ receiveAddress: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq',
54
+ accountNumber: 0,
55
+ quoteOrRate: 'quote',
56
+ };
57
+ it('reports an error for a swapper that cannot quote one', async () => {
58
+ const result = await getTradeQuotes(exactOutputQuoteInput, SwapperName.Thorchain, deps);
59
+ expect(result).toBeDefined();
60
+ expect(result?.isErr()).toBe(true);
61
+ expect(result?.unwrapErr().code).toEqual(TradeQuoteError.ExactOutputNotSupported);
62
+ expect(result?.swapperName).toEqual(SwapperName.Thorchain);
63
+ });
64
+ it('skips entirely on a zero buy amount', async () => {
65
+ const result = await getTradeQuotes({ ...exactOutputQuoteInput, buyAmountCryptoBaseUnit: '0' }, SwapperName.Thorchain, deps);
66
+ expect(result).toBeUndefined();
67
+ });
68
+ });
@@ -1,3 +1,3 @@
1
- import type { GetTradeQuoteInput, GetTradeRateInput, QuoteResult, RateResult, SwapperDeps, SwapperName } from './types.js';
2
- export declare const getTradeQuotes: (getTradeQuoteInput: GetTradeQuoteInput, swapperName: SwapperName, deps: SwapperDeps) => Promise<QuoteResult | undefined>;
3
- export declare const getTradeRates: (getTradeRateInput: GetTradeRateInput, swapperName: SwapperName, deps: SwapperDeps, quoteTimeoutMs?: number) => Promise<RateResult | undefined>;
1
+ import type { GetExactOutputTradeQuoteInput, GetExactOutputTradeRateInput, GetTradeQuoteInput, GetTradeRateInput, QuoteResult, RateResult, SwapperDeps, SwapperName } from './types.js';
2
+ export declare const getTradeQuotes: (getTradeQuoteInput: GetTradeQuoteInput | GetExactOutputTradeQuoteInput, swapperName: SwapperName, deps: SwapperDeps) => Promise<QuoteResult | undefined>;
3
+ export declare const getTradeRates: (getTradeRateInput: GetTradeRateInput | GetExactOutputTradeRateInput, swapperName: SwapperName, deps: SwapperDeps, quoteTimeoutMs?: number) => Promise<RateResult | undefined>;
@@ -1,17 +1,31 @@
1
1
  import { bnOrZero, timeoutMonadic, timeoutMonadicWithOriginal } from '@shapeshiftoss/utils';
2
+ import { Err } from '@sniptt/monads';
2
3
  import { QUOTE_TIMEOUT_ERROR, QUOTE_TIMEOUT_MS, swappers } from './constants.js';
3
4
  import { TradeQuoteError } from './types.js';
4
5
  import { makeSwapErrorRight } from './utils/index.js';
6
+ const EXACT_OUTPUT_NOT_SUPPORTED_ERROR = makeSwapErrorRight({
7
+ code: TradeQuoteError.ExactOutputNotSupported,
8
+ message: 'This swapper cannot derive a sell amount from an exact buy amount',
9
+ });
5
10
  export const getTradeQuotes = async (getTradeQuoteInput, swapperName, deps) => {
6
11
  if (bnOrZero(getTradeQuoteInput.affiliateBps).lt(0))
7
12
  return;
8
- if (getTradeQuoteInput.sellAmountIncludingProtocolFeesCryptoBaseUnit === '0')
9
- return;
10
13
  const swapper = swappers[swapperName];
11
14
  if (swapper === undefined)
12
15
  return;
16
+ // Compared numerically so a padded zero can't slip through as an amount
17
+ const drivingAmount = 'buyAmountCryptoBaseUnit' in getTradeQuoteInput
18
+ ? getTradeQuoteInput.buyAmountCryptoBaseUnit
19
+ : getTradeQuoteInput.sellAmountIncludingProtocolFeesCryptoBaseUnit;
20
+ if (bnOrZero(drivingAmount).isZero())
21
+ return;
22
+ const quotePromise = 'buyAmountCryptoBaseUnit' in getTradeQuoteInput
23
+ ? swapper.getExactOutputTradeQuote?.(getTradeQuoteInput, deps)
24
+ : swapper.getTradeQuote(getTradeQuoteInput, deps);
25
+ if (!quotePromise)
26
+ return { ...Err(EXACT_OUTPUT_NOT_SUPPORTED_ERROR), swapperName };
13
27
  try {
14
- const quote = await timeoutMonadic(swapper.getTradeQuote(getTradeQuoteInput, deps), QUOTE_TIMEOUT_MS, QUOTE_TIMEOUT_ERROR);
28
+ const quote = await timeoutMonadic(quotePromise, QUOTE_TIMEOUT_MS, QUOTE_TIMEOUT_ERROR);
15
29
  return {
16
30
  ...quote,
17
31
  swapperName,
@@ -26,13 +40,22 @@ export const getTradeQuotes = async (getTradeQuoteInput, swapperName, deps) => {
26
40
  export const getTradeRates = async (getTradeRateInput, swapperName, deps, quoteTimeoutMs = QUOTE_TIMEOUT_MS) => {
27
41
  if (bnOrZero(getTradeRateInput.affiliateBps).lt(0))
28
42
  return;
29
- if (getTradeRateInput.sellAmountIncludingProtocolFeesCryptoBaseUnit === '0')
30
- return;
31
43
  const swapper = swappers[swapperName];
32
44
  if (swapper === undefined)
33
45
  return;
46
+ // Compared numerically so a padded zero can't slip through as an amount
47
+ const drivingAmount = 'buyAmountCryptoBaseUnit' in getTradeRateInput
48
+ ? getTradeRateInput.buyAmountCryptoBaseUnit
49
+ : getTradeRateInput.sellAmountIncludingProtocolFeesCryptoBaseUnit;
50
+ if (bnOrZero(drivingAmount).isZero())
51
+ return;
52
+ const ratePromise = 'buyAmountCryptoBaseUnit' in getTradeRateInput
53
+ ? swapper.getExactOutputTradeRate?.(getTradeRateInput, deps)
54
+ : swapper.getTradeRate(getTradeRateInput, deps);
55
+ if (!ratePromise)
56
+ return { ...Err(EXACT_OUTPUT_NOT_SUPPORTED_ERROR), swapperName };
34
57
  try {
35
- const { timed, original } = timeoutMonadicWithOriginal(swapper.getTradeRate(getTradeRateInput, deps), quoteTimeoutMs, makeSwapErrorRight({
58
+ const { timed, original } = timeoutMonadicWithOriginal(ratePromise, quoteTimeoutMs, makeSwapErrorRight({
36
59
  code: TradeQuoteError.Timeout,
37
60
  message: `quote timed out after ${quoteTimeoutMs / 1000}s`,
38
61
  }));
@@ -3,6 +3,7 @@ import type { StepDataArgs, SwapErrorRight, TxBuildData } from '../../../types.j
3
3
  import type { AcrossSwapTx } from './types.js';
4
4
  type BaseArgs = {
5
5
  swapTx: AcrossSwapTx;
6
+ sellAmountCryptoBaseUnit: string;
6
7
  spenderAddress: string;
7
8
  fallbackNetworkFeeCryptoBaseUnit: string;
8
9
  };
@@ -7,7 +7,7 @@ import { makeNetworkFeeEstimationFailedErr, makeSwapErrorRight, makeTradeStepBui
7
7
  import { getEvmNetworkFeeCryptoBaseUnit } from '../../../utils/evm/index.js';
8
8
  import { getSolanaNetworkFeeCryptoBaseUnit } from '../../../utils/solana/index.js';
9
9
  export async function getAcrossStepData(args) {
10
- const { swapTx, sellAsset, spenderAddress, from, type, input, fallbackNetworkFeeCryptoBaseUnit, deps, } = args;
10
+ const { swapTx, sellAsset, sellAmountCryptoBaseUnit, spenderAddress, from, type, input, fallbackNetworkFeeCryptoBaseUnit, deps, } = args;
11
11
  const supportsEIP1559 = 'supportsEIP1559' in input ? input.supportsEIP1559 : false;
12
12
  switch (swapTx.ecosystem) {
13
13
  case 'evm': {
@@ -22,7 +22,7 @@ export async function getAcrossStepData(args) {
22
22
  };
23
23
  const stateOverride = {
24
24
  sellAsset,
25
- sellAmountCryptoBaseUnit: input.sellAmountIncludingProtocolFeesCryptoBaseUnit,
25
+ sellAmountCryptoBaseUnit,
26
26
  spenderAddress,
27
27
  };
28
28
  if (type === 'rate') {
@@ -167,6 +167,7 @@ export const getAcrossTradeContext = async ({ input, deps, depositor, recipient,
167
167
  stepDataArgs: {
168
168
  swapTx: quote.swapTx,
169
169
  sellAsset,
170
+ sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
170
171
  from: depositor,
171
172
  spenderAddress: allowanceContract,
172
173
  fallbackNetworkFeeCryptoBaseUnit: quote.fees.originGas.amount,
@@ -3,6 +3,7 @@ import type { StepDataArgs, SwapErrorRight, TxBuildData } from '../../../types.j
3
3
  import type { BebopQuoteResponse } from '../types.js';
4
4
  type BaseArgs = {
5
5
  tx: BebopQuoteResponse['tx'];
6
+ sellAmountCryptoBaseUnit: string;
6
7
  spenderAddress: string;
7
8
  };
8
9
  export type GetBebopStepDataArgs = StepDataArgs<BaseArgs, {
@@ -5,7 +5,7 @@ import { fromHex } from 'viem';
5
5
  import { makeNetworkFeeEstimationFailedErr } from '../../../utils/index.js';
6
6
  import { getEvmNetworkFeeCryptoBaseUnit } from '../../../utils/evm/index.js';
7
7
  export async function getBebopStepData(args) {
8
- const { tx, spenderAddress, sellAsset, type, input, from, deps } = args;
8
+ const { tx, spenderAddress, sellAsset, sellAmountCryptoBaseUnit, type, input, from, deps } = args;
9
9
  const adapter = deps.assertGetEvmChainAdapter(sellAsset.chainId);
10
10
  const supportsEIP1559 = 'supportsEIP1559' in input ? input.supportsEIP1559 : false;
11
11
  if (type === 'rate') {
@@ -40,7 +40,7 @@ export async function getBebopStepData(args) {
40
40
  supportsEIP1559,
41
41
  stateOverride: {
42
42
  sellAsset,
43
- sellAmountCryptoBaseUnit: input.sellAmountIncludingProtocolFeesCryptoBaseUnit,
43
+ sellAmountCryptoBaseUnit,
44
44
  spenderAddress,
45
45
  },
46
46
  });
@@ -75,6 +75,7 @@ export const getBebopTradeContext = async ({ input, deps, from, receiver, }) =>
75
75
  tx: quote.tx,
76
76
  spenderAddress: allowanceContract,
77
77
  sellAsset,
78
+ sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
78
79
  from,
79
80
  deps,
80
81
  },
@@ -10,14 +10,18 @@ export const getBobGatewayTradeQuote = async (input, deps) => {
10
10
  if (maybeAddresses.isErr())
11
11
  return Err(maybeAddresses.unwrapErr());
12
12
  const { sendAddress, receiveAddress } = maybeAddresses.unwrap();
13
+ const isEvmSell = isEvmChainId(sellAsset.chainId);
13
14
  // omit the sender for utxo sells so order creation does not enforce a per-address confirmed
14
15
  // funds check (deposits are matched via op_return, not the sending address)
15
- const sender = isEvmChainId(sellAsset.chainId) ? sendAddress : undefined;
16
+ const sender = isEvmSell ? sendAddress : undefined;
17
+ // utxo deposits are refunded on the sell chain, so refunds go to the sending address
18
+ const refundAddress = isEvmSell ? undefined : sendAddress;
16
19
  const maybeContext = await getBobGatewayTradeContext({
17
20
  input,
18
21
  deps,
19
22
  sender,
20
23
  recipient: receiveAddress,
24
+ refundAddress,
21
25
  });
22
26
  if (maybeContext.isErr())
23
27
  return Err(maybeContext.unwrapErr());
@@ -5,9 +5,18 @@ import { getBobGatewayTradeContext } from '../utils/getBobGatewayTradeContext.js
5
5
  import { dummyAddressForChainId } from '../utils/helpers.js';
6
6
  export const getBobGatewayTradeRate = async (input, deps) => {
7
7
  const { accountNumber, sellAsset, buyAsset, receiveAddress } = input;
8
+ const isBtcSell = sellAsset.chainId === btcChainId;
8
9
  const recipient = receiveAddress ?? dummyAddressForChainId(buyAsset.chainId);
9
- const sender = sellAsset.chainId === btcChainId ? undefined : dummyAddressForChainId(sellAsset.chainId);
10
- const maybeContext = await getBobGatewayTradeContext({ input, deps, sender, recipient });
10
+ const sender = isBtcSell ? undefined : dummyAddressForChainId(sellAsset.chainId);
11
+ // utxo deposits are refunded on the sell chain, so the refund address must be a btc address
12
+ const refundAddress = isBtcSell ? dummyAddressForChainId(sellAsset.chainId) : undefined;
13
+ const maybeContext = await getBobGatewayTradeContext({
14
+ input,
15
+ deps,
16
+ sender,
17
+ recipient,
18
+ refundAddress,
19
+ });
11
20
  if (maybeContext.isErr())
12
21
  return Err(maybeContext.unwrapErr());
13
22
  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 {};
@@ -4,7 +4,7 @@ import { v4 as uuid } from 'uuid';
4
4
  import { SwapperName } from '../../../types.js';
5
5
  import { getInputOutputRate } from '../../../utils/index.js';
6
6
  import { assertValidTrade, getBobGatewayAllowanceContract, getBobGatewayQuote, parseBobGatewayQuote, } from './helpers.js';
7
- export const getBobGatewayTradeContext = async ({ input, deps, sender, recipient, }) => {
7
+ export const getBobGatewayTradeContext = async ({ input, deps, sender, recipient, refundAddress, }) => {
8
8
  const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit, affiliateBps, slippageTolerancePercentageDecimal, } = input;
9
9
  const assertion = assertValidTrade({ sellAsset, buyAsset });
10
10
  if (assertion.isErr())
@@ -18,6 +18,7 @@ export const getBobGatewayTradeContext = async ({ input, deps, sender, recipient
18
18
  buyChainName,
19
19
  sender,
20
20
  recipient,
21
+ refundAddress,
21
22
  amount: sellAmountIncludingProtocolFeesCryptoBaseUnit,
22
23
  affiliateBps,
23
24
  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;
@@ -41,7 +41,7 @@ export const getBobGatewayAffiliates = (affiliateBps) => {
41
41
  const affiliateAddress = getTreasuryAddressFromChainId(ethChainId);
42
42
  return [{ address: getAddress(affiliateAddress), bps: bps.toNumber() }];
43
43
  };
44
- export const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, amount, affiliateBps, slippageTolerancePercentageDecimal, }) => {
44
+ export const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChainName, buyChainName, sender, recipient, refundAddress, amount, affiliateBps, slippageTolerancePercentageDecimal, }) => {
45
45
  const slippage = decimalSlippageToBobBps(slippageTolerancePercentageDecimal ??
46
46
  getDefaultSlippageDecimalPercentageForSwapper(SwapperName.BobGateway));
47
47
  try {
@@ -55,6 +55,7 @@ export const getBobGatewayQuote = async ({ config, sellAsset, buyAsset, sellChai
55
55
  amount,
56
56
  maxSlippage: Number(slippage),
57
57
  ownerAddress: sellChainName === 'bitcoin' ? recipient : sender,
58
+ refundAddress,
58
59
  affiliates: getBobGatewayAffiliates(affiliateBps),
59
60
  });
60
61
  return Ok(quote);
@@ -1,5 +1,6 @@
1
1
  import { executeEvmTransaction, executeSolanaTransaction, executeTronTransaction, } from '../../utils/index.js';
2
2
  export const chainflipSwapper = {
3
+ supportsExternalPayment: true,
3
4
  executeEvmTransaction,
4
5
  executeSolanaTransaction,
5
6
  executeTronTransaction,
@@ -0,0 +1,46 @@
1
+ import { Ok } from '@sniptt/monads';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { chainflipApi } from './endpoints.js';
4
+ import { chainflipService } from './utils/chainflipService.js';
5
+ vi.mock('./utils/chainflipService', () => ({
6
+ chainflipService: { get: vi.fn() },
7
+ }));
8
+ const makeInput = () => ({
9
+ config: { VITE_CHAINFLIP_API_URL: 'https://broker', VITE_CHAINFLIP_API_KEY: 'key' },
10
+ swap: { metadata: { swapperMetadata: { name: 'chainflip', swapId: 1234 } } },
11
+ });
12
+ const mockStatus = (status) => vi.mocked(chainflipService.get).mockResolvedValue(Ok({ data: { status } }));
13
+ describe('chainflip checkTradeStatus', () => {
14
+ beforeEach(() => {
15
+ vi.clearAllMocks();
16
+ });
17
+ it('reports the deposit tx hash while the swap is still pending', async () => {
18
+ mockStatus({
19
+ state: 'swapping',
20
+ swapId: '1234',
21
+ deposit: { transactionReference: '0xdeposit' },
22
+ });
23
+ const result = await chainflipApi.checkTradeStatus(makeInput());
24
+ expect(result.sellTxHash).toBe('0xdeposit');
25
+ expect(result.buyTxHash).toBeUndefined();
26
+ });
27
+ it('reports the deposit tx hash alongside a completed egress', async () => {
28
+ mockStatus({
29
+ state: 'completed',
30
+ swapId: '1234',
31
+ deposit: { transactionReference: '0xdeposit' },
32
+ swapEgress: { transactionReference: '0xegress' },
33
+ });
34
+ const result = await chainflipApi.checkTradeStatus(makeInput());
35
+ expect(result.sellTxHash).toBe('0xdeposit');
36
+ expect(result.buyTxHash).toBe('0xegress');
37
+ });
38
+ it('has no deposit tx hash before the deposit is witnessed', async () => {
39
+ mockStatus({ state: 'waiting', swapId: '1234' });
40
+ expect((await chainflipApi.checkTradeStatus(makeInput())).sellTxHash).toBeUndefined();
41
+ });
42
+ it('treats a null transaction reference as no deposit yet', async () => {
43
+ mockStatus({ state: 'receiving', swapId: '1234', deposit: { transactionReference: null } });
44
+ expect((await chainflipApi.checkTradeStatus(makeInput())).sellTxHash).toBeUndefined();
45
+ });
46
+ });
@@ -52,13 +52,15 @@ export const chainflipApi = {
52
52
  };
53
53
  }
54
54
  const { data: statusResponse } = maybeStatusResponse.unwrap();
55
- const buyTxHash = statusResponse.status.swapEgress?.transactionReference;
55
+ const buyTxHash = statusResponse.status.swapEgress?.transactionReference ?? undefined;
56
+ const sellTxHash = statusResponse.status.deposit?.transactionReference ?? undefined;
56
57
  const swapperTxId = statusResponse.status.swapId;
57
58
  const swapperTxLink = swapperTxId ? `https://scan.chainflip.io/swaps/${swapperTxId}` : undefined;
58
59
  // Assume no outbound Tx is a pending Tx
59
60
  if (!buyTxHash) {
60
61
  return {
61
- buyTxHash: undefined,
62
+ buyTxHash,
63
+ sellTxHash,
62
64
  status: TxStatus.Pending,
63
65
  swapperTxId,
64
66
  swapperTxLink,
@@ -69,6 +71,7 @@ export const chainflipApi = {
69
71
  // Chainflip waits for 3 confirmations to assume complete (vs. 1 for us), which is turbo long.
70
72
  return {
71
73
  buyTxHash,
74
+ sellTxHash,
72
75
  status: TxStatus.Confirmed,
73
76
  swapperTxId,
74
77
  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 @@ import type { StepDataArgs, SwapErrorRight, TxBuildData } from '../../../types.j
3
3
  import type { DebridgeTx } from './types.js';
4
4
  type BaseArgs = {
5
5
  tx: DebridgeTx;
6
+ sellAmountCryptoBaseUnit: string;
6
7
  gasLimit: string | undefined;
7
8
  spenderAddress: string;
8
9
  fallbackNetworkFeeCryptoBaseUnit: string | undefined;
@@ -3,7 +3,7 @@ import { Err, Ok } from '@sniptt/monads';
3
3
  import { makeNetworkFeeEstimationFailedErr } from '../../../utils/index.js';
4
4
  import { getEvmNetworkFeeCryptoBaseUnit } from '../../../utils/evm/index.js';
5
5
  export async function getDebridgeStepData(args) {
6
- const { tx, gasLimit, spenderAddress, fallbackNetworkFeeCryptoBaseUnit, sellAsset, from, type, input, deps, } = args;
6
+ const { tx, sellAmountCryptoBaseUnit, gasLimit, spenderAddress, fallbackNetworkFeeCryptoBaseUnit, sellAsset, from, type, input, deps, } = args;
7
7
  const adapter = deps.assertGetEvmChainAdapter(sellAsset.chainId);
8
8
  const supportsEIP1559 = 'supportsEIP1559' in input ? input.supportsEIP1559 : false;
9
9
  const transactionData = {
@@ -16,7 +16,7 @@ export async function getDebridgeStepData(args) {
16
16
  };
17
17
  const stateOverride = {
18
18
  sellAsset,
19
- sellAmountCryptoBaseUnit: input.sellAmountIncludingProtocolFeesCryptoBaseUnit,
19
+ sellAmountCryptoBaseUnit,
20
20
  spenderAddress,
21
21
  };
22
22
  if (type === 'rate') {
@@ -81,6 +81,7 @@ export const getDebridgeTradeContext = async ({ input, deps, senderAddress, reci
81
81
  protocolFees: quote.protocolFees,
82
82
  stepDataArgs: {
83
83
  tx: quote.tx,
84
+ sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
84
85
  gasLimit: quote.gasLimit,
85
86
  fallbackNetworkFeeCryptoBaseUnit: quote.fallbackNetworkFeeCryptoBaseUnit,
86
87
  sellAsset,
@@ -1,5 +1,6 @@
1
1
  import { executeEvmTransaction, executeNearTransaction, executeSolanaTransaction, executeStarknetTransaction, executeSuiTransaction, executeTonTransaction, executeTronTransaction, } from '../../utils/index.js';
2
2
  export const nearIntentsSwapper = {
3
+ supportsExternalPayment: true,
3
4
  executeEvmTransaction,
4
5
  executeSolanaTransaction,
5
6
  executeStarknetTransaction,
@@ -0,0 +1,50 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { nearIntentsApi } from './endpoints.js';
3
+ import { OneClickService } from './utils/oneClickService.js';
4
+ vi.mock('./utils/oneClickService', () => ({
5
+ initializeOneClickService: vi.fn(),
6
+ OneClickService: { getExecutionStatus: vi.fn() },
7
+ }));
8
+ const makeInput = () => ({
9
+ config: { VITE_NEAR_INTENTS_API_KEY: 'key' },
10
+ swap: {
11
+ metadata: { swapperMetadata: { name: 'nearIntents', depositAddress: '0xdepositaddress' } },
12
+ },
13
+ });
14
+ const mockExecutionStatus = (response) => vi.mocked(OneClickService.getExecutionStatus).mockResolvedValue(response);
15
+ describe('near intents checkTradeStatus', () => {
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ });
19
+ it('reports the origin chain deposit tx hash', async () => {
20
+ mockExecutionStatus({
21
+ status: 'PROCESSING',
22
+ swapDetails: {
23
+ originChainTxHashes: [{ hash: '0xdeposit', explorerUrl: '' }],
24
+ destinationChainTxHashes: [],
25
+ },
26
+ });
27
+ const result = await nearIntentsApi.checkTradeStatus(makeInput());
28
+ expect(result.sellTxHash).toBe('0xdeposit');
29
+ expect(result.buyTxHash).toBeUndefined();
30
+ });
31
+ it('reports both sides once the swap settles', async () => {
32
+ mockExecutionStatus({
33
+ status: 'SUCCESS',
34
+ swapDetails: {
35
+ originChainTxHashes: [{ hash: '0xdeposit', explorerUrl: '' }],
36
+ destinationChainTxHashes: [{ hash: '0xsettlement', explorerUrl: '' }],
37
+ },
38
+ });
39
+ const result = await nearIntentsApi.checkTradeStatus(makeInput());
40
+ expect(result.sellTxHash).toBe('0xdeposit');
41
+ expect(result.buyTxHash).toBe('0xsettlement');
42
+ });
43
+ it('has no deposit tx hash before the deposit lands', async () => {
44
+ mockExecutionStatus({
45
+ status: 'PENDING_DEPOSIT',
46
+ swapDetails: { originChainTxHashes: [], destinationChainTxHashes: [] },
47
+ });
48
+ expect((await nearIntentsApi.checkTradeStatus(makeInput())).sellTxHash).toBeUndefined();
49
+ });
50
+ });
@@ -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,4 +1,5 @@
1
1
  export const ONE_CLICK_BASE_URL = 'https://1click.chaindefuser.com';
2
2
  export const DEFAULT_SLIPPAGE_BPS = 50;
3
- export const DEFAULT_QUOTE_DEADLINE_MS = 30 * 60 * 1000;
4
- export const BTC_QUOTE_DEADLINE_MS = 60 * 60 * 1000;
3
+ // 1Click refunds a deposit it cannot settle by the requested deadline, and prices a quote the same at any length
4
+ export const DEFAULT_QUOTE_DEADLINE_MS = 60 * 60 * 1000;
5
+ export const UTXO_QUOTE_DEADLINE_MS = 4 * 60 * 60 * 1000;
@@ -4,13 +4,15 @@ import { getEvmTransactionFees, getUnsignedEvmTransaction } from '../../utils/ev
4
4
  import { getSolanaTransactionFees, getUnsignedSolanaTransaction } from '../../utils/solana/index.js';
5
5
  import { getTronTransactionFees, getUnsignedTronTransaction } from '../../utils/tron/index.js';
6
6
  import { getUnsignedUtxoTransaction, getUtxoTransactionFees } from '../../utils/utxo/index.js';
7
- import { getTradeQuote } from './swapperApi/getTradeQuote.js';
8
- import { getTradeRate } from './swapperApi/getTradeRate.js';
7
+ import { getExactOutputTradeQuote, getTradeQuote } from './swapperApi/getTradeQuote.js';
8
+ import { getExactOutputTradeRate, getTradeRate } from './swapperApi/getTradeRate.js';
9
9
  import { getNearIntentsStatusMessage, mapNearIntentsStatus } from './utils/helpers.js';
10
10
  import { initializeOneClickService, OneClickService } from './utils/oneClickService.js';
11
11
  export const nearIntentsApi = {
12
12
  getTradeQuote: (input, deps) => getTradeQuote(input, deps),
13
13
  getTradeRate: (input, deps) => getTradeRate(input, deps),
14
+ getExactOutputTradeQuote: (input, deps) => getExactOutputTradeQuote(input, deps),
15
+ getExactOutputTradeRate: (input, deps) => getExactOutputTradeRate(input, deps),
14
16
  getUnsignedEvmTransaction,
15
17
  getEvmTransactionFees,
16
18
  getUnsignedUtxoTransaction,
@@ -161,14 +163,15 @@ export const nearIntentsApi = {
161
163
  const statusResponse = await OneClickService.getExecutionStatus(depositAddress);
162
164
  const txStatus = mapNearIntentsStatus(statusResponse.status);
163
165
  const message = getNearIntentsStatusMessage(statusResponse.status);
164
- // Extract buyTxHash from destination chain transactions
165
166
  const buyTxHash = statusResponse.swapDetails?.destinationChainTxHashes?.[0]?.hash;
167
+ const sellTxHash = statusResponse.swapDetails?.originChainTxHashes?.[0]?.hash;
166
168
  // amountOut is only meaningful destination-denominated on terminal success - in-flight and
167
169
  // refund states may carry settlement-internal or refund values
168
170
  const actualBuyAmountCryptoBaseUnit = statusResponse.status === 'SUCCESS' ? statusResponse.swapDetails?.amountOut : undefined;
169
171
  return {
170
172
  status: txStatus,
171
173
  buyTxHash,
174
+ sellTxHash,
172
175
  swapperTxLink: `https://explorer.near-intents.org/transactions/${depositAddress}`,
173
176
  message,
174
177
  actualBuyAmountCryptoBaseUnit,
@@ -1,4 +1,5 @@
1
1
  import type { Result } from '@sniptt/monads';
2
2
  import type { SwapErrorRight, SwapperDeps, TradeQuote } from '../../../types.js';
3
- import type { NearIntentsTradeQuoteInput } from '../types.js';
3
+ import type { NearIntentsExactOutputTradeQuoteInput, NearIntentsTradeQuoteInput } from '../types.js';
4
4
  export declare const getTradeQuote: (input: NearIntentsTradeQuoteInput, deps: SwapperDeps) => Promise<Result<TradeQuote[], SwapErrorRight>>;
5
+ export declare const getExactOutputTradeQuote: (input: NearIntentsExactOutputTradeQuoteInput, deps: SwapperDeps) => Promise<Result<TradeQuote[], SwapErrorRight>>;
@@ -1,17 +1,19 @@
1
1
  import { Err, Ok } from '@sniptt/monads';
2
2
  import { assertQuoteAddresses } from '../../../utils/index.js';
3
+ import { getTradeAmount } from '../../../utils/helpers.js';
3
4
  import { QuoteRequest } from '../types.js';
4
5
  import { getNearIntentsStepData } from '../utils/getNearIntentsStepData.js';
5
6
  import { getNearIntentsTradeContext } from '../utils/getNearIntentsTradeContext.js';
6
7
  import { buildNearIntentsQuoteRequest, fetchNearIntentsQuote, getNearIntentsQuoteDeadline, resolveNearIntentsAssets, } from '../utils/helpers.js';
7
8
  import { initializeOneClickService } from '../utils/oneClickService.js';
8
- export const getTradeQuote = async (input, deps) => {
9
- const { sellAsset, buyAsset, accountNumber, affiliateBps, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmount, } = input;
9
+ const getQuote = async (input, deps) => {
10
+ const { sellAsset, buyAsset, accountNumber, affiliateBps } = input;
10
11
  const addresses = assertQuoteAddresses(input);
11
12
  if (addresses.isErr())
12
13
  return Err(addresses.unwrapErr());
13
14
  const { sendAddress, receiveAddress } = addresses.unwrap();
14
15
  initializeOneClickService(deps.config.VITE_NEAR_INTENTS_API_KEY);
16
+ const amount = getTradeAmount(input);
15
17
  const maybeAssets = await resolveNearIntentsAssets({ sellAsset, buyAsset });
16
18
  if (maybeAssets.isErr())
17
19
  return Err(maybeAssets.unwrapErr());
@@ -19,7 +21,7 @@ export const getTradeQuote = async (input, deps) => {
19
21
  const quoteRequest = buildNearIntentsQuoteRequest({
20
22
  originAsset,
21
23
  destinationAsset,
22
- sellAmountCryptoBaseUnit: sellAmount,
24
+ amount,
23
25
  slippageTolerancePercentageDecimal: input.slippageTolerancePercentageDecimal,
24
26
  affiliateBps,
25
27
  refundTo: sendAddress,
@@ -72,3 +74,5 @@ export const getTradeQuote = async (input, deps) => {
72
74
  };
73
75
  return Ok([tradeQuote]);
74
76
  };
77
+ export const getTradeQuote = (input, deps) => getQuote(input, deps);
78
+ export const getExactOutputTradeQuote = (input, deps) => getQuote(input, deps);
@@ -1,4 +1,5 @@
1
1
  import type { Result } from '@sniptt/monads';
2
2
  import type { SwapErrorRight, SwapperDeps, TradeRate } from '../../../types.js';
3
- import type { NearIntentsTradeRateInput } from '../types.js';
3
+ import type { NearIntentsExactOutputTradeRateInput, NearIntentsTradeRateInput } from '../types.js';
4
4
  export declare const getTradeRate: (input: NearIntentsTradeRateInput, deps: SwapperDeps) => Promise<Result<TradeRate[], SwapErrorRight>>;
5
+ export declare const getExactOutputTradeRate: (input: NearIntentsExactOutputTradeRateInput, deps: SwapperDeps) => Promise<Result<TradeRate[], SwapErrorRight>>;