@avalabs/fusion-sdk 0.23.1 → 0.25.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 (51) hide show
  1. package/dist/errors.cjs +1 -1
  2. package/dist/errors.cjs.map +1 -1
  3. package/dist/errors.d.cts +18 -1
  4. package/dist/errors.d.ts +18 -1
  5. package/dist/errors.js +1 -1
  6. package/dist/errors.js.map +1 -1
  7. package/dist/quoter/quoter.cjs.map +1 -1
  8. package/dist/quoter/quoter.d.cts +3 -1
  9. package/dist/quoter/quoter.d.ts +3 -1
  10. package/dist/quoter/quoter.js.map +1 -1
  11. package/dist/transfer-service/markr/_handlers/stream-quotes.cjs +1 -1
  12. package/dist/transfer-service/markr/_handlers/stream-quotes.cjs.map +1 -1
  13. package/dist/transfer-service/markr/_handlers/stream-quotes.js +1 -1
  14. package/dist/transfer-service/markr/_handlers/stream-quotes.js.map +1 -1
  15. package/dist/transfer-service/markr/_schema.cjs +1 -1
  16. package/dist/transfer-service/markr/_schema.cjs.map +1 -1
  17. package/dist/transfer-service/markr/_schema.js +1 -1
  18. package/dist/transfer-service/markr/_schema.js.map +1 -1
  19. package/dist/transfer-service/markr/_utils.cjs +1 -1
  20. package/dist/transfer-service/markr/_utils.cjs.map +1 -1
  21. package/dist/transfer-service/markr/_utils.js +1 -1
  22. package/dist/transfer-service/markr/_utils.js.map +1 -1
  23. package/dist/transfer-service/markr/recurring/_api.cjs +1 -1
  24. package/dist/transfer-service/markr/recurring/_api.cjs.map +1 -1
  25. package/dist/transfer-service/markr/recurring/_api.js +1 -1
  26. package/dist/transfer-service/markr/recurring/_api.js.map +1 -1
  27. package/dist/transfer-service/markr/recurring/_chain-info.cjs +1 -1
  28. package/dist/transfer-service/markr/recurring/_chain-info.cjs.map +1 -1
  29. package/dist/transfer-service/markr/recurring/_chain-info.js +1 -1
  30. package/dist/transfer-service/markr/recurring/_chain-info.js.map +1 -1
  31. package/dist/transfer-service/markr/recurring/_eligibility.cjs +1 -1
  32. package/dist/transfer-service/markr/recurring/_eligibility.cjs.map +1 -1
  33. package/dist/transfer-service/markr/recurring/_eligibility.js +1 -1
  34. package/dist/transfer-service/markr/recurring/_eligibility.js.map +1 -1
  35. package/dist/transfer-service/markr/recurring/_namespace.cjs +1 -1
  36. package/dist/transfer-service/markr/recurring/_namespace.cjs.map +1 -1
  37. package/dist/transfer-service/markr/recurring/_namespace.js +1 -1
  38. package/dist/transfer-service/markr/recurring/_namespace.js.map +1 -1
  39. package/dist/transfer-service/markr/recurring/_schema.cjs +1 -1
  40. package/dist/transfer-service/markr/recurring/_schema.cjs.map +1 -1
  41. package/dist/transfer-service/markr/recurring/_schema.js +1 -1
  42. package/dist/transfer-service/markr/recurring/_schema.js.map +1 -1
  43. package/dist/transfer-service/markr/recurring/types.cjs +1 -1
  44. package/dist/transfer-service/markr/recurring/types.cjs.map +1 -1
  45. package/dist/transfer-service/markr/recurring/types.d.cts +32 -23
  46. package/dist/transfer-service/markr/recurring/types.d.ts +32 -23
  47. package/dist/transfer-service/markr/recurring/types.js +1 -1
  48. package/dist/transfer-service/markr/recurring/types.js.map +1 -1
  49. package/dist/types/service.d.cts +3 -1
  50. package/dist/types/service.d.ts +3 -1
  51. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"_utils.cjs","names":["markrGetInfoChains","eip155ChainIdToCaip2","SdkError","ErrorReason","ErrorCode","markrGetTokenList","TokenType","isSolanaNamespace","NATIVE_SOL_ADDRESS","ERC_ZERO_ADDRESS","calculateMinimumAmountOut","ServiceType","markrGetPartnerInfo","isEvmAddress","isSolAddress"],"sources":["../../../src/transfer-service/markr/_utils.ts"],"sourcesContent":["import { ErrorCode, ErrorReason, SdkError } from '../../errors';\nimport {\n BaseError,\n decodeErrorResult,\n isAddressEqual,\n isHex,\n toFunctionSelector,\n type Address as EvmAddress,\n type Hex,\n} from 'viem';\nimport {\n type QuoteResponseData,\n type SupportedChainsResponseItemEvm,\n type SupportedChainsResponseItemSvm,\n type TokenListResponse,\n} from './_schema';\nimport { ERC_ZERO_ADDRESS, NATIVE_SOL_ADDRESS, ServiceType, TokenType } from '../../constants';\nimport { isSolanaNamespace } from '../../_utils/chain';\nimport { calculateMinimumAmountOut } from '../../_utils/math';\nimport { eip155ChainIdToCaip2 } from '../../utils/caip';\nimport type { Asset } from '../../types/asset';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Quote, QuoteFee, QuoteFees, QuoteFeeToken, QuoterProps } from '../../types/quote';\nimport { markrGetInfoChains, markrGetPartnerInfo, markrGetTokenList, type ApiOptions } from './_api';\nimport { isEvmAddress } from '../../utils/evm-address';\nimport { isSolAddress } from '../../utils/sol-address';\n\nexport type SupportedChainsMap = Map<\n Caip2ChainId,\n {\n /** `number` for EVM, `Caip2ChainId` for SVM */\n chainId: number | Caip2ChainId;\n crossChainSwapEnabled: boolean;\n crossChainTargetChainIds: ReadonlySet<Caip2ChainId>;\n swapEnabled: boolean;\n tokenList: boolean;\n /**\n * Recurring-swap metadata (passed through from `/info/chains[].recurring`).\n * Present only on EVM chains where Markr enables recurring swaps. Consumed\n * by `recurring/_chain-info.ts` `deriveRecurringChainInfo` (no second\n * `/info/chains` fetch).\n */\n recurring?: {\n minFrequencySeconds: number;\n supportedTokens: ReadonlyArray<{ address: EvmAddress; minimumAmount: string }>;\n };\n }\n>;\n\n/**\n * Checks whether the given source → target chain route is supported by Markr.\n */\nexport function isRouteSupported(\n supportedChains: SupportedChainsMap,\n sourceChainId: Caip2ChainId,\n targetChainId: Caip2ChainId,\n): boolean {\n const sourceChain = supportedChains.get(sourceChainId);\n const targetChain = supportedChains.get(targetChainId);\n\n if (!sourceChain || !targetChain) return false;\n\n if (sourceChainId === targetChainId) {\n return sourceChain.swapEnabled;\n }\n\n return sourceChain.crossChainSwapEnabled && sourceChain.crossChainTargetChainIds.has(targetChainId);\n}\n\nexport async function getSupportedChains(\n apiOptions: ApiOptions,\n disableCrossChainSwaps = false,\n): Promise<SupportedChainsMap> {\n const supportedChains: SupportedChainsMap = new Map();\n\n try {\n const chains = await markrGetInfoChains(apiOptions);\n const filteredSupportedChains = chains.filter(\n (chain): chain is SupportedChainsResponseItemEvm | SupportedChainsResponseItemSvm => {\n // Only include chains types that we support in the SDK (EVM and Solana for now).\n return chain.chainType === 'evm' || chain.chainType === 'svm';\n },\n );\n\n for (const chain of filteredSupportedChains) {\n const key: Caip2ChainId = typeof chain.chainId === 'number' ? eip155ChainIdToCaip2(chain.chainId) : chain.chainId;\n\n const isCrossChainSwapEnabled =\n chain.enabled_services.includes('cross-chain-quote') && chain.enabled_services.includes('cross-chain-swap');\n const isSwapEnabled = chain.enabled_services.includes('quote') && chain.enabled_services.includes('swap');\n const normalizedLaneChainIds = new Set<Caip2ChainId>();\n\n for (const lane of chain.lanes) {\n const laneChainId = typeof lane === 'number' ? eip155ChainIdToCaip2(lane) : lane;\n\n if (laneChainId !== key) {\n normalizedLaneChainIds.add(laneChainId);\n }\n }\n\n const crossChainSwapEnabled = disableCrossChainSwaps ? false : isCrossChainSwapEnabled;\n const crossChainTargetChainIds = crossChainSwapEnabled ? normalizedLaneChainIds : new Set<Caip2ChainId>();\n\n if (!isSwapEnabled && crossChainTargetChainIds.size === 0) {\n continue;\n }\n\n const recurring =\n chain.chainType === 'evm' && chain.recurring\n ? {\n minFrequencySeconds: chain.recurring.minFrequencySeconds,\n supportedTokens: chain.recurring.supportedTokens,\n }\n : undefined;\n\n supportedChains.set(key, {\n chainId: chain.chainId,\n crossChainSwapEnabled,\n crossChainTargetChainIds,\n swapEnabled: isSwapEnabled,\n tokenList: chain.enabled_services.includes('token-list'),\n ...(recurring ? { recurring } : {}),\n });\n }\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch supported chains from Markr API.',\n });\n }\n\n return supportedChains;\n}\n\nexport async function getSupportedTokens(\n apiOptions: ApiOptions,\n supportedChains: SupportedChainsMap,\n): Promise<Map<Caip2ChainId, Promise<TokenListResponse>>> {\n // Parallel fetch token lists for all supported chains.\n const supportedTokens: Map<Caip2ChainId, Promise<TokenListResponse>> = new Map();\n\n for (const [caip2ChainId, chainInfo] of supportedChains.entries()) {\n // Only fetch token list for EVM chains that support swaps.\n if (\n typeof chainInfo.chainId === 'number' &&\n chainInfo.tokenList &&\n (chainInfo.crossChainSwapEnabled || chainInfo.swapEnabled)\n ) {\n try {\n const tokenListPromise = markrGetTokenList(apiOptions, chainInfo.chainId);\n supportedTokens.set(caip2ChainId, tokenListPromise);\n } catch {\n // Ignore errors for individual token list fetches.\n // TODO: Logger?\n }\n }\n }\n\n return supportedTokens;\n}\n\n/**\n * Determine if the provided token address represents the native token for its chain.\n *\n * Markr represents EVM native tokens as the zero address and Solana native SOL\n * as the wrapped SOL mint address.\n */\nexport function isTokenAddressNative(address: string): boolean {\n return address === ERC_ZERO_ADDRESS || address === NATIVE_SOL_ADDRESS;\n}\n\n/**\n * Convert an `Asset` to the address string expected by the Markr API.\n *\n * For NATIVE assets the representation depends on the chain:\n * - EVM chains: `0x0000...0000` (ERC zero address)\n * - Solana chains: `So11111111111111111111111111111111111111112` (wrapped SOL mint)\n *\n * @param asset The asset to convert.\n * @param chainId CAIP-2 chain ID used to disambiguate NATIVE assets across chain types.\n */\nexport function assetToAddressString(asset: Asset, chainId: Caip2ChainId): string {\n if (asset.type === TokenType.NATIVE) {\n if (isSolanaNamespace(chainId)) {\n return NATIVE_SOL_ADDRESS;\n }\n\n return ERC_ZERO_ADDRESS;\n }\n\n return asset.address;\n}\n\n/**\n * Markr rejects `minAmountOut=0` for non-zero quotes.\n *\n * For very small quotes, integer truncation after slippage can produce `0`.\n * Clamp to `1` in that edge case to satisfy Markr API validation while\n * preserving normal slippage behavior for larger quotes.\n */\nexport function calculateMarkrMinimumAmountOut({\n amountOut,\n assetOut,\n slippageBps,\n}: Pick<Quote, 'amountOut' | 'assetOut' | 'slippageBps'>): bigint {\n const minAmountOut = calculateMinimumAmountOut({ amountOut, assetOut, slippageBps });\n\n if (amountOut > 0n && minAmountOut === 0n) {\n return 1n;\n }\n\n return minAmountOut;\n}\n\n/**\n * Returns additive fee amount charged in the source asset on the source chain.\n *\n * This is used for allowance/balance checks where users must fund both the\n * transfer input and any additive source-asset fee components. A safety buffer\n * is applied to the additive fee total to reduce the chance of under-approvals.\n *\n * @see https://docs.debridge.com/dln-details/overview/fee-structure#best-practice-checklist-for-integrators\n *\n * @param quote The quote containing the fees to analyze.\n * @param bufferBps Buffer in basis points applied to the additive fee total.\n * Defaults to 3000 bps (30%).\n */\nexport function getAdditiveSourceAssetFeeAmount(\n { assetIn, fees, sourceChain }: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>,\n bufferBps = 3_000,\n): bigint {\n const additiveSourceAssetFeeAmount = fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n if (assetIn.type === TokenType.NATIVE) {\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }\n\n if (assetIn.type === TokenType.ERC20) {\n return fee.token.type === TokenType.ERC20 && isAddressEqual(fee.token.address, assetIn.address)\n ? total + fee.amount\n : total;\n }\n\n return fee.token.type === TokenType.SPL && fee.token.address === assetIn.address ? total + fee.amount : total;\n }, 0n);\n\n return (additiveSourceAssetFeeAmount * BigInt(10_000 + bufferBps)) / 10_000n;\n}\n\n/**\n * Returns additive fee amount charged in native asset on the source chain.\n */\nexport function getAdditiveSourceNativeAssetFeeAmount({\n fees,\n sourceChain,\n}: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>): bigint {\n return fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }, 0n);\n}\n\nexport function quoteFromMarkrQuoteResponseData(\n data: QuoteResponseData,\n quoterProps: QuoterProps,\n partnerFeeBps: number,\n): Quote {\n const partnerFee: bigint = (data.amountOut * BigInt(partnerFeeBps)) / 10_000n;\n const amountOutAfterFees: bigint = data.amountOut - partnerFee;\n\n const fees: QuoteFees = [\n ...mapMarkrQuoteResponseDataFeesToQuoteFees(data.fees),\n // Add the partner fee as a QuoteFee component for Markr quotes.\n {\n type: 'partner',\n fundingModel: 'included',\n name: 'Core Fee',\n amount: partnerFee,\n chainId: quoterProps.targetChain.chainId,\n token: assetToFeeToken(quoterProps.targetAsset),\n },\n ];\n\n return {\n aggregator: {\n id: data.aggregator.id,\n logoUrl: data.aggregator.logo_url,\n name: data.aggregator.name,\n },\n amountIn: data.amountIn,\n amountOut: amountOutAfterFees,\n assetIn: quoterProps.sourceAsset,\n assetOut: quoterProps.targetAsset,\n expiresAt: data.expiredAt, // Markr returns `expiredAt` as Unix time in seconds; use as-is.\n fees,\n fromAddress: quoterProps.fromAddress,\n /**\n * This gasEstimate is rough or may be missing.\n * Consumers should use TransferManager.estimateGas for a more accurate estimate.\n */\n gasEstimate: data.gasEstimate,\n id: data.uuid,\n partnerFeeBps,\n serviceType: ServiceType.MARKR,\n slippageBps: quoterProps.slippageBps ?? data.recommendedSlippage,\n sourceChain: quoterProps.sourceChain,\n targetChain: quoterProps.targetChain,\n toAddress: quoterProps.toAddress,\n } satisfies Quote;\n}\n\nexport async function getPartnerFeeBps(apiOptions: ApiOptions): Promise<number> {\n try {\n const partnerInfo = await markrGetPartnerInfo(apiOptions);\n return partnerInfo.fee;\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch partner info from Markr API.',\n });\n }\n}\n\nexport function mapMarkrQuoteResponseDataFeesToQuoteFees(markrFees: QuoteResponseData['fees']): QuoteFees {\n if (!markrFees) {\n return [];\n }\n\n const fees: QuoteFee[] = [];\n\n for (const fee of markrFees) {\n const chainId = typeof fee.token.chainId === 'number' ? eip155ChainIdToCaip2(fee.token.chainId) : fee.token.chainId;\n\n let token: QuoteFeeToken | undefined;\n\n if (isTokenAddressNative(fee.token.address)) {\n token = { type: TokenType.NATIVE };\n } else if (isEvmAddress(fee.token.address)) {\n token = { type: TokenType.ERC20, address: fee.token.address };\n } else if (isSolAddress(fee.token.address)) {\n token = { type: TokenType.SPL, address: fee.token.address };\n }\n\n if (!token) {\n // Skip fee if we can't determine token type.\n continue;\n }\n\n fees.push({\n type: fee.type,\n fundingModel: fee.extra ? 'additive' : 'included',\n name: fee.name,\n amount: fee.amount,\n chainId,\n token,\n });\n }\n\n return fees;\n}\n\nfunction assetToFeeToken(asset: Asset): QuoteFeeToken {\n if (asset.type === TokenType.NATIVE) {\n return { type: TokenType.NATIVE };\n }\n\n if (asset.type === TokenType.SPL) {\n return { type: TokenType.SPL, address: asset.address };\n }\n\n return { type: TokenType.ERC20, address: asset.address };\n}\n\nexport type MarkrRouterAbi =\n | (typeof import('./_abis/swap-wrapper-abi'))['MARKR_SWAP_WRAPPER_ABI']\n | (typeof import('./_abis/cross-chain-swap-wrapper-abi'))['MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI'];\n\nexport async function getMarkrSwapWrapperAbi(crossChain: boolean): Promise<MarkrRouterAbi> {\n if (crossChain) {\n return (await import('./_abis/cross-chain-swap-wrapper-abi')).MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI;\n }\n\n return (await import('./_abis/swap-wrapper-abi')).MARKR_SWAP_WRAPPER_ABI;\n}\n\nfunction extractHexDataFromError(error: unknown): Hex | undefined {\n if (error instanceof BaseError) {\n const directData = (error as { data?: unknown }).data;\n if (isHex(directData)) return directData;\n\n let walkedData: Hex | undefined;\n error.walk((walkedError) => {\n const nestedData = (walkedError as { data?: unknown }).data;\n if (!walkedData && isHex(nestedData)) walkedData = nestedData;\n return false;\n });\n if (walkedData) return walkedData;\n\n const causeData = (error as { cause?: { data?: unknown } }).cause?.data;\n if (isHex(causeData)) return causeData;\n }\n\n if (!error || typeof error !== 'object') return undefined;\n\n const e = error as {\n data?: unknown;\n error?: { data?: unknown };\n cause?: { data?: unknown };\n response?: { data?: unknown };\n body?: { error?: { data?: unknown } };\n };\n\n if (isHex(e.data)) return e.data;\n if (isHex(e.error?.data)) return e.error.data;\n if (isHex(e.cause?.data)) return e.cause.data;\n if (isHex(e.response?.data)) return e.response.data;\n if (isHex(e.body?.error?.data)) return e.body.error.data;\n\n return undefined;\n}\n\nexport function decodeMarkrRevertError(abi: MarkrRouterAbi, error: unknown): string | null {\n const revertData = extractHexDataFromError(error);\n\n if (!revertData) {\n return null;\n }\n\n try {\n const decoded = decodeErrorResult({ abi, data: revertData });\n const decodedArgs = decoded.args.length > 0 ? `(${decoded.args.map(String).join(', ')})` : '()';\n\n return `${decoded.errorName}${decodedArgs}`;\n } catch {\n const selector = revertData.slice(0, 10).toLowerCase();\n\n for (const abiItem of abi) {\n if (abiItem.type !== 'error') {\n continue;\n }\n\n const signature = `${abiItem.name}(${abiItem.inputs.map((input) => input.type).join(',')})`;\n if (toFunctionSelector(signature).toLowerCase() === selector) {\n return `${abiItem.name}()`;\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_utils.cjs","names":["markrGetInfoChains","eip155ChainIdToCaip2","SdkError","ErrorReason","ErrorCode","markrGetTokenList","TokenType","isSolanaNamespace","NATIVE_SOL_ADDRESS","ERC_ZERO_ADDRESS","calculateMinimumAmountOut","ServiceType","markrGetPartnerInfo","isEvmAddress","isSolAddress"],"sources":["../../../src/transfer-service/markr/_utils.ts"],"sourcesContent":["import { ErrorCode, ErrorReason, SdkError } from '../../errors';\nimport {\n BaseError,\n decodeErrorResult,\n isAddressEqual,\n isHex,\n toFunctionSelector,\n type Address as EvmAddress,\n type Hex,\n} from 'viem';\nimport {\n type QuoteResponseData,\n type SupportedChainsResponseItemEvm,\n type SupportedChainsResponseItemSvm,\n type TokenListResponse,\n} from './_schema';\nimport { ERC_ZERO_ADDRESS, NATIVE_SOL_ADDRESS, ServiceType, TokenType } from '../../constants';\nimport { isSolanaNamespace } from '../../_utils/chain';\nimport { calculateMinimumAmountOut } from '../../_utils/math';\nimport { eip155ChainIdToCaip2 } from '../../utils/caip';\nimport type { Asset } from '../../types/asset';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Quote, QuoteFee, QuoteFees, QuoteFeeToken, QuoterProps } from '../../types/quote';\nimport { markrGetInfoChains, markrGetPartnerInfo, markrGetTokenList, type ApiOptions } from './_api';\nimport { isEvmAddress } from '../../utils/evm-address';\nimport { isSolAddress } from '../../utils/sol-address';\n\nexport type SupportedChainsMap = Map<\n Caip2ChainId,\n {\n /** `number` for EVM, `Caip2ChainId` for SVM */\n chainId: number | Caip2ChainId;\n crossChainSwapEnabled: boolean;\n crossChainTargetChainIds: ReadonlySet<Caip2ChainId>;\n swapEnabled: boolean;\n tokenList: boolean;\n /**\n * Chain wrapped-native ERC-20 address (e.g. WAVAX on 43114, WETH on 1).\n * Mirrors `/info/chains[].wrapped_token` — present on EVM mainnets that\n * Markr serves, undefined otherwise. Consumed by the recurring native-\n * input flow (`_chain-info.ts` → `RecurringChainInfoEntry`) so the SDK\n * doesn't need its own static wrapped-native map.\n */\n wrappedNativeAddress?: EvmAddress;\n /**\n * Recurring-swap metadata (passed through from `/info/chains[].recurring`).\n * Present only on EVM chains where Markr enables recurring swaps. Consumed\n * by `recurring/_chain-info.ts` `deriveRecurringChainInfo` (no second\n * `/info/chains` fetch).\n */\n recurring?: {\n minFrequencySeconds: number;\n };\n }\n>;\n\n/**\n * Checks whether the given source → target chain route is supported by Markr.\n */\nexport function isRouteSupported(\n supportedChains: SupportedChainsMap,\n sourceChainId: Caip2ChainId,\n targetChainId: Caip2ChainId,\n): boolean {\n const sourceChain = supportedChains.get(sourceChainId);\n const targetChain = supportedChains.get(targetChainId);\n\n if (!sourceChain || !targetChain) return false;\n\n if (sourceChainId === targetChainId) {\n return sourceChain.swapEnabled;\n }\n\n return sourceChain.crossChainSwapEnabled && sourceChain.crossChainTargetChainIds.has(targetChainId);\n}\n\nexport async function getSupportedChains(\n apiOptions: ApiOptions,\n disableCrossChainSwaps = false,\n): Promise<SupportedChainsMap> {\n const supportedChains: SupportedChainsMap = new Map();\n\n try {\n const chains = await markrGetInfoChains(apiOptions);\n const filteredSupportedChains = chains.filter(\n (chain): chain is SupportedChainsResponseItemEvm | SupportedChainsResponseItemSvm => {\n // Only include chains types that we support in the SDK (EVM and Solana for now).\n return chain.chainType === 'evm' || chain.chainType === 'svm';\n },\n );\n\n for (const chain of filteredSupportedChains) {\n const key: Caip2ChainId = typeof chain.chainId === 'number' ? eip155ChainIdToCaip2(chain.chainId) : chain.chainId;\n\n const isCrossChainSwapEnabled =\n chain.enabled_services.includes('cross-chain-quote') && chain.enabled_services.includes('cross-chain-swap');\n const isSwapEnabled = chain.enabled_services.includes('quote') && chain.enabled_services.includes('swap');\n const normalizedLaneChainIds = new Set<Caip2ChainId>();\n\n for (const lane of chain.lanes) {\n const laneChainId = typeof lane === 'number' ? eip155ChainIdToCaip2(lane) : lane;\n\n if (laneChainId !== key) {\n normalizedLaneChainIds.add(laneChainId);\n }\n }\n\n const crossChainSwapEnabled = disableCrossChainSwaps ? false : isCrossChainSwapEnabled;\n const crossChainTargetChainIds = crossChainSwapEnabled ? normalizedLaneChainIds : new Set<Caip2ChainId>();\n\n if (!isSwapEnabled && crossChainTargetChainIds.size === 0) {\n continue;\n }\n\n const recurring =\n chain.chainType === 'evm' && chain.recurring\n ? {\n minFrequencySeconds: chain.recurring.minFrequencySeconds,\n }\n : undefined;\n\n // `wrapped_token` is documented optional on Markr's `/info/chains` and\n // is only meaningful for EVM chains. Pass it through verbatim — the\n // recurring native-input flow keys off this to decide whether the SDK\n // can wrap on-chain for the user.\n const wrappedNativeAddress = chain.chainType === 'evm' ? chain.wrapped_token : undefined;\n\n supportedChains.set(key, {\n chainId: chain.chainId,\n crossChainSwapEnabled,\n crossChainTargetChainIds,\n swapEnabled: isSwapEnabled,\n tokenList: chain.enabled_services.includes('token-list'),\n ...(wrappedNativeAddress ? { wrappedNativeAddress } : {}),\n ...(recurring ? { recurring } : {}),\n });\n }\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch supported chains from Markr API.',\n });\n }\n\n return supportedChains;\n}\n\nexport async function getSupportedTokens(\n apiOptions: ApiOptions,\n supportedChains: SupportedChainsMap,\n): Promise<Map<Caip2ChainId, Promise<TokenListResponse>>> {\n // Parallel fetch token lists for all supported chains.\n const supportedTokens: Map<Caip2ChainId, Promise<TokenListResponse>> = new Map();\n\n for (const [caip2ChainId, chainInfo] of supportedChains.entries()) {\n // Only fetch token list for EVM chains that support swaps.\n if (\n typeof chainInfo.chainId === 'number' &&\n chainInfo.tokenList &&\n (chainInfo.crossChainSwapEnabled || chainInfo.swapEnabled)\n ) {\n try {\n const tokenListPromise = markrGetTokenList(apiOptions, chainInfo.chainId);\n supportedTokens.set(caip2ChainId, tokenListPromise);\n } catch {\n // Ignore errors for individual token list fetches.\n // TODO: Logger?\n }\n }\n }\n\n return supportedTokens;\n}\n\n/**\n * Determine if the provided token address represents the native token for its chain.\n *\n * Markr represents EVM native tokens as the zero address and Solana native SOL\n * as the wrapped SOL mint address.\n */\nexport function isTokenAddressNative(address: string): boolean {\n return address === ERC_ZERO_ADDRESS || address === NATIVE_SOL_ADDRESS;\n}\n\n/**\n * Convert an `Asset` to the address string expected by the Markr API.\n *\n * For NATIVE assets the representation depends on the chain:\n * - EVM chains: `0x0000...0000` (ERC zero address)\n * - Solana chains: `So11111111111111111111111111111111111111112` (wrapped SOL mint)\n *\n * @param asset The asset to convert.\n * @param chainId CAIP-2 chain ID used to disambiguate NATIVE assets across chain types.\n */\nexport function assetToAddressString(asset: Asset, chainId: Caip2ChainId): string {\n if (asset.type === TokenType.NATIVE) {\n if (isSolanaNamespace(chainId)) {\n return NATIVE_SOL_ADDRESS;\n }\n\n return ERC_ZERO_ADDRESS;\n }\n\n return asset.address;\n}\n\n/**\n * Markr rejects `minAmountOut=0` for non-zero quotes.\n *\n * For very small quotes, integer truncation after slippage can produce `0`.\n * Clamp to `1` in that edge case to satisfy Markr API validation while\n * preserving normal slippage behavior for larger quotes.\n */\nexport function calculateMarkrMinimumAmountOut({\n amountOut,\n assetOut,\n slippageBps,\n}: Pick<Quote, 'amountOut' | 'assetOut' | 'slippageBps'>): bigint {\n const minAmountOut = calculateMinimumAmountOut({ amountOut, assetOut, slippageBps });\n\n if (amountOut > 0n && minAmountOut === 0n) {\n return 1n;\n }\n\n return minAmountOut;\n}\n\n/**\n * Returns additive fee amount charged in the source asset on the source chain.\n *\n * This is used for allowance/balance checks where users must fund both the\n * transfer input and any additive source-asset fee components. A safety buffer\n * is applied to the additive fee total to reduce the chance of under-approvals.\n *\n * @see https://docs.debridge.com/dln-details/overview/fee-structure#best-practice-checklist-for-integrators\n *\n * @param quote The quote containing the fees to analyze.\n * @param bufferBps Buffer in basis points applied to the additive fee total.\n * Defaults to 3000 bps (30%).\n */\nexport function getAdditiveSourceAssetFeeAmount(\n { assetIn, fees, sourceChain }: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>,\n bufferBps = 3_000,\n): bigint {\n const additiveSourceAssetFeeAmount = fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n if (assetIn.type === TokenType.NATIVE) {\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }\n\n if (assetIn.type === TokenType.ERC20) {\n return fee.token.type === TokenType.ERC20 && isAddressEqual(fee.token.address, assetIn.address)\n ? total + fee.amount\n : total;\n }\n\n return fee.token.type === TokenType.SPL && fee.token.address === assetIn.address ? total + fee.amount : total;\n }, 0n);\n\n return (additiveSourceAssetFeeAmount * BigInt(10_000 + bufferBps)) / 10_000n;\n}\n\n/**\n * Returns additive fee amount charged in native asset on the source chain.\n */\nexport function getAdditiveSourceNativeAssetFeeAmount({\n fees,\n sourceChain,\n}: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>): bigint {\n return fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }, 0n);\n}\n\nexport function quoteFromMarkrQuoteResponseData(\n data: QuoteResponseData,\n quoterProps: QuoterProps,\n partnerFeeBps: number,\n): Quote {\n const partnerFee: bigint = (data.amountOut * BigInt(partnerFeeBps)) / 10_000n;\n const amountOutAfterFees: bigint = data.amountOut - partnerFee;\n\n const fees: QuoteFees = [\n ...mapMarkrQuoteResponseDataFeesToQuoteFees(data.fees),\n // Add the partner fee as a QuoteFee component for Markr quotes.\n {\n type: 'partner',\n fundingModel: 'included',\n name: 'Core Fee',\n amount: partnerFee,\n chainId: quoterProps.targetChain.chainId,\n token: assetToFeeToken(quoterProps.targetAsset),\n },\n ];\n\n return {\n aggregator: {\n id: data.aggregator.id,\n logoUrl: data.aggregator.logo_url,\n name: data.aggregator.name,\n },\n amountIn: data.amountIn,\n amountOut: amountOutAfterFees,\n assetIn: quoterProps.sourceAsset,\n assetOut: quoterProps.targetAsset,\n expiresAt: data.expiredAt, // Markr returns `expiredAt` as Unix time in seconds; use as-is.\n fees,\n fromAddress: quoterProps.fromAddress,\n /**\n * This gasEstimate is rough or may be missing.\n * Consumers should use TransferManager.estimateGas for a more accurate estimate.\n */\n gasEstimate: data.gasEstimate,\n id: data.uuid,\n partnerFeeBps,\n serviceType: ServiceType.MARKR,\n slippageBps: quoterProps.slippageBps ?? data.recommendedSlippage,\n sourceChain: quoterProps.sourceChain,\n targetChain: quoterProps.targetChain,\n toAddress: quoterProps.toAddress,\n } satisfies Quote;\n}\n\nexport async function getPartnerFeeBps(apiOptions: ApiOptions): Promise<number> {\n try {\n const partnerInfo = await markrGetPartnerInfo(apiOptions);\n return partnerInfo.fee;\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch partner info from Markr API.',\n });\n }\n}\n\nexport function mapMarkrQuoteResponseDataFeesToQuoteFees(markrFees: QuoteResponseData['fees']): QuoteFees {\n if (!markrFees) {\n return [];\n }\n\n const fees: QuoteFee[] = [];\n\n for (const fee of markrFees) {\n const chainId = typeof fee.token.chainId === 'number' ? eip155ChainIdToCaip2(fee.token.chainId) : fee.token.chainId;\n\n let token: QuoteFeeToken | undefined;\n\n if (isTokenAddressNative(fee.token.address)) {\n token = { type: TokenType.NATIVE };\n } else if (isEvmAddress(fee.token.address)) {\n token = { type: TokenType.ERC20, address: fee.token.address };\n } else if (isSolAddress(fee.token.address)) {\n token = { type: TokenType.SPL, address: fee.token.address };\n }\n\n if (!token) {\n // Skip fee if we can't determine token type.\n continue;\n }\n\n fees.push({\n type: fee.type,\n fundingModel: fee.extra ? 'additive' : 'included',\n name: fee.name,\n amount: fee.amount,\n chainId,\n token,\n });\n }\n\n return fees;\n}\n\nfunction assetToFeeToken(asset: Asset): QuoteFeeToken {\n if (asset.type === TokenType.NATIVE) {\n return { type: TokenType.NATIVE };\n }\n\n if (asset.type === TokenType.SPL) {\n return { type: TokenType.SPL, address: asset.address };\n }\n\n return { type: TokenType.ERC20, address: asset.address };\n}\n\nexport type MarkrRouterAbi =\n | (typeof import('./_abis/swap-wrapper-abi'))['MARKR_SWAP_WRAPPER_ABI']\n | (typeof import('./_abis/cross-chain-swap-wrapper-abi'))['MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI'];\n\nexport async function getMarkrSwapWrapperAbi(crossChain: boolean): Promise<MarkrRouterAbi> {\n if (crossChain) {\n return (await import('./_abis/cross-chain-swap-wrapper-abi')).MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI;\n }\n\n return (await import('./_abis/swap-wrapper-abi')).MARKR_SWAP_WRAPPER_ABI;\n}\n\nfunction extractHexDataFromError(error: unknown): Hex | undefined {\n if (error instanceof BaseError) {\n const directData = (error as { data?: unknown }).data;\n if (isHex(directData)) return directData;\n\n let walkedData: Hex | undefined;\n error.walk((walkedError) => {\n const nestedData = (walkedError as { data?: unknown }).data;\n if (!walkedData && isHex(nestedData)) walkedData = nestedData;\n return false;\n });\n if (walkedData) return walkedData;\n\n const causeData = (error as { cause?: { data?: unknown } }).cause?.data;\n if (isHex(causeData)) return causeData;\n }\n\n if (!error || typeof error !== 'object') return undefined;\n\n const e = error as {\n data?: unknown;\n error?: { data?: unknown };\n cause?: { data?: unknown };\n response?: { data?: unknown };\n body?: { error?: { data?: unknown } };\n };\n\n if (isHex(e.data)) return e.data;\n if (isHex(e.error?.data)) return e.error.data;\n if (isHex(e.cause?.data)) return e.cause.data;\n if (isHex(e.response?.data)) return e.response.data;\n if (isHex(e.body?.error?.data)) return e.body.error.data;\n\n return undefined;\n}\n\nexport function decodeMarkrRevertError(abi: MarkrRouterAbi, error: unknown): string | null {\n const revertData = extractHexDataFromError(error);\n\n if (!revertData) {\n return null;\n }\n\n try {\n const decoded = decodeErrorResult({ abi, data: revertData });\n const decodedArgs = decoded.args.length > 0 ? `(${decoded.args.map(String).join(', ')})` : '()';\n\n return `${decoded.errorName}${decodedArgs}`;\n } catch {\n const selector = revertData.slice(0, 10).toLowerCase();\n\n for (const abiItem of abi) {\n if (abiItem.type !== 'error') {\n continue;\n }\n\n const signature = `${abiItem.name}(${abiItem.inputs.map((input) => input.type).join(',')})`;\n if (toFunctionSelector(signature).toLowerCase() === selector) {\n return `${abiItem.name}()`;\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{ERC_ZERO_ADDRESS as e,NATIVE_SOL_ADDRESS as t,ServiceType as n,TokenType as r}from"../../constants.js";import{ErrorCode as i,ErrorReason as a,SdkError as o}from"../../errors.js";import{eip155ChainIdToCaip2 as s}from"../../utils/caip.js";import{isSolanaNamespace as c}from"../../_utils/chain.js";import{isEvmAddress as l}from"../../utils/evm-address.js";import{calculateMinimumAmountOut as u}from"../../_utils/math.js";import{markrGetInfoChains as d,markrGetPartnerInfo as f,markrGetTokenList as p}from"./_api.js";import{isSolAddress as m}from"../../utils/sol-address.js";import{isAddressEqual as h}from"viem";function g(e,t,n){let r=e.get(t),i=e.get(n);return!r||!i?!1:t===n?r.swapEnabled:r.crossChainSwapEnabled&&r.crossChainTargetChainIds.has(n)}async function _(e,t=!1){let n=new Map;try{let r=(await d(e)).filter(e=>e.chainType===`evm`||e.chainType===`svm`);for(let e of r){let r=typeof e.chainId==`number`?s(e.chainId):e.chainId,i=e.enabled_services.includes(`cross-chain-quote`)&&e.enabled_services.includes(`cross-chain-swap`),a=e.enabled_services.includes(`quote`)&&e.enabled_services.includes(`swap`),o=new Set;for(let t of e.lanes){let e=typeof t==`number`?s(t):t;e!==r&&o.add(e)}let c=t?!1:i,l=c?o:new Set;if(!a&&l.size===0)continue;let u=e.chainType===`evm`&&e.recurring?{minFrequencySeconds:e.recurring.minFrequencySeconds,supportedTokens:e.recurring.supportedTokens}:void 0;n.set(r,{chainId:e.chainId,crossChainSwapEnabled:c,crossChainTargetChainIds:l,swapEnabled:a,tokenList:e.enabled_services.includes(`token-list`),...u?{recurring:u}:{}})}}catch(e){throw new o(a.UNKNOWN,i.INITIALIZATION_FAILED,{cause:e,details:`Failed to fetch supported chains from Markr API.`})}return n}async function v(e,t){let n=new Map;for(let[r,i]of t.entries())if(typeof i.chainId==`number`&&i.tokenList&&(i.crossChainSwapEnabled||i.swapEnabled))try{let t=p(e,i.chainId);n.set(r,t)}catch{}return n}function y(e){return e===`0x0000000000000000000000000000000000000000`||e===`11111111111111111111111111111111`}function b(n,i){return n.type===r.NATIVE?c(i)?t:e:n.address}function x({amountOut:e,assetOut:t,slippageBps:n}){let r=u({amountOut:e,assetOut:t,slippageBps:n});return e>0n&&r===0n?1n:r}function S({assetIn:e,fees:t,sourceChain:n},i=3e3){return t.reduce((t,i)=>i.fundingModel!==`additive`||i.chainId!==n.chainId?t:e.type===r.NATIVE?i.token.type===r.NATIVE?t+i.amount:t:e.type===r.ERC20?i.token.type===r.ERC20&&h(i.token.address,e.address)?t+i.amount:t:i.token.type===r.SPL&&i.token.address===e.address?t+i.amount:t,0n)*BigInt(1e4+i)/10000n}function C({fees:e,sourceChain:t}){return e.reduce((e,n)=>n.fundingModel!==`additive`||n.chainId!==t.chainId?e:n.token.type===r.NATIVE?e+n.amount:e,0n)}function w(e,t,r){let i=e.amountOut*BigInt(r)/10000n,a=e.amountOut-i,o=[...E(e.fees),{type:`partner`,fundingModel:`included`,name:`Core Fee`,amount:i,chainId:t.targetChain.chainId,token:D(t.targetAsset)}];return{aggregator:{id:e.aggregator.id,logoUrl:e.aggregator.logo_url,name:e.aggregator.name},amountIn:e.amountIn,amountOut:a,assetIn:t.sourceAsset,assetOut:t.targetAsset,expiresAt:e.expiredAt,fees:o,fromAddress:t.fromAddress,gasEstimate:e.gasEstimate,id:e.uuid,partnerFeeBps:r,serviceType:n.MARKR,slippageBps:t.slippageBps??e.recommendedSlippage,sourceChain:t.sourceChain,targetChain:t.targetChain,toAddress:t.toAddress}}async function T(e){try{return(await f(e)).fee}catch(e){throw new o(a.UNKNOWN,i.INITIALIZATION_FAILED,{cause:e,details:`Failed to fetch partner info from Markr API.`})}}function E(e){if(!e)return[];let t=[];for(let n of e){let e=typeof n.token.chainId==`number`?s(n.token.chainId):n.token.chainId,i;y(n.token.address)?i={type:r.NATIVE}:l(n.token.address)?i={type:r.ERC20,address:n.token.address}:m(n.token.address)&&(i={type:r.SPL,address:n.token.address}),i&&t.push({type:n.type,fundingModel:n.extra?`additive`:`included`,name:n.name,amount:n.amount,chainId:e,token:i})}return t}function D(e){return e.type===r.NATIVE?{type:r.NATIVE}:e.type===r.SPL?{type:r.SPL,address:e.address}:{type:r.ERC20,address:e.address}}async function O(e){return e?(await import(`./_abis/cross-chain-swap-wrapper-abi.js`)).MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI:(await import(`./_abis/swap-wrapper-abi.js`)).MARKR_SWAP_WRAPPER_ABI}export{b as assetToAddressString,x as calculateMarkrMinimumAmountOut,S as getAdditiveSourceAssetFeeAmount,C as getAdditiveSourceNativeAssetFeeAmount,O as getMarkrSwapWrapperAbi,T as getPartnerFeeBps,_ as getSupportedChains,v as getSupportedTokens,g as isRouteSupported,y as isTokenAddressNative,w as quoteFromMarkrQuoteResponseData};
1
+ import{ERC_ZERO_ADDRESS as e,NATIVE_SOL_ADDRESS as t,ServiceType as n,TokenType as r}from"../../constants.js";import{ErrorCode as i,ErrorReason as a,SdkError as o}from"../../errors.js";import{eip155ChainIdToCaip2 as s}from"../../utils/caip.js";import{isSolanaNamespace as c}from"../../_utils/chain.js";import{isEvmAddress as l}from"../../utils/evm-address.js";import{calculateMinimumAmountOut as u}from"../../_utils/math.js";import{markrGetInfoChains as d,markrGetPartnerInfo as f,markrGetTokenList as p}from"./_api.js";import{isSolAddress as m}from"../../utils/sol-address.js";import{isAddressEqual as h}from"viem";function g(e,t,n){let r=e.get(t),i=e.get(n);return!r||!i?!1:t===n?r.swapEnabled:r.crossChainSwapEnabled&&r.crossChainTargetChainIds.has(n)}async function _(e,t=!1){let n=new Map;try{let r=(await d(e)).filter(e=>e.chainType===`evm`||e.chainType===`svm`);for(let e of r){let r=typeof e.chainId==`number`?s(e.chainId):e.chainId,i=e.enabled_services.includes(`cross-chain-quote`)&&e.enabled_services.includes(`cross-chain-swap`),a=e.enabled_services.includes(`quote`)&&e.enabled_services.includes(`swap`),o=new Set;for(let t of e.lanes){let e=typeof t==`number`?s(t):t;e!==r&&o.add(e)}let c=t?!1:i,l=c?o:new Set;if(!a&&l.size===0)continue;let u=e.chainType===`evm`&&e.recurring?{minFrequencySeconds:e.recurring.minFrequencySeconds}:void 0,d=e.chainType===`evm`?e.wrapped_token:void 0;n.set(r,{chainId:e.chainId,crossChainSwapEnabled:c,crossChainTargetChainIds:l,swapEnabled:a,tokenList:e.enabled_services.includes(`token-list`),...d?{wrappedNativeAddress:d}:{},...u?{recurring:u}:{}})}}catch(e){throw new o(a.UNKNOWN,i.INITIALIZATION_FAILED,{cause:e,details:`Failed to fetch supported chains from Markr API.`})}return n}async function v(e,t){let n=new Map;for(let[r,i]of t.entries())if(typeof i.chainId==`number`&&i.tokenList&&(i.crossChainSwapEnabled||i.swapEnabled))try{let t=p(e,i.chainId);n.set(r,t)}catch{}return n}function y(e){return e===`0x0000000000000000000000000000000000000000`||e===`11111111111111111111111111111111`}function b(n,i){return n.type===r.NATIVE?c(i)?t:e:n.address}function x({amountOut:e,assetOut:t,slippageBps:n}){let r=u({amountOut:e,assetOut:t,slippageBps:n});return e>0n&&r===0n?1n:r}function S({assetIn:e,fees:t,sourceChain:n},i=3e3){return t.reduce((t,i)=>i.fundingModel!==`additive`||i.chainId!==n.chainId?t:e.type===r.NATIVE?i.token.type===r.NATIVE?t+i.amount:t:e.type===r.ERC20?i.token.type===r.ERC20&&h(i.token.address,e.address)?t+i.amount:t:i.token.type===r.SPL&&i.token.address===e.address?t+i.amount:t,0n)*BigInt(1e4+i)/10000n}function C({fees:e,sourceChain:t}){return e.reduce((e,n)=>n.fundingModel!==`additive`||n.chainId!==t.chainId?e:n.token.type===r.NATIVE?e+n.amount:e,0n)}function w(e,t,r){let i=e.amountOut*BigInt(r)/10000n,a=e.amountOut-i,o=[...E(e.fees),{type:`partner`,fundingModel:`included`,name:`Core Fee`,amount:i,chainId:t.targetChain.chainId,token:D(t.targetAsset)}];return{aggregator:{id:e.aggregator.id,logoUrl:e.aggregator.logo_url,name:e.aggregator.name},amountIn:e.amountIn,amountOut:a,assetIn:t.sourceAsset,assetOut:t.targetAsset,expiresAt:e.expiredAt,fees:o,fromAddress:t.fromAddress,gasEstimate:e.gasEstimate,id:e.uuid,partnerFeeBps:r,serviceType:n.MARKR,slippageBps:t.slippageBps??e.recommendedSlippage,sourceChain:t.sourceChain,targetChain:t.targetChain,toAddress:t.toAddress}}async function T(e){try{return(await f(e)).fee}catch(e){throw new o(a.UNKNOWN,i.INITIALIZATION_FAILED,{cause:e,details:`Failed to fetch partner info from Markr API.`})}}function E(e){if(!e)return[];let t=[];for(let n of e){let e=typeof n.token.chainId==`number`?s(n.token.chainId):n.token.chainId,i;y(n.token.address)?i={type:r.NATIVE}:l(n.token.address)?i={type:r.ERC20,address:n.token.address}:m(n.token.address)&&(i={type:r.SPL,address:n.token.address}),i&&t.push({type:n.type,fundingModel:n.extra?`additive`:`included`,name:n.name,amount:n.amount,chainId:e,token:i})}return t}function D(e){return e.type===r.NATIVE?{type:r.NATIVE}:e.type===r.SPL?{type:r.SPL,address:e.address}:{type:r.ERC20,address:e.address}}async function O(e){return e?(await import(`./_abis/cross-chain-swap-wrapper-abi.js`)).MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI:(await import(`./_abis/swap-wrapper-abi.js`)).MARKR_SWAP_WRAPPER_ABI}export{b as assetToAddressString,x as calculateMarkrMinimumAmountOut,S as getAdditiveSourceAssetFeeAmount,C as getAdditiveSourceNativeAssetFeeAmount,O as getMarkrSwapWrapperAbi,T as getPartnerFeeBps,_ as getSupportedChains,v as getSupportedTokens,g as isRouteSupported,y as isTokenAddressNative,w as quoteFromMarkrQuoteResponseData};
2
2
  //# sourceMappingURL=_utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_utils.js","names":[],"sources":["../../../src/transfer-service/markr/_utils.ts"],"sourcesContent":["import { ErrorCode, ErrorReason, SdkError } from '../../errors';\nimport {\n BaseError,\n decodeErrorResult,\n isAddressEqual,\n isHex,\n toFunctionSelector,\n type Address as EvmAddress,\n type Hex,\n} from 'viem';\nimport {\n type QuoteResponseData,\n type SupportedChainsResponseItemEvm,\n type SupportedChainsResponseItemSvm,\n type TokenListResponse,\n} from './_schema';\nimport { ERC_ZERO_ADDRESS, NATIVE_SOL_ADDRESS, ServiceType, TokenType } from '../../constants';\nimport { isSolanaNamespace } from '../../_utils/chain';\nimport { calculateMinimumAmountOut } from '../../_utils/math';\nimport { eip155ChainIdToCaip2 } from '../../utils/caip';\nimport type { Asset } from '../../types/asset';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Quote, QuoteFee, QuoteFees, QuoteFeeToken, QuoterProps } from '../../types/quote';\nimport { markrGetInfoChains, markrGetPartnerInfo, markrGetTokenList, type ApiOptions } from './_api';\nimport { isEvmAddress } from '../../utils/evm-address';\nimport { isSolAddress } from '../../utils/sol-address';\n\nexport type SupportedChainsMap = Map<\n Caip2ChainId,\n {\n /** `number` for EVM, `Caip2ChainId` for SVM */\n chainId: number | Caip2ChainId;\n crossChainSwapEnabled: boolean;\n crossChainTargetChainIds: ReadonlySet<Caip2ChainId>;\n swapEnabled: boolean;\n tokenList: boolean;\n /**\n * Recurring-swap metadata (passed through from `/info/chains[].recurring`).\n * Present only on EVM chains where Markr enables recurring swaps. Consumed\n * by `recurring/_chain-info.ts` `deriveRecurringChainInfo` (no second\n * `/info/chains` fetch).\n */\n recurring?: {\n minFrequencySeconds: number;\n supportedTokens: ReadonlyArray<{ address: EvmAddress; minimumAmount: string }>;\n };\n }\n>;\n\n/**\n * Checks whether the given source → target chain route is supported by Markr.\n */\nexport function isRouteSupported(\n supportedChains: SupportedChainsMap,\n sourceChainId: Caip2ChainId,\n targetChainId: Caip2ChainId,\n): boolean {\n const sourceChain = supportedChains.get(sourceChainId);\n const targetChain = supportedChains.get(targetChainId);\n\n if (!sourceChain || !targetChain) return false;\n\n if (sourceChainId === targetChainId) {\n return sourceChain.swapEnabled;\n }\n\n return sourceChain.crossChainSwapEnabled && sourceChain.crossChainTargetChainIds.has(targetChainId);\n}\n\nexport async function getSupportedChains(\n apiOptions: ApiOptions,\n disableCrossChainSwaps = false,\n): Promise<SupportedChainsMap> {\n const supportedChains: SupportedChainsMap = new Map();\n\n try {\n const chains = await markrGetInfoChains(apiOptions);\n const filteredSupportedChains = chains.filter(\n (chain): chain is SupportedChainsResponseItemEvm | SupportedChainsResponseItemSvm => {\n // Only include chains types that we support in the SDK (EVM and Solana for now).\n return chain.chainType === 'evm' || chain.chainType === 'svm';\n },\n );\n\n for (const chain of filteredSupportedChains) {\n const key: Caip2ChainId = typeof chain.chainId === 'number' ? eip155ChainIdToCaip2(chain.chainId) : chain.chainId;\n\n const isCrossChainSwapEnabled =\n chain.enabled_services.includes('cross-chain-quote') && chain.enabled_services.includes('cross-chain-swap');\n const isSwapEnabled = chain.enabled_services.includes('quote') && chain.enabled_services.includes('swap');\n const normalizedLaneChainIds = new Set<Caip2ChainId>();\n\n for (const lane of chain.lanes) {\n const laneChainId = typeof lane === 'number' ? eip155ChainIdToCaip2(lane) : lane;\n\n if (laneChainId !== key) {\n normalizedLaneChainIds.add(laneChainId);\n }\n }\n\n const crossChainSwapEnabled = disableCrossChainSwaps ? false : isCrossChainSwapEnabled;\n const crossChainTargetChainIds = crossChainSwapEnabled ? normalizedLaneChainIds : new Set<Caip2ChainId>();\n\n if (!isSwapEnabled && crossChainTargetChainIds.size === 0) {\n continue;\n }\n\n const recurring =\n chain.chainType === 'evm' && chain.recurring\n ? {\n minFrequencySeconds: chain.recurring.minFrequencySeconds,\n supportedTokens: chain.recurring.supportedTokens,\n }\n : undefined;\n\n supportedChains.set(key, {\n chainId: chain.chainId,\n crossChainSwapEnabled,\n crossChainTargetChainIds,\n swapEnabled: isSwapEnabled,\n tokenList: chain.enabled_services.includes('token-list'),\n ...(recurring ? { recurring } : {}),\n });\n }\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch supported chains from Markr API.',\n });\n }\n\n return supportedChains;\n}\n\nexport async function getSupportedTokens(\n apiOptions: ApiOptions,\n supportedChains: SupportedChainsMap,\n): Promise<Map<Caip2ChainId, Promise<TokenListResponse>>> {\n // Parallel fetch token lists for all supported chains.\n const supportedTokens: Map<Caip2ChainId, Promise<TokenListResponse>> = new Map();\n\n for (const [caip2ChainId, chainInfo] of supportedChains.entries()) {\n // Only fetch token list for EVM chains that support swaps.\n if (\n typeof chainInfo.chainId === 'number' &&\n chainInfo.tokenList &&\n (chainInfo.crossChainSwapEnabled || chainInfo.swapEnabled)\n ) {\n try {\n const tokenListPromise = markrGetTokenList(apiOptions, chainInfo.chainId);\n supportedTokens.set(caip2ChainId, tokenListPromise);\n } catch {\n // Ignore errors for individual token list fetches.\n // TODO: Logger?\n }\n }\n }\n\n return supportedTokens;\n}\n\n/**\n * Determine if the provided token address represents the native token for its chain.\n *\n * Markr represents EVM native tokens as the zero address and Solana native SOL\n * as the wrapped SOL mint address.\n */\nexport function isTokenAddressNative(address: string): boolean {\n return address === ERC_ZERO_ADDRESS || address === NATIVE_SOL_ADDRESS;\n}\n\n/**\n * Convert an `Asset` to the address string expected by the Markr API.\n *\n * For NATIVE assets the representation depends on the chain:\n * - EVM chains: `0x0000...0000` (ERC zero address)\n * - Solana chains: `So11111111111111111111111111111111111111112` (wrapped SOL mint)\n *\n * @param asset The asset to convert.\n * @param chainId CAIP-2 chain ID used to disambiguate NATIVE assets across chain types.\n */\nexport function assetToAddressString(asset: Asset, chainId: Caip2ChainId): string {\n if (asset.type === TokenType.NATIVE) {\n if (isSolanaNamespace(chainId)) {\n return NATIVE_SOL_ADDRESS;\n }\n\n return ERC_ZERO_ADDRESS;\n }\n\n return asset.address;\n}\n\n/**\n * Markr rejects `minAmountOut=0` for non-zero quotes.\n *\n * For very small quotes, integer truncation after slippage can produce `0`.\n * Clamp to `1` in that edge case to satisfy Markr API validation while\n * preserving normal slippage behavior for larger quotes.\n */\nexport function calculateMarkrMinimumAmountOut({\n amountOut,\n assetOut,\n slippageBps,\n}: Pick<Quote, 'amountOut' | 'assetOut' | 'slippageBps'>): bigint {\n const minAmountOut = calculateMinimumAmountOut({ amountOut, assetOut, slippageBps });\n\n if (amountOut > 0n && minAmountOut === 0n) {\n return 1n;\n }\n\n return minAmountOut;\n}\n\n/**\n * Returns additive fee amount charged in the source asset on the source chain.\n *\n * This is used for allowance/balance checks where users must fund both the\n * transfer input and any additive source-asset fee components. A safety buffer\n * is applied to the additive fee total to reduce the chance of under-approvals.\n *\n * @see https://docs.debridge.com/dln-details/overview/fee-structure#best-practice-checklist-for-integrators\n *\n * @param quote The quote containing the fees to analyze.\n * @param bufferBps Buffer in basis points applied to the additive fee total.\n * Defaults to 3000 bps (30%).\n */\nexport function getAdditiveSourceAssetFeeAmount(\n { assetIn, fees, sourceChain }: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>,\n bufferBps = 3_000,\n): bigint {\n const additiveSourceAssetFeeAmount = fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n if (assetIn.type === TokenType.NATIVE) {\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }\n\n if (assetIn.type === TokenType.ERC20) {\n return fee.token.type === TokenType.ERC20 && isAddressEqual(fee.token.address, assetIn.address)\n ? total + fee.amount\n : total;\n }\n\n return fee.token.type === TokenType.SPL && fee.token.address === assetIn.address ? total + fee.amount : total;\n }, 0n);\n\n return (additiveSourceAssetFeeAmount * BigInt(10_000 + bufferBps)) / 10_000n;\n}\n\n/**\n * Returns additive fee amount charged in native asset on the source chain.\n */\nexport function getAdditiveSourceNativeAssetFeeAmount({\n fees,\n sourceChain,\n}: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>): bigint {\n return fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }, 0n);\n}\n\nexport function quoteFromMarkrQuoteResponseData(\n data: QuoteResponseData,\n quoterProps: QuoterProps,\n partnerFeeBps: number,\n): Quote {\n const partnerFee: bigint = (data.amountOut * BigInt(partnerFeeBps)) / 10_000n;\n const amountOutAfterFees: bigint = data.amountOut - partnerFee;\n\n const fees: QuoteFees = [\n ...mapMarkrQuoteResponseDataFeesToQuoteFees(data.fees),\n // Add the partner fee as a QuoteFee component for Markr quotes.\n {\n type: 'partner',\n fundingModel: 'included',\n name: 'Core Fee',\n amount: partnerFee,\n chainId: quoterProps.targetChain.chainId,\n token: assetToFeeToken(quoterProps.targetAsset),\n },\n ];\n\n return {\n aggregator: {\n id: data.aggregator.id,\n logoUrl: data.aggregator.logo_url,\n name: data.aggregator.name,\n },\n amountIn: data.amountIn,\n amountOut: amountOutAfterFees,\n assetIn: quoterProps.sourceAsset,\n assetOut: quoterProps.targetAsset,\n expiresAt: data.expiredAt, // Markr returns `expiredAt` as Unix time in seconds; use as-is.\n fees,\n fromAddress: quoterProps.fromAddress,\n /**\n * This gasEstimate is rough or may be missing.\n * Consumers should use TransferManager.estimateGas for a more accurate estimate.\n */\n gasEstimate: data.gasEstimate,\n id: data.uuid,\n partnerFeeBps,\n serviceType: ServiceType.MARKR,\n slippageBps: quoterProps.slippageBps ?? data.recommendedSlippage,\n sourceChain: quoterProps.sourceChain,\n targetChain: quoterProps.targetChain,\n toAddress: quoterProps.toAddress,\n } satisfies Quote;\n}\n\nexport async function getPartnerFeeBps(apiOptions: ApiOptions): Promise<number> {\n try {\n const partnerInfo = await markrGetPartnerInfo(apiOptions);\n return partnerInfo.fee;\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch partner info from Markr API.',\n });\n }\n}\n\nexport function mapMarkrQuoteResponseDataFeesToQuoteFees(markrFees: QuoteResponseData['fees']): QuoteFees {\n if (!markrFees) {\n return [];\n }\n\n const fees: QuoteFee[] = [];\n\n for (const fee of markrFees) {\n const chainId = typeof fee.token.chainId === 'number' ? eip155ChainIdToCaip2(fee.token.chainId) : fee.token.chainId;\n\n let token: QuoteFeeToken | undefined;\n\n if (isTokenAddressNative(fee.token.address)) {\n token = { type: TokenType.NATIVE };\n } else if (isEvmAddress(fee.token.address)) {\n token = { type: TokenType.ERC20, address: fee.token.address };\n } else if (isSolAddress(fee.token.address)) {\n token = { type: TokenType.SPL, address: fee.token.address };\n }\n\n if (!token) {\n // Skip fee if we can't determine token type.\n continue;\n }\n\n fees.push({\n type: fee.type,\n fundingModel: fee.extra ? 'additive' : 'included',\n name: fee.name,\n amount: fee.amount,\n chainId,\n token,\n });\n }\n\n return fees;\n}\n\nfunction assetToFeeToken(asset: Asset): QuoteFeeToken {\n if (asset.type === TokenType.NATIVE) {\n return { type: TokenType.NATIVE };\n }\n\n if (asset.type === TokenType.SPL) {\n return { type: TokenType.SPL, address: asset.address };\n }\n\n return { type: TokenType.ERC20, address: asset.address };\n}\n\nexport type MarkrRouterAbi =\n | (typeof import('./_abis/swap-wrapper-abi'))['MARKR_SWAP_WRAPPER_ABI']\n | (typeof import('./_abis/cross-chain-swap-wrapper-abi'))['MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI'];\n\nexport async function getMarkrSwapWrapperAbi(crossChain: boolean): Promise<MarkrRouterAbi> {\n if (crossChain) {\n return (await import('./_abis/cross-chain-swap-wrapper-abi')).MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI;\n }\n\n return (await import('./_abis/swap-wrapper-abi')).MARKR_SWAP_WRAPPER_ABI;\n}\n\nfunction extractHexDataFromError(error: unknown): Hex | undefined {\n if (error instanceof BaseError) {\n const directData = (error as { data?: unknown }).data;\n if (isHex(directData)) return directData;\n\n let walkedData: Hex | undefined;\n error.walk((walkedError) => {\n const nestedData = (walkedError as { data?: unknown }).data;\n if (!walkedData && isHex(nestedData)) walkedData = nestedData;\n return false;\n });\n if (walkedData) return walkedData;\n\n const causeData = (error as { cause?: { data?: unknown } }).cause?.data;\n if (isHex(causeData)) return causeData;\n }\n\n if (!error || typeof error !== 'object') return undefined;\n\n const e = error as {\n data?: unknown;\n error?: { data?: unknown };\n cause?: { data?: unknown };\n response?: { data?: unknown };\n body?: { error?: { data?: unknown } };\n };\n\n if (isHex(e.data)) return e.data;\n if (isHex(e.error?.data)) return e.error.data;\n if (isHex(e.cause?.data)) return e.cause.data;\n if (isHex(e.response?.data)) return e.response.data;\n if (isHex(e.body?.error?.data)) return e.body.error.data;\n\n return undefined;\n}\n\nexport function decodeMarkrRevertError(abi: MarkrRouterAbi, error: unknown): string | null {\n const revertData = extractHexDataFromError(error);\n\n if (!revertData) {\n return null;\n }\n\n try {\n const decoded = decodeErrorResult({ abi, data: revertData });\n const decodedArgs = decoded.args.length > 0 ? `(${decoded.args.map(String).join(', ')})` : '()';\n\n return `${decoded.errorName}${decodedArgs}`;\n } catch {\n const selector = revertData.slice(0, 10).toLowerCase();\n\n for (const abiItem of abi) {\n if (abiItem.type !== 'error') {\n continue;\n }\n\n const signature = `${abiItem.name}(${abiItem.inputs.map((input) => input.type).join(',')})`;\n if (toFunctionSelector(signature).toLowerCase() === selector) {\n return `${abiItem.name}()`;\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_utils.js","names":[],"sources":["../../../src/transfer-service/markr/_utils.ts"],"sourcesContent":["import { ErrorCode, ErrorReason, SdkError } from '../../errors';\nimport {\n BaseError,\n decodeErrorResult,\n isAddressEqual,\n isHex,\n toFunctionSelector,\n type Address as EvmAddress,\n type Hex,\n} from 'viem';\nimport {\n type QuoteResponseData,\n type SupportedChainsResponseItemEvm,\n type SupportedChainsResponseItemSvm,\n type TokenListResponse,\n} from './_schema';\nimport { ERC_ZERO_ADDRESS, NATIVE_SOL_ADDRESS, ServiceType, TokenType } from '../../constants';\nimport { isSolanaNamespace } from '../../_utils/chain';\nimport { calculateMinimumAmountOut } from '../../_utils/math';\nimport { eip155ChainIdToCaip2 } from '../../utils/caip';\nimport type { Asset } from '../../types/asset';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Quote, QuoteFee, QuoteFees, QuoteFeeToken, QuoterProps } from '../../types/quote';\nimport { markrGetInfoChains, markrGetPartnerInfo, markrGetTokenList, type ApiOptions } from './_api';\nimport { isEvmAddress } from '../../utils/evm-address';\nimport { isSolAddress } from '../../utils/sol-address';\n\nexport type SupportedChainsMap = Map<\n Caip2ChainId,\n {\n /** `number` for EVM, `Caip2ChainId` for SVM */\n chainId: number | Caip2ChainId;\n crossChainSwapEnabled: boolean;\n crossChainTargetChainIds: ReadonlySet<Caip2ChainId>;\n swapEnabled: boolean;\n tokenList: boolean;\n /**\n * Chain wrapped-native ERC-20 address (e.g. WAVAX on 43114, WETH on 1).\n * Mirrors `/info/chains[].wrapped_token` — present on EVM mainnets that\n * Markr serves, undefined otherwise. Consumed by the recurring native-\n * input flow (`_chain-info.ts` → `RecurringChainInfoEntry`) so the SDK\n * doesn't need its own static wrapped-native map.\n */\n wrappedNativeAddress?: EvmAddress;\n /**\n * Recurring-swap metadata (passed through from `/info/chains[].recurring`).\n * Present only on EVM chains where Markr enables recurring swaps. Consumed\n * by `recurring/_chain-info.ts` `deriveRecurringChainInfo` (no second\n * `/info/chains` fetch).\n */\n recurring?: {\n minFrequencySeconds: number;\n };\n }\n>;\n\n/**\n * Checks whether the given source → target chain route is supported by Markr.\n */\nexport function isRouteSupported(\n supportedChains: SupportedChainsMap,\n sourceChainId: Caip2ChainId,\n targetChainId: Caip2ChainId,\n): boolean {\n const sourceChain = supportedChains.get(sourceChainId);\n const targetChain = supportedChains.get(targetChainId);\n\n if (!sourceChain || !targetChain) return false;\n\n if (sourceChainId === targetChainId) {\n return sourceChain.swapEnabled;\n }\n\n return sourceChain.crossChainSwapEnabled && sourceChain.crossChainTargetChainIds.has(targetChainId);\n}\n\nexport async function getSupportedChains(\n apiOptions: ApiOptions,\n disableCrossChainSwaps = false,\n): Promise<SupportedChainsMap> {\n const supportedChains: SupportedChainsMap = new Map();\n\n try {\n const chains = await markrGetInfoChains(apiOptions);\n const filteredSupportedChains = chains.filter(\n (chain): chain is SupportedChainsResponseItemEvm | SupportedChainsResponseItemSvm => {\n // Only include chains types that we support in the SDK (EVM and Solana for now).\n return chain.chainType === 'evm' || chain.chainType === 'svm';\n },\n );\n\n for (const chain of filteredSupportedChains) {\n const key: Caip2ChainId = typeof chain.chainId === 'number' ? eip155ChainIdToCaip2(chain.chainId) : chain.chainId;\n\n const isCrossChainSwapEnabled =\n chain.enabled_services.includes('cross-chain-quote') && chain.enabled_services.includes('cross-chain-swap');\n const isSwapEnabled = chain.enabled_services.includes('quote') && chain.enabled_services.includes('swap');\n const normalizedLaneChainIds = new Set<Caip2ChainId>();\n\n for (const lane of chain.lanes) {\n const laneChainId = typeof lane === 'number' ? eip155ChainIdToCaip2(lane) : lane;\n\n if (laneChainId !== key) {\n normalizedLaneChainIds.add(laneChainId);\n }\n }\n\n const crossChainSwapEnabled = disableCrossChainSwaps ? false : isCrossChainSwapEnabled;\n const crossChainTargetChainIds = crossChainSwapEnabled ? normalizedLaneChainIds : new Set<Caip2ChainId>();\n\n if (!isSwapEnabled && crossChainTargetChainIds.size === 0) {\n continue;\n }\n\n const recurring =\n chain.chainType === 'evm' && chain.recurring\n ? {\n minFrequencySeconds: chain.recurring.minFrequencySeconds,\n }\n : undefined;\n\n // `wrapped_token` is documented optional on Markr's `/info/chains` and\n // is only meaningful for EVM chains. Pass it through verbatim — the\n // recurring native-input flow keys off this to decide whether the SDK\n // can wrap on-chain for the user.\n const wrappedNativeAddress = chain.chainType === 'evm' ? chain.wrapped_token : undefined;\n\n supportedChains.set(key, {\n chainId: chain.chainId,\n crossChainSwapEnabled,\n crossChainTargetChainIds,\n swapEnabled: isSwapEnabled,\n tokenList: chain.enabled_services.includes('token-list'),\n ...(wrappedNativeAddress ? { wrappedNativeAddress } : {}),\n ...(recurring ? { recurring } : {}),\n });\n }\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch supported chains from Markr API.',\n });\n }\n\n return supportedChains;\n}\n\nexport async function getSupportedTokens(\n apiOptions: ApiOptions,\n supportedChains: SupportedChainsMap,\n): Promise<Map<Caip2ChainId, Promise<TokenListResponse>>> {\n // Parallel fetch token lists for all supported chains.\n const supportedTokens: Map<Caip2ChainId, Promise<TokenListResponse>> = new Map();\n\n for (const [caip2ChainId, chainInfo] of supportedChains.entries()) {\n // Only fetch token list for EVM chains that support swaps.\n if (\n typeof chainInfo.chainId === 'number' &&\n chainInfo.tokenList &&\n (chainInfo.crossChainSwapEnabled || chainInfo.swapEnabled)\n ) {\n try {\n const tokenListPromise = markrGetTokenList(apiOptions, chainInfo.chainId);\n supportedTokens.set(caip2ChainId, tokenListPromise);\n } catch {\n // Ignore errors for individual token list fetches.\n // TODO: Logger?\n }\n }\n }\n\n return supportedTokens;\n}\n\n/**\n * Determine if the provided token address represents the native token for its chain.\n *\n * Markr represents EVM native tokens as the zero address and Solana native SOL\n * as the wrapped SOL mint address.\n */\nexport function isTokenAddressNative(address: string): boolean {\n return address === ERC_ZERO_ADDRESS || address === NATIVE_SOL_ADDRESS;\n}\n\n/**\n * Convert an `Asset` to the address string expected by the Markr API.\n *\n * For NATIVE assets the representation depends on the chain:\n * - EVM chains: `0x0000...0000` (ERC zero address)\n * - Solana chains: `So11111111111111111111111111111111111111112` (wrapped SOL mint)\n *\n * @param asset The asset to convert.\n * @param chainId CAIP-2 chain ID used to disambiguate NATIVE assets across chain types.\n */\nexport function assetToAddressString(asset: Asset, chainId: Caip2ChainId): string {\n if (asset.type === TokenType.NATIVE) {\n if (isSolanaNamespace(chainId)) {\n return NATIVE_SOL_ADDRESS;\n }\n\n return ERC_ZERO_ADDRESS;\n }\n\n return asset.address;\n}\n\n/**\n * Markr rejects `minAmountOut=0` for non-zero quotes.\n *\n * For very small quotes, integer truncation after slippage can produce `0`.\n * Clamp to `1` in that edge case to satisfy Markr API validation while\n * preserving normal slippage behavior for larger quotes.\n */\nexport function calculateMarkrMinimumAmountOut({\n amountOut,\n assetOut,\n slippageBps,\n}: Pick<Quote, 'amountOut' | 'assetOut' | 'slippageBps'>): bigint {\n const minAmountOut = calculateMinimumAmountOut({ amountOut, assetOut, slippageBps });\n\n if (amountOut > 0n && minAmountOut === 0n) {\n return 1n;\n }\n\n return minAmountOut;\n}\n\n/**\n * Returns additive fee amount charged in the source asset on the source chain.\n *\n * This is used for allowance/balance checks where users must fund both the\n * transfer input and any additive source-asset fee components. A safety buffer\n * is applied to the additive fee total to reduce the chance of under-approvals.\n *\n * @see https://docs.debridge.com/dln-details/overview/fee-structure#best-practice-checklist-for-integrators\n *\n * @param quote The quote containing the fees to analyze.\n * @param bufferBps Buffer in basis points applied to the additive fee total.\n * Defaults to 3000 bps (30%).\n */\nexport function getAdditiveSourceAssetFeeAmount(\n { assetIn, fees, sourceChain }: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>,\n bufferBps = 3_000,\n): bigint {\n const additiveSourceAssetFeeAmount = fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n if (assetIn.type === TokenType.NATIVE) {\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }\n\n if (assetIn.type === TokenType.ERC20) {\n return fee.token.type === TokenType.ERC20 && isAddressEqual(fee.token.address, assetIn.address)\n ? total + fee.amount\n : total;\n }\n\n return fee.token.type === TokenType.SPL && fee.token.address === assetIn.address ? total + fee.amount : total;\n }, 0n);\n\n return (additiveSourceAssetFeeAmount * BigInt(10_000 + bufferBps)) / 10_000n;\n}\n\n/**\n * Returns additive fee amount charged in native asset on the source chain.\n */\nexport function getAdditiveSourceNativeAssetFeeAmount({\n fees,\n sourceChain,\n}: Pick<Quote, 'assetIn' | 'fees' | 'sourceChain'>): bigint {\n return fees.reduce((total, fee) => {\n if (fee.fundingModel !== 'additive' || fee.chainId !== sourceChain.chainId) {\n return total;\n }\n\n return fee.token.type === TokenType.NATIVE ? total + fee.amount : total;\n }, 0n);\n}\n\nexport function quoteFromMarkrQuoteResponseData(\n data: QuoteResponseData,\n quoterProps: QuoterProps,\n partnerFeeBps: number,\n): Quote {\n const partnerFee: bigint = (data.amountOut * BigInt(partnerFeeBps)) / 10_000n;\n const amountOutAfterFees: bigint = data.amountOut - partnerFee;\n\n const fees: QuoteFees = [\n ...mapMarkrQuoteResponseDataFeesToQuoteFees(data.fees),\n // Add the partner fee as a QuoteFee component for Markr quotes.\n {\n type: 'partner',\n fundingModel: 'included',\n name: 'Core Fee',\n amount: partnerFee,\n chainId: quoterProps.targetChain.chainId,\n token: assetToFeeToken(quoterProps.targetAsset),\n },\n ];\n\n return {\n aggregator: {\n id: data.aggregator.id,\n logoUrl: data.aggregator.logo_url,\n name: data.aggregator.name,\n },\n amountIn: data.amountIn,\n amountOut: amountOutAfterFees,\n assetIn: quoterProps.sourceAsset,\n assetOut: quoterProps.targetAsset,\n expiresAt: data.expiredAt, // Markr returns `expiredAt` as Unix time in seconds; use as-is.\n fees,\n fromAddress: quoterProps.fromAddress,\n /**\n * This gasEstimate is rough or may be missing.\n * Consumers should use TransferManager.estimateGas for a more accurate estimate.\n */\n gasEstimate: data.gasEstimate,\n id: data.uuid,\n partnerFeeBps,\n serviceType: ServiceType.MARKR,\n slippageBps: quoterProps.slippageBps ?? data.recommendedSlippage,\n sourceChain: quoterProps.sourceChain,\n targetChain: quoterProps.targetChain,\n toAddress: quoterProps.toAddress,\n } satisfies Quote;\n}\n\nexport async function getPartnerFeeBps(apiOptions: ApiOptions): Promise<number> {\n try {\n const partnerInfo = await markrGetPartnerInfo(apiOptions);\n return partnerInfo.fee;\n } catch (error) {\n throw new SdkError(ErrorReason.UNKNOWN, ErrorCode.INITIALIZATION_FAILED, {\n cause: error,\n details: 'Failed to fetch partner info from Markr API.',\n });\n }\n}\n\nexport function mapMarkrQuoteResponseDataFeesToQuoteFees(markrFees: QuoteResponseData['fees']): QuoteFees {\n if (!markrFees) {\n return [];\n }\n\n const fees: QuoteFee[] = [];\n\n for (const fee of markrFees) {\n const chainId = typeof fee.token.chainId === 'number' ? eip155ChainIdToCaip2(fee.token.chainId) : fee.token.chainId;\n\n let token: QuoteFeeToken | undefined;\n\n if (isTokenAddressNative(fee.token.address)) {\n token = { type: TokenType.NATIVE };\n } else if (isEvmAddress(fee.token.address)) {\n token = { type: TokenType.ERC20, address: fee.token.address };\n } else if (isSolAddress(fee.token.address)) {\n token = { type: TokenType.SPL, address: fee.token.address };\n }\n\n if (!token) {\n // Skip fee if we can't determine token type.\n continue;\n }\n\n fees.push({\n type: fee.type,\n fundingModel: fee.extra ? 'additive' : 'included',\n name: fee.name,\n amount: fee.amount,\n chainId,\n token,\n });\n }\n\n return fees;\n}\n\nfunction assetToFeeToken(asset: Asset): QuoteFeeToken {\n if (asset.type === TokenType.NATIVE) {\n return { type: TokenType.NATIVE };\n }\n\n if (asset.type === TokenType.SPL) {\n return { type: TokenType.SPL, address: asset.address };\n }\n\n return { type: TokenType.ERC20, address: asset.address };\n}\n\nexport type MarkrRouterAbi =\n | (typeof import('./_abis/swap-wrapper-abi'))['MARKR_SWAP_WRAPPER_ABI']\n | (typeof import('./_abis/cross-chain-swap-wrapper-abi'))['MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI'];\n\nexport async function getMarkrSwapWrapperAbi(crossChain: boolean): Promise<MarkrRouterAbi> {\n if (crossChain) {\n return (await import('./_abis/cross-chain-swap-wrapper-abi')).MARKR_CROSS_CHAIN_SWAP_WRAPPER_ABI;\n }\n\n return (await import('./_abis/swap-wrapper-abi')).MARKR_SWAP_WRAPPER_ABI;\n}\n\nfunction extractHexDataFromError(error: unknown): Hex | undefined {\n if (error instanceof BaseError) {\n const directData = (error as { data?: unknown }).data;\n if (isHex(directData)) return directData;\n\n let walkedData: Hex | undefined;\n error.walk((walkedError) => {\n const nestedData = (walkedError as { data?: unknown }).data;\n if (!walkedData && isHex(nestedData)) walkedData = nestedData;\n return false;\n });\n if (walkedData) return walkedData;\n\n const causeData = (error as { cause?: { data?: unknown } }).cause?.data;\n if (isHex(causeData)) return causeData;\n }\n\n if (!error || typeof error !== 'object') return undefined;\n\n const e = error as {\n data?: unknown;\n error?: { data?: unknown };\n cause?: { data?: unknown };\n response?: { data?: unknown };\n body?: { error?: { data?: unknown } };\n };\n\n if (isHex(e.data)) return e.data;\n if (isHex(e.error?.data)) return e.error.data;\n if (isHex(e.cause?.data)) return e.cause.data;\n if (isHex(e.response?.data)) return e.response.data;\n if (isHex(e.body?.error?.data)) return e.body.error.data;\n\n return undefined;\n}\n\nexport function decodeMarkrRevertError(abi: MarkrRouterAbi, error: unknown): string | null {\n const revertData = extractHexDataFromError(error);\n\n if (!revertData) {\n return null;\n }\n\n try {\n const decoded = decodeErrorResult({ abi, data: revertData });\n const decodedArgs = decoded.args.length > 0 ? `(${decoded.args.map(String).join(', ')})` : '()';\n\n return `${decoded.errorName}${decodedArgs}`;\n } catch {\n const selector = revertData.slice(0, 10).toLowerCase();\n\n for (const abiItem of abi) {\n if (abiItem.type !== 'error') {\n continue;\n }\n\n const signature = `${abiItem.name}(${abiItem.inputs.map((input) => input.type).join(',')})`;\n if (toFunctionSelector(signature).toLowerCase() === selector) {\n return `${abiItem.name}()`;\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../errors.cjs`);require(`./types.cjs`);const t=require(`../_schema.cjs`),n=require(`../../fetch-utilities.cjs`),r=require(`../_api.cjs`),i=require(`./_schema.cjs`),a=/^0x[0-9a-fA-F]{64}$/;function o(t){if(t.error)return!(t.error instanceof e.AbortedError);let n=t.response?.status??0;return n===408||n===425||n===429||n>=500&&n<=599}function s(t){if(t===1/0||t===-1)return-1;if(!Number.isInteger(t)||t<2||t>365)throw new e.InvalidParamsError(`Invalid numberOfOrders`,`Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(t)}`);return t}async function c({apiBaseUrl:t,apiToken:a,fetch:o},c){let l=`/recurring/quote`,u=n.combineUrlPathnames(t,l),d={appId:c.appId,chainId:c.chainId,tokenIn:c.tokenIn,tokenInDecimals:c.tokenInDecimals,tokenOut:c.tokenOut,tokenOutDecimals:c.tokenOutDecimals,amount:c.amount.toString(),numberOfOrders:s(c.numberOfOrders),frequency:c.frequency,slippage:c.slippage},f=await n.fetchJson(u,{body:JSON.stringify(d),fetch:o,headers:r.getAuthHeaders(a),method:`POST`}),p=i.RecurringQuoteResponseSchema.safeParse(f);if(!p.success)throw new e.ResponseValidationError(`Invalid response from Markr "${l}" endpoint.`,p.error.issues,f);return p.data}async function l({apiBaseUrl:i,apiToken:a,fetch:s},c){let l=`/recurring/swap`,u=await n.fetchJson(n.combineUrlPathnames(i,l),{body:JSON.stringify(c),fetch:s,headers:r.getAuthHeaders(a),method:`POST`,retries:3,retryOn:o}),d=t.WrappedSwapTransactionResponseSchema.safeParse(u);if(!d.success)throw new e.ResponseValidationError(`Invalid response from Markr "${l}" endpoint.`,d.error.issues,u);return d.data}async function u({apiBaseUrl:t,apiToken:a,fetch:o},s){let c=new URLSearchParams({address:s.address});s.chainId!==void 0&&c.set(`chainId`,String(s.chainId)),s.status!==void 0&&c.set(`status`,s.status);let l=await n.fetchJson(n.combineUrlPathnames(t,`/recurring/orders?${c.toString()}`),{headers:r.getAuthHeaders(a),fetch:o}),u=i.ListRecurringOrdersResponseSchema.safeParse(l);if(!u.success)throw new e.ResponseValidationError(`Invalid response from Markr "/recurring/orders" endpoint.`,u.error.issues,l);return u.data}async function d({apiBaseUrl:i,apiToken:s,fetch:c},l,u){if(!a.test(u.orderId))throw new e.InvalidParamsError(`Invalid recurring orderId`,`Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(u.orderId)}`);let d=`/recurring/orders/${encodeURIComponent(u.orderId)}/${l}`,f=await n.fetchJson(n.combineUrlPathnames(i,d),{body:JSON.stringify({address:u.address,chainId:u.chainId}),fetch:c,headers:r.getAuthHeaders(s),method:`POST`,retries:3,retryOn:o}),p=t.WrappedSwapTransactionResponseSchema.safeParse(f);if(!p.success)throw new e.ResponseValidationError(`Invalid response from Markr "${d}" endpoint.`,p.error.issues,f);return p.data}function f(e,t){return d(e,`cancel`,t)}function p(e,t){return d(e,`pause`,t)}function m(e,t){return d(e,`unpause`,t)}exports.markrListRecurringOrders=u,exports.markrPrepareCancellation=f,exports.markrPreparePause=p,exports.markrPrepareUnpause=m,exports.markrRecurringQuote=c,exports.markrRecurringSwap=l;
1
+ const e=require(`../../../errors.cjs`);require(`./types.cjs`);const t=require(`../_schema.cjs`),n=require(`../../fetch-utilities.cjs`),r=require(`../_api.cjs`),i=require(`./_schema.cjs`),a=/^0x[0-9a-fA-F]{64}$/;function o(t){if(t.error)return!(t.error instanceof e.AbortedError);let n=t.response?.status??0;return n===408||n===425||n===429||n>=500&&n<=599}function s(t){if(t===1/0||t===-1)return-1;if(!Number.isInteger(t)||t<2||t>365)throw new e.InvalidParamsError(`Invalid numberOfOrders`,`Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(t)}`);return t}async function c({apiBaseUrl:t,apiToken:a,fetch:o},c){let l=`/recurring/quote`,u=n.combineUrlPathnames(t,l),d={appId:c.appId,chainId:c.chainId,tokenIn:c.tokenIn,tokenInDecimals:c.tokenInDecimals,tokenOut:c.tokenOut,tokenOutDecimals:c.tokenOutDecimals,amount:c.amount.toString(),numberOfOrders:s(c.numberOfOrders),frequency:c.frequency,slippage:c.slippage},f=await n.fetchJson(u,{body:JSON.stringify(d),fetch:o,headers:r.getAuthHeaders(a),method:`POST`}),p=i.RecurringQuoteResponseSchema.safeParse(f);if(!p.success)throw new e.ResponseValidationError(`Invalid response from Markr "${l}" endpoint.`,p.error.issues,f);return p.data}async function l({apiBaseUrl:t,apiToken:a,fetch:s},c){let l=`/recurring/swap`,u=await n.fetchJson(n.combineUrlPathnames(t,l),{body:JSON.stringify(c),fetch:s,headers:r.getAuthHeaders(a),method:`POST`,retries:3,retryOn:o}),d=i.RecurringSwapResponseSchema.safeParse(u);if(!d.success)throw new e.ResponseValidationError(`Invalid response from Markr "${l}" endpoint.`,d.error.issues,u);return d.data}async function u({apiBaseUrl:t,apiToken:a,fetch:o},s){let c=new URLSearchParams({address:s.address});s.chainId!==void 0&&c.set(`chainId`,String(s.chainId)),s.status!==void 0&&c.set(`status`,s.status);let l=await n.fetchJson(n.combineUrlPathnames(t,`/recurring/orders?${c.toString()}`),{headers:r.getAuthHeaders(a),fetch:o}),u=i.ListRecurringOrdersResponseSchema.safeParse(l);if(!u.success)throw new e.ResponseValidationError(`Invalid response from Markr "/recurring/orders" endpoint.`,u.error.issues,l);return u.data}async function d({apiBaseUrl:i,apiToken:s,fetch:c},l,u){if(!a.test(u.orderId))throw new e.InvalidParamsError(`Invalid recurring orderId`,`Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(u.orderId)}`);let d=`/recurring/orders/${encodeURIComponent(u.orderId)}/${l}`,f=await n.fetchJson(n.combineUrlPathnames(i,d),{body:JSON.stringify({address:u.address,chainId:u.chainId}),fetch:c,headers:r.getAuthHeaders(s),method:`POST`,retries:3,retryOn:o}),p=t.WrappedSwapTransactionResponseSchema.safeParse(f);if(!p.success)throw new e.ResponseValidationError(`Invalid response from Markr "${d}" endpoint.`,p.error.issues,f);return p.data}function f(e,t){return d(e,`cancel`,t)}function p(e,t){return d(e,`pause`,t)}function m(e,t){return d(e,`unpause`,t)}exports.markrListRecurringOrders=u,exports.markrPrepareCancellation=f,exports.markrPreparePause=p,exports.markrPrepareUnpause=m,exports.markrRecurringQuote=c,exports.markrRecurringSwap=l;
2
2
  //# sourceMappingURL=_api.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_api.cjs","names":["AbortedError","InvalidParamsError","combineUrlPathnames","fetchJson","getAuthHeaders","RecurringQuoteResponseSchema","ResponseValidationError","WrappedSwapTransactionResponseSchema","ListRecurringOrdersResponseSchema"],"sources":["../../../../src/transfer-service/markr/recurring/_api.ts"],"sourcesContent":["import { AbortedError, InvalidParamsError, ResponseValidationError } from '../../../errors';\nimport { combineUrlPathnames, fetchJson } from '../../fetch-utilities';\nimport { WrappedSwapTransactionResponseSchema, type WrappedSwapTransactionResponse } from '../_schema';\nimport { getAuthHeaders, type ApiOptions } from '../_api';\nimport { ListRecurringOrdersResponseSchema, RecurringQuoteResponseSchema } from './_schema';\nimport {\n RECURRING_UNLIMITED_ORDERS_SENTINEL,\n type ListRecurringOrdersParams,\n type ListRecurringOrdersResponse,\n type RecurringOrderActionApiParams,\n type RecurringQuoteParams,\n type RecurringQuoteResponse,\n type RecurringSwapParams,\n} from './types';\n\n/** bytes32 hex — same regex as `RecurringOrderSchema.orderId`. */\nconst ORDER_ID_REGEX = /^0x[0-9a-fA-F]{64}$/;\n\n/**\n * Retry policy for `/recurring/swap` and the order-action endpoints\n * (`/recurring/orders/{}/cancel`, `/recurring/orders/{}/pause`,\n * `/recurring/orders/{}/unpause`) — Markr documents all of them as\n * idempotent on the same params, so transient network / 5xx failures are\n * safe to replay. Mirrors the `markrGetCrossChainStatus` pattern but admits\n * POST (whereas the GET variant only whitelists safe verbs) and does NOT\n * retry 404 (for these endpoints, 404 means the resource genuinely doesn't\n * exist).\n */\nconst IDEMPOTENT_POST_RETRY_COUNT = 3;\n\nfunction retryIdempotentPost(ctx: { error?: unknown; response?: Response }): boolean {\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) return false;\n return true;\n }\n const status = ctx.response?.status ?? 0;\n return status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599);\n}\n\n/**\n * Translate the SDK-public `numberOfOrders` value into Markr's wire shape:\n * - `Infinity` or `-1` → wire sentinel `-1` (unlimited).\n * - Integer in `[2, 365]` → passthrough (matches Markr's documented bounds).\n * - Anything else (`NaN`, `0`, `1`, negatives other than `-1`, non-integers,\n * values > 365, `-Infinity`) → `InvalidParamsError` at the SDK boundary.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1quote/post — Markr's\n * `numberOfOrders` spec: `-1` for infinite, or integer 2-365 inclusive.\n */\nfunction translateNumberOfOrders(input: number): number {\n if (input === Infinity || input === RECURRING_UNLIMITED_ORDERS_SENTINEL) {\n return RECURRING_UNLIMITED_ORDERS_SENTINEL;\n }\n\n if (!Number.isInteger(input) || input < 2 || input > 365) {\n throw new InvalidParamsError(\n 'Invalid numberOfOrders',\n `Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(input)}`,\n );\n }\n\n return input;\n}\n\nexport async function markrRecurringQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringQuoteParams,\n): Promise<RecurringQuoteResponse> {\n const endpoint = '/recurring/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const body = {\n appId: params.appId,\n chainId: params.chainId,\n tokenIn: params.tokenIn,\n tokenInDecimals: params.tokenInDecimals,\n tokenOut: params.tokenOut,\n tokenOutDecimals: params.tokenOutDecimals,\n // Inline `.toString()` — `JSON.stringify` cannot serialize bigint, so the\n // wire shape stays a decimal string.\n amount: params.amount.toString(),\n numberOfOrders: translateNumberOfOrders(params.numberOfOrders),\n frequency: params.frequency,\n slippage: params.slippage,\n };\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(body),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = RecurringQuoteResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the first-fill swap calldata for a previously quoted recurring swap.\n *\n * Allowance must already cover `totalAmountIn` from the quote. The returned\n * `WrappedSwapTransactionResponse` is signed and broadcast by the caller (the\n * SDK does not sign on the recurring path).\n *\n * Idempotent on the same `uuid` per the Markr docs — retried on transient\n * network / 5xx failures via `retryIdempotentPost`.\n */\nexport async function markrRecurringSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringSwapParams,\n): Promise<WrappedSwapTransactionResponse> {\n const endpoint = '/recurring/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = WrappedSwapTransactionResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\nexport async function markrListRecurringOrders(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: ListRecurringOrdersParams,\n): Promise<ListRecurringOrdersResponse> {\n // `URLSearchParams` URL-encodes each value — defense-in-depth against a\n // caller reaching from JS (no TS brand check) and passing an `address` like\n // `0x…&adminKey=x` that would otherwise inject extra query params.\n const searchParams = new URLSearchParams({ address: params.address });\n if (params.chainId !== undefined) searchParams.set('chainId', String(params.chainId));\n if (params.status !== undefined) searchParams.set('status', params.status);\n\n const endpoint = `/recurring/orders?${searchParams.toString()}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = ListRecurringOrdersResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"/recurring/orders\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Shared implementation for the three calldata-returning order-action\n * endpoints (`cancel`, `pause`, `unpause`). All three share:\n * - identical body shape `{ address, chainId }`\n * - identical response shape (`WrappedSwapTransactionResponse`)\n * - identical bytes32 `orderId` validation (path-traversal defense before\n * URL interpolation — catches stale UUIDs / malformed values as a clear\n * `InvalidParamsError` at the SDK boundary rather than an opaque 404\n * from the orchestrator)\n * - identical idempotent-POST retry policy\n *\n * Kept private so all three exported helpers go through the same wire\n * contract — adding a new field (or tightening a check) is a one-place\n * change here.\n */\nasync function postRecurringOrderAction(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n action: 'cancel' | 'pause' | 'unpause',\n params: { orderId: `0x${string}`; address: string; chainId: number },\n): Promise<WrappedSwapTransactionResponse> {\n if (!ORDER_ID_REGEX.test(params.orderId)) {\n throw new InvalidParamsError(\n 'Invalid recurring orderId',\n `Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(params.orderId)}`,\n );\n }\n\n const endpoint = `/recurring/orders/${encodeURIComponent(params.orderId)}/${action}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify({ address: params.address, chainId: params.chainId }),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = WrappedSwapTransactionResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the calldata to cancel the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — cancellation only takes effect after\n * the TX confirms, at which point Markr transitions the order's `status`\n * to `'cancelled'`.\n *\n * Only `'active'` and `'paused'` orders can be cancelled per the docs; an\n * attempt to cancel a `'completed'` (or otherwise non-cancellable) order\n * surfaces as `HttpError(400)` from the orchestrator.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple per the Markr\n * docs — retried on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1orders~1{orderId}~1cancel/post\n */\nexport function markrPrepareCancellation(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'cancel', params);\n}\n\n/**\n * Fetches the calldata to pause the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — the schedule only transitions to\n * `status: 'paused'` once the TX confirms and Markr observes the on-chain\n * event.\n *\n * Only `'active'` orders can be paused per the docs; an attempt to pause a\n * non-`'active'` order surfaces as `HttpError(400)` from the orchestrator.\n *\n * Pausing preserves the existing ERC-20 allowance — when the user later\n * unpauses, no re-approval is required and no new native schedule fee is\n * charged. This is the key UX benefit of pause over cancel-and-recreate.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1pause/post\n */\nexport function markrPreparePause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'pause', params);\n}\n\n/**\n * Fetches the calldata to unpause (resume) the recurring schedule\n * identified by `orderId`. The returned `WrappedSwapTransactionResponse` is\n * signed and broadcast on-chain by the caller — the schedule only\n * transitions back to `status: 'active'` once the TX confirms and Markr\n * observes the on-chain event.\n *\n * Only `'paused'` orders can be unpaused per the docs; an attempt to\n * unpause a non-`'paused'` order surfaces as `HttpError(400)` from the\n * orchestrator.\n *\n * Resumes execution from where the schedule left off — remaining fills\n * continue on the original `frequency` cadence and the orchestrator\n * recomputes `nextExecutionAt` after the TX confirms.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1unpause/post\n */\nexport function markrPrepareUnpause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'unpause', params);\n}\n"],"mappings":"2LAgBM,EAAiB,sBAcvB,SAAS,EAAoB,EAAwD,CACnF,GAAI,EAAI,MAEN,MADA,EAAI,EAAI,iBAAiBA,EAAAA,cAG3B,IAAM,EAAS,EAAI,UAAU,QAAU,EACvC,OAAO,IAAW,KAAO,IAAW,KAAO,IAAW,KAAQ,GAAU,KAAO,GAAU,IAa3F,SAAS,EAAwB,EAAuB,CACtD,GAAI,IAAU,KAAY,IAAA,GACxB,MAAA,GAGF,GAAI,CAAC,OAAO,UAAU,EAAM,EAAI,EAAQ,GAAK,EAAQ,IACnD,MAAM,IAAIC,EAAAA,mBACR,yBACA,sEAAsE,OAAO,EAAM,GACpF,CAGH,OAAO,EAGT,eAAsB,EACpB,CAAE,aAAY,WAAU,MAAO,GAC/B,EACiC,CACjC,IAAM,EAAW,mBACX,EAAMC,EAAAA,oBAAoB,EAAY,EAAS,CAE/C,EAAO,CACX,MAAO,EAAO,MACd,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,gBAAiB,EAAO,gBACxB,SAAU,EAAO,SACjB,iBAAkB,EAAO,iBAGzB,OAAQ,EAAO,OAAO,UAAU,CAChC,eAAgB,EAAwB,EAAO,eAAe,CAC9D,UAAW,EAAO,UAClB,SAAU,EAAO,SAClB,CAEK,EAAU,MAAMC,EAAAA,UAAU,EAAK,CACnC,KAAM,KAAK,UAAU,EAAK,CAC1B,MAAO,EACP,QAASC,EAAAA,eAAe,EAAS,CACjC,OAAQ,OACT,CAAC,CAEI,EAASC,EAAAA,6BAA6B,UAAU,EAAQ,CAE9D,GAAI,CAAC,EAAO,QACV,MAAM,IAAIC,EAAAA,wBACR,gCAAgC,EAAS,aACzC,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAahB,eAAsB,EACpB,CAAE,aAAY,WAAU,MAAO,GAC/B,EACyC,CACzC,IAAM,EAAW,kBAGX,EAAU,MAAMH,EAAAA,UAFVD,EAAAA,oBAAoB,EAAY,EAAS,CAEhB,CACnC,KAAM,KAAK,UAAU,EAAO,CAC5B,MAAO,EACP,QAASE,EAAAA,eAAe,EAAS,CACjC,OAAQ,OACR,QAAS,EACT,QAAS,EACV,CAAC,CAEI,EAASG,EAAAA,qCAAqC,UAAU,EAAQ,CAEtE,GAAI,CAAC,EAAO,QACV,MAAM,IAAID,EAAAA,wBACR,gCAAgC,EAAS,aACzC,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAGhB,eAAsB,EACpB,CAAE,aAAY,WAAU,MAAO,GAC/B,EACsC,CAItC,IAAM,EAAe,IAAI,gBAAgB,CAAE,QAAS,EAAO,QAAS,CAAC,CACjE,EAAO,UAAY,IAAA,IAAW,EAAa,IAAI,UAAW,OAAO,EAAO,QAAQ,CAAC,CACjF,EAAO,SAAW,IAAA,IAAW,EAAa,IAAI,SAAU,EAAO,OAAO,CAK1E,IAAM,EAAU,MAAMH,EAAAA,UAFVD,EAAAA,oBAAoB,EADf,qBAAqB,EAAa,UAAU,GACR,CAEhB,CAAE,QAASE,EAAAA,eAAe,EAAS,CAAE,MAAO,EAAa,CAAC,CAEzF,EAASI,EAAAA,kCAAkC,UAAU,EAAQ,CAEnE,GAAI,CAAC,EAAO,QACV,MAAM,IAAIF,EAAAA,wBACR,4DACA,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAkBhB,eAAe,EACb,CAAE,aAAY,WAAU,MAAO,GAC/B,EACA,EACyC,CACzC,GAAI,CAAC,EAAe,KAAK,EAAO,QAAQ,CACtC,MAAM,IAAIL,EAAAA,mBACR,4BACA,qDAAqD,KAAK,UAAU,EAAO,QAAQ,GACpF,CAGH,IAAM,EAAW,qBAAqB,mBAAmB,EAAO,QAAQ,CAAC,GAAG,IAGtE,EAAU,MAAME,EAAAA,UAFVD,EAAAA,oBAAoB,EAAY,EAAS,CAEhB,CACnC,KAAM,KAAK,UAAU,CAAE,QAAS,EAAO,QAAS,QAAS,EAAO,QAAS,CAAC,CAC1E,MAAO,EACP,QAASE,EAAAA,eAAe,EAAS,CACjC,OAAQ,OACR,QAAS,EACT,QAAS,EACV,CAAC,CAEI,EAASG,EAAAA,qCAAqC,UAAU,EAAQ,CAEtE,GAAI,CAAC,EAAO,QACV,MAAM,IAAID,EAAAA,wBACR,gCAAgC,EAAS,aACzC,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAmBhB,SAAgB,EACd,EACA,EACyC,CACzC,OAAO,EAAyB,EAAY,SAAU,EAAO,CAsB/D,SAAgB,EACd,EACA,EACyC,CACzC,OAAO,EAAyB,EAAY,QAAS,EAAO,CAuB9D,SAAgB,EACd,EACA,EACyC,CACzC,OAAO,EAAyB,EAAY,UAAW,EAAO"}
1
+ {"version":3,"file":"_api.cjs","names":["AbortedError","InvalidParamsError","combineUrlPathnames","fetchJson","getAuthHeaders","RecurringQuoteResponseSchema","ResponseValidationError","RecurringSwapResponseSchema","ListRecurringOrdersResponseSchema","WrappedSwapTransactionResponseSchema"],"sources":["../../../../src/transfer-service/markr/recurring/_api.ts"],"sourcesContent":["import { AbortedError, InvalidParamsError, ResponseValidationError } from '../../../errors';\nimport { combineUrlPathnames, fetchJson } from '../../fetch-utilities';\nimport { WrappedSwapTransactionResponseSchema, type WrappedSwapTransactionResponse } from '../_schema';\nimport { getAuthHeaders, type ApiOptions } from '../_api';\nimport {\n ListRecurringOrdersResponseSchema,\n RecurringQuoteResponseSchema,\n RecurringSwapResponseSchema,\n} from './_schema';\nimport {\n RECURRING_UNLIMITED_ORDERS_SENTINEL,\n type ListRecurringOrdersParams,\n type ListRecurringOrdersResponse,\n type RecurringOrderActionApiParams,\n type RecurringQuoteParams,\n type RecurringQuoteResponse,\n type RecurringSwapParams,\n type RecurringSwapResponse,\n} from './types';\n\n/** bytes32 hex — same regex as `RecurringOrderSchema.orderId`. */\nconst ORDER_ID_REGEX = /^0x[0-9a-fA-F]{64}$/;\n\n/**\n * Retry policy for `/recurring/swap` and the order-action endpoints\n * (`/recurring/orders/{}/cancel`, `/recurring/orders/{}/pause`,\n * `/recurring/orders/{}/unpause`) — Markr documents all of them as\n * idempotent on the same params, so transient network / 5xx failures are\n * safe to replay. Mirrors the `markrGetCrossChainStatus` pattern but admits\n * POST (whereas the GET variant only whitelists safe verbs) and does NOT\n * retry 404 (for these endpoints, 404 means the resource genuinely doesn't\n * exist).\n */\nconst IDEMPOTENT_POST_RETRY_COUNT = 3;\n\nfunction retryIdempotentPost(ctx: { error?: unknown; response?: Response }): boolean {\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) return false;\n return true;\n }\n const status = ctx.response?.status ?? 0;\n return status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599);\n}\n\n/**\n * Translate the SDK-public `numberOfOrders` value into Markr's wire shape:\n * - `Infinity` or `-1` → wire sentinel `-1` (unlimited).\n * - Integer in `[2, 365]` → passthrough (matches Markr's documented bounds).\n * - Anything else (`NaN`, `0`, `1`, negatives other than `-1`, non-integers,\n * values > 365, `-Infinity`) → `InvalidParamsError` at the SDK boundary.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1quote/post — Markr's\n * `numberOfOrders` spec: `-1` for infinite, or integer 2-365 inclusive.\n */\nfunction translateNumberOfOrders(input: number): number {\n if (input === Infinity || input === RECURRING_UNLIMITED_ORDERS_SENTINEL) {\n return RECURRING_UNLIMITED_ORDERS_SENTINEL;\n }\n\n if (!Number.isInteger(input) || input < 2 || input > 365) {\n throw new InvalidParamsError(\n 'Invalid numberOfOrders',\n `Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(input)}`,\n );\n }\n\n return input;\n}\n\nexport async function markrRecurringQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringQuoteParams,\n): Promise<RecurringQuoteResponse> {\n const endpoint = '/recurring/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const body = {\n appId: params.appId,\n chainId: params.chainId,\n tokenIn: params.tokenIn,\n tokenInDecimals: params.tokenInDecimals,\n tokenOut: params.tokenOut,\n tokenOutDecimals: params.tokenOutDecimals,\n // Inline `.toString()` — `JSON.stringify` cannot serialize bigint, so the\n // wire shape stays a decimal string.\n amount: params.amount.toString(),\n numberOfOrders: translateNumberOfOrders(params.numberOfOrders),\n frequency: params.frequency,\n slippage: params.slippage,\n };\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(body),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = RecurringQuoteResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the first-fill swap calldata for a previously quoted recurring swap.\n *\n * Returns an **ordered array** of `RecurringSwapTransaction` steps per the\n * Markr `/recurring/swap` doc:\n * - ERC-20 `tokenIn`: `[createOrder]` (1 element). Caller must have already\n * approved `totalAmountIn` of the ERC-20 to the RecurringSwaps router.\n * - Native `tokenIn` (`0x0…`): `[wrap, createOrder]` (2 elements). Caller\n * must approve the wrapped-native ERC-20 to RecurringSwaps **between**\n * the wrap and createOrder signatures (the SDK inserts that step itself —\n * Markr does not return an approval entry).\n *\n * The SDK signs and broadcasts each entry in array order; relay-driven\n * fills follow `createOrder` on Markr's schedule cadence.\n *\n * Idempotent on the same `uuid` per the Markr docs — retried on transient\n * network / 5xx failures via `retryIdempotentPost`.\n */\nexport async function markrRecurringSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringSwapParams,\n): Promise<RecurringSwapResponse> {\n const endpoint = '/recurring/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = RecurringSwapResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\nexport async function markrListRecurringOrders(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: ListRecurringOrdersParams,\n): Promise<ListRecurringOrdersResponse> {\n // `URLSearchParams` URL-encodes each value — defense-in-depth against a\n // caller reaching from JS (no TS brand check) and passing an `address` like\n // `0x…&adminKey=x` that would otherwise inject extra query params.\n const searchParams = new URLSearchParams({ address: params.address });\n if (params.chainId !== undefined) searchParams.set('chainId', String(params.chainId));\n if (params.status !== undefined) searchParams.set('status', params.status);\n\n const endpoint = `/recurring/orders?${searchParams.toString()}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = ListRecurringOrdersResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"/recurring/orders\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Shared implementation for the three calldata-returning order-action\n * endpoints (`cancel`, `pause`, `unpause`). All three share:\n * - identical body shape `{ address, chainId }`\n * - identical response shape (`WrappedSwapTransactionResponse`)\n * - identical bytes32 `orderId` validation (path-traversal defense before\n * URL interpolation — catches stale UUIDs / malformed values as a clear\n * `InvalidParamsError` at the SDK boundary rather than an opaque 404\n * from the orchestrator)\n * - identical idempotent-POST retry policy\n *\n * Kept private so all three exported helpers go through the same wire\n * contract — adding a new field (or tightening a check) is a one-place\n * change here.\n */\nasync function postRecurringOrderAction(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n action: 'cancel' | 'pause' | 'unpause',\n params: { orderId: `0x${string}`; address: string; chainId: number },\n): Promise<WrappedSwapTransactionResponse> {\n if (!ORDER_ID_REGEX.test(params.orderId)) {\n throw new InvalidParamsError(\n 'Invalid recurring orderId',\n `Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(params.orderId)}`,\n );\n }\n\n const endpoint = `/recurring/orders/${encodeURIComponent(params.orderId)}/${action}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify({ address: params.address, chainId: params.chainId }),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = WrappedSwapTransactionResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the calldata to cancel the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — cancellation only takes effect after\n * the TX confirms, at which point Markr transitions the order's `status`\n * to `'cancelled'`.\n *\n * Only `'active'` and `'paused'` orders can be cancelled per the docs; an\n * attempt to cancel a `'completed'` (or otherwise non-cancellable) order\n * surfaces as `HttpError(400)` from the orchestrator.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple per the Markr\n * docs — retried on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1orders~1{orderId}~1cancel/post\n */\nexport function markrPrepareCancellation(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'cancel', params);\n}\n\n/**\n * Fetches the calldata to pause the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — the schedule only transitions to\n * `status: 'paused'` once the TX confirms and Markr observes the on-chain\n * event.\n *\n * Only `'active'` orders can be paused per the docs; an attempt to pause a\n * non-`'active'` order surfaces as `HttpError(400)` from the orchestrator.\n *\n * Pausing preserves the existing ERC-20 allowance — when the user later\n * unpauses, no re-approval is required and no new native schedule fee is\n * charged. This is the key UX benefit of pause over cancel-and-recreate.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1pause/post\n */\nexport function markrPreparePause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'pause', params);\n}\n\n/**\n * Fetches the calldata to unpause (resume) the recurring schedule\n * identified by `orderId`. The returned `WrappedSwapTransactionResponse` is\n * signed and broadcast on-chain by the caller — the schedule only\n * transitions back to `status: 'active'` once the TX confirms and Markr\n * observes the on-chain event.\n *\n * Only `'paused'` orders can be unpaused per the docs; an attempt to\n * unpause a non-`'paused'` order surfaces as `HttpError(400)` from the\n * orchestrator.\n *\n * Resumes execution from where the schedule left off — remaining fills\n * continue on the original `frequency` cadence and the orchestrator\n * recomputes `nextExecutionAt` after the TX confirms.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1unpause/post\n */\nexport function markrPrepareUnpause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'unpause', params);\n}\n"],"mappings":"2LAqBM,EAAiB,sBAcvB,SAAS,EAAoB,EAAwD,CACnF,GAAI,EAAI,MAEN,MADA,EAAI,EAAI,iBAAiBA,EAAAA,cAG3B,IAAM,EAAS,EAAI,UAAU,QAAU,EACvC,OAAO,IAAW,KAAO,IAAW,KAAO,IAAW,KAAQ,GAAU,KAAO,GAAU,IAa3F,SAAS,EAAwB,EAAuB,CACtD,GAAI,IAAU,KAAY,IAAA,GACxB,MAAA,GAGF,GAAI,CAAC,OAAO,UAAU,EAAM,EAAI,EAAQ,GAAK,EAAQ,IACnD,MAAM,IAAIC,EAAAA,mBACR,yBACA,sEAAsE,OAAO,EAAM,GACpF,CAGH,OAAO,EAGT,eAAsB,EACpB,CAAE,aAAY,WAAU,MAAO,GAC/B,EACiC,CACjC,IAAM,EAAW,mBACX,EAAMC,EAAAA,oBAAoB,EAAY,EAAS,CAE/C,EAAO,CACX,MAAO,EAAO,MACd,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,gBAAiB,EAAO,gBACxB,SAAU,EAAO,SACjB,iBAAkB,EAAO,iBAGzB,OAAQ,EAAO,OAAO,UAAU,CAChC,eAAgB,EAAwB,EAAO,eAAe,CAC9D,UAAW,EAAO,UAClB,SAAU,EAAO,SAClB,CAEK,EAAU,MAAMC,EAAAA,UAAU,EAAK,CACnC,KAAM,KAAK,UAAU,EAAK,CAC1B,MAAO,EACP,QAASC,EAAAA,eAAe,EAAS,CACjC,OAAQ,OACT,CAAC,CAEI,EAASC,EAAAA,6BAA6B,UAAU,EAAQ,CAE9D,GAAI,CAAC,EAAO,QACV,MAAM,IAAIC,EAAAA,wBACR,gCAAgC,EAAS,aACzC,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAqBhB,eAAsB,EACpB,CAAE,aAAY,WAAU,MAAO,GAC/B,EACgC,CAChC,IAAM,EAAW,kBAGX,EAAU,MAAMH,EAAAA,UAFVD,EAAAA,oBAAoB,EAAY,EAAS,CAEhB,CACnC,KAAM,KAAK,UAAU,EAAO,CAC5B,MAAO,EACP,QAASE,EAAAA,eAAe,EAAS,CACjC,OAAQ,OACR,QAAS,EACT,QAAS,EACV,CAAC,CAEI,EAASG,EAAAA,4BAA4B,UAAU,EAAQ,CAE7D,GAAI,CAAC,EAAO,QACV,MAAM,IAAID,EAAAA,wBACR,gCAAgC,EAAS,aACzC,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAGhB,eAAsB,EACpB,CAAE,aAAY,WAAU,MAAO,GAC/B,EACsC,CAItC,IAAM,EAAe,IAAI,gBAAgB,CAAE,QAAS,EAAO,QAAS,CAAC,CACjE,EAAO,UAAY,IAAA,IAAW,EAAa,IAAI,UAAW,OAAO,EAAO,QAAQ,CAAC,CACjF,EAAO,SAAW,IAAA,IAAW,EAAa,IAAI,SAAU,EAAO,OAAO,CAK1E,IAAM,EAAU,MAAMH,EAAAA,UAFVD,EAAAA,oBAAoB,EADf,qBAAqB,EAAa,UAAU,GACR,CAEhB,CAAE,QAASE,EAAAA,eAAe,EAAS,CAAE,MAAO,EAAa,CAAC,CAEzF,EAASI,EAAAA,kCAAkC,UAAU,EAAQ,CAEnE,GAAI,CAAC,EAAO,QACV,MAAM,IAAIF,EAAAA,wBACR,4DACA,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAkBhB,eAAe,EACb,CAAE,aAAY,WAAU,MAAO,GAC/B,EACA,EACyC,CACzC,GAAI,CAAC,EAAe,KAAK,EAAO,QAAQ,CACtC,MAAM,IAAIL,EAAAA,mBACR,4BACA,qDAAqD,KAAK,UAAU,EAAO,QAAQ,GACpF,CAGH,IAAM,EAAW,qBAAqB,mBAAmB,EAAO,QAAQ,CAAC,GAAG,IAGtE,EAAU,MAAME,EAAAA,UAFVD,EAAAA,oBAAoB,EAAY,EAAS,CAEhB,CACnC,KAAM,KAAK,UAAU,CAAE,QAAS,EAAO,QAAS,QAAS,EAAO,QAAS,CAAC,CAC1E,MAAO,EACP,QAASE,EAAAA,eAAe,EAAS,CACjC,OAAQ,OACR,QAAS,EACT,QAAS,EACV,CAAC,CAEI,EAASK,EAAAA,qCAAqC,UAAU,EAAQ,CAEtE,GAAI,CAAC,EAAO,QACV,MAAM,IAAIH,EAAAA,wBACR,gCAAgC,EAAS,aACzC,EAAO,MAAM,OACb,EACD,CAGH,OAAO,EAAO,KAmBhB,SAAgB,EACd,EACA,EACyC,CACzC,OAAO,EAAyB,EAAY,SAAU,EAAO,CAsB/D,SAAgB,EACd,EACA,EACyC,CACzC,OAAO,EAAyB,EAAY,QAAS,EAAO,CAuB9D,SAAgB,EACd,EACA,EACyC,CACzC,OAAO,EAAyB,EAAY,UAAW,EAAO"}
@@ -1,2 +1,2 @@
1
- import{AbortedError as e,InvalidParamsError as t,ResponseValidationError as n}from"../../../errors.js";import"./types.js";import{WrappedSwapTransactionResponseSchema as r}from"../_schema.js";import{combineUrlPathnames as i,fetchJson as a}from"../../fetch-utilities.js";import{getAuthHeaders as o}from"../_api.js";import{ListRecurringOrdersResponseSchema as s,RecurringQuoteResponseSchema as c}from"./_schema.js";const l=/^0x[0-9a-fA-F]{64}$/;function u(t){if(t.error)return!(t.error instanceof e);let n=t.response?.status??0;return n===408||n===425||n===429||n>=500&&n<=599}function d(e){if(e===1/0||e===-1)return-1;if(!Number.isInteger(e)||e<2||e>365)throw new t(`Invalid numberOfOrders`,`Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(e)}`);return e}async function f({apiBaseUrl:e,apiToken:t,fetch:r},s){let l=`/recurring/quote`,u=i(e,l),f={appId:s.appId,chainId:s.chainId,tokenIn:s.tokenIn,tokenInDecimals:s.tokenInDecimals,tokenOut:s.tokenOut,tokenOutDecimals:s.tokenOutDecimals,amount:s.amount.toString(),numberOfOrders:d(s.numberOfOrders),frequency:s.frequency,slippage:s.slippage},p=await a(u,{body:JSON.stringify(f),fetch:r,headers:o(t),method:`POST`}),m=c.safeParse(p);if(!m.success)throw new n(`Invalid response from Markr "${l}" endpoint.`,m.error.issues,p);return m.data}async function p({apiBaseUrl:e,apiToken:t,fetch:s},c){let l=`/recurring/swap`,d=await a(i(e,l),{body:JSON.stringify(c),fetch:s,headers:o(t),method:`POST`,retries:3,retryOn:u}),f=r.safeParse(d);if(!f.success)throw new n(`Invalid response from Markr "${l}" endpoint.`,f.error.issues,d);return f.data}async function m({apiBaseUrl:e,apiToken:t,fetch:r},c){let l=new URLSearchParams({address:c.address});c.chainId!==void 0&&l.set(`chainId`,String(c.chainId)),c.status!==void 0&&l.set(`status`,c.status);let u=await a(i(e,`/recurring/orders?${l.toString()}`),{headers:o(t),fetch:r}),d=s.safeParse(u);if(!d.success)throw new n(`Invalid response from Markr "/recurring/orders" endpoint.`,d.error.issues,u);return d.data}async function h({apiBaseUrl:e,apiToken:s,fetch:c},d,f){if(!l.test(f.orderId))throw new t(`Invalid recurring orderId`,`Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(f.orderId)}`);let p=`/recurring/orders/${encodeURIComponent(f.orderId)}/${d}`,m=await a(i(e,p),{body:JSON.stringify({address:f.address,chainId:f.chainId}),fetch:c,headers:o(s),method:`POST`,retries:3,retryOn:u}),h=r.safeParse(m);if(!h.success)throw new n(`Invalid response from Markr "${p}" endpoint.`,h.error.issues,m);return h.data}function g(e,t){return h(e,`cancel`,t)}function _(e,t){return h(e,`pause`,t)}function v(e,t){return h(e,`unpause`,t)}export{m as markrListRecurringOrders,g as markrPrepareCancellation,_ as markrPreparePause,v as markrPrepareUnpause,f as markrRecurringQuote,p as markrRecurringSwap};
1
+ import{AbortedError as e,InvalidParamsError as t,ResponseValidationError as n}from"../../../errors.js";import"./types.js";import{WrappedSwapTransactionResponseSchema as r}from"../_schema.js";import{combineUrlPathnames as i,fetchJson as a}from"../../fetch-utilities.js";import{getAuthHeaders as o}from"../_api.js";import{ListRecurringOrdersResponseSchema as s,RecurringQuoteResponseSchema as c,RecurringSwapResponseSchema as l}from"./_schema.js";const u=/^0x[0-9a-fA-F]{64}$/;function d(t){if(t.error)return!(t.error instanceof e);let n=t.response?.status??0;return n===408||n===425||n===429||n>=500&&n<=599}function f(e){if(e===1/0||e===-1)return-1;if(!Number.isInteger(e)||e<2||e>365)throw new t(`Invalid numberOfOrders`,`Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(e)}`);return e}async function p({apiBaseUrl:e,apiToken:t,fetch:r},s){let l=`/recurring/quote`,u=i(e,l),d={appId:s.appId,chainId:s.chainId,tokenIn:s.tokenIn,tokenInDecimals:s.tokenInDecimals,tokenOut:s.tokenOut,tokenOutDecimals:s.tokenOutDecimals,amount:s.amount.toString(),numberOfOrders:f(s.numberOfOrders),frequency:s.frequency,slippage:s.slippage},p=await a(u,{body:JSON.stringify(d),fetch:r,headers:o(t),method:`POST`}),m=c.safeParse(p);if(!m.success)throw new n(`Invalid response from Markr "${l}" endpoint.`,m.error.issues,p);return m.data}async function m({apiBaseUrl:e,apiToken:t,fetch:r},s){let c=`/recurring/swap`,u=await a(i(e,c),{body:JSON.stringify(s),fetch:r,headers:o(t),method:`POST`,retries:3,retryOn:d}),f=l.safeParse(u);if(!f.success)throw new n(`Invalid response from Markr "${c}" endpoint.`,f.error.issues,u);return f.data}async function h({apiBaseUrl:e,apiToken:t,fetch:r},c){let l=new URLSearchParams({address:c.address});c.chainId!==void 0&&l.set(`chainId`,String(c.chainId)),c.status!==void 0&&l.set(`status`,c.status);let u=await a(i(e,`/recurring/orders?${l.toString()}`),{headers:o(t),fetch:r}),d=s.safeParse(u);if(!d.success)throw new n(`Invalid response from Markr "/recurring/orders" endpoint.`,d.error.issues,u);return d.data}async function g({apiBaseUrl:e,apiToken:s,fetch:c},l,f){if(!u.test(f.orderId))throw new t(`Invalid recurring orderId`,`Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(f.orderId)}`);let p=`/recurring/orders/${encodeURIComponent(f.orderId)}/${l}`,m=await a(i(e,p),{body:JSON.stringify({address:f.address,chainId:f.chainId}),fetch:c,headers:o(s),method:`POST`,retries:3,retryOn:d}),h=r.safeParse(m);if(!h.success)throw new n(`Invalid response from Markr "${p}" endpoint.`,h.error.issues,m);return h.data}function _(e,t){return g(e,`cancel`,t)}function v(e,t){return g(e,`pause`,t)}function y(e,t){return g(e,`unpause`,t)}export{h as markrListRecurringOrders,_ as markrPrepareCancellation,v as markrPreparePause,y as markrPrepareUnpause,p as markrRecurringQuote,m as markrRecurringSwap};
2
2
  //# sourceMappingURL=_api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_api.js","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_api.ts"],"sourcesContent":["import { AbortedError, InvalidParamsError, ResponseValidationError } from '../../../errors';\nimport { combineUrlPathnames, fetchJson } from '../../fetch-utilities';\nimport { WrappedSwapTransactionResponseSchema, type WrappedSwapTransactionResponse } from '../_schema';\nimport { getAuthHeaders, type ApiOptions } from '../_api';\nimport { ListRecurringOrdersResponseSchema, RecurringQuoteResponseSchema } from './_schema';\nimport {\n RECURRING_UNLIMITED_ORDERS_SENTINEL,\n type ListRecurringOrdersParams,\n type ListRecurringOrdersResponse,\n type RecurringOrderActionApiParams,\n type RecurringQuoteParams,\n type RecurringQuoteResponse,\n type RecurringSwapParams,\n} from './types';\n\n/** bytes32 hex — same regex as `RecurringOrderSchema.orderId`. */\nconst ORDER_ID_REGEX = /^0x[0-9a-fA-F]{64}$/;\n\n/**\n * Retry policy for `/recurring/swap` and the order-action endpoints\n * (`/recurring/orders/{}/cancel`, `/recurring/orders/{}/pause`,\n * `/recurring/orders/{}/unpause`) — Markr documents all of them as\n * idempotent on the same params, so transient network / 5xx failures are\n * safe to replay. Mirrors the `markrGetCrossChainStatus` pattern but admits\n * POST (whereas the GET variant only whitelists safe verbs) and does NOT\n * retry 404 (for these endpoints, 404 means the resource genuinely doesn't\n * exist).\n */\nconst IDEMPOTENT_POST_RETRY_COUNT = 3;\n\nfunction retryIdempotentPost(ctx: { error?: unknown; response?: Response }): boolean {\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) return false;\n return true;\n }\n const status = ctx.response?.status ?? 0;\n return status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599);\n}\n\n/**\n * Translate the SDK-public `numberOfOrders` value into Markr's wire shape:\n * - `Infinity` or `-1` → wire sentinel `-1` (unlimited).\n * - Integer in `[2, 365]` → passthrough (matches Markr's documented bounds).\n * - Anything else (`NaN`, `0`, `1`, negatives other than `-1`, non-integers,\n * values > 365, `-Infinity`) → `InvalidParamsError` at the SDK boundary.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1quote/post — Markr's\n * `numberOfOrders` spec: `-1` for infinite, or integer 2-365 inclusive.\n */\nfunction translateNumberOfOrders(input: number): number {\n if (input === Infinity || input === RECURRING_UNLIMITED_ORDERS_SENTINEL) {\n return RECURRING_UNLIMITED_ORDERS_SENTINEL;\n }\n\n if (!Number.isInteger(input) || input < 2 || input > 365) {\n throw new InvalidParamsError(\n 'Invalid numberOfOrders',\n `Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(input)}`,\n );\n }\n\n return input;\n}\n\nexport async function markrRecurringQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringQuoteParams,\n): Promise<RecurringQuoteResponse> {\n const endpoint = '/recurring/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const body = {\n appId: params.appId,\n chainId: params.chainId,\n tokenIn: params.tokenIn,\n tokenInDecimals: params.tokenInDecimals,\n tokenOut: params.tokenOut,\n tokenOutDecimals: params.tokenOutDecimals,\n // Inline `.toString()` — `JSON.stringify` cannot serialize bigint, so the\n // wire shape stays a decimal string.\n amount: params.amount.toString(),\n numberOfOrders: translateNumberOfOrders(params.numberOfOrders),\n frequency: params.frequency,\n slippage: params.slippage,\n };\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(body),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = RecurringQuoteResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the first-fill swap calldata for a previously quoted recurring swap.\n *\n * Allowance must already cover `totalAmountIn` from the quote. The returned\n * `WrappedSwapTransactionResponse` is signed and broadcast by the caller (the\n * SDK does not sign on the recurring path).\n *\n * Idempotent on the same `uuid` per the Markr docs — retried on transient\n * network / 5xx failures via `retryIdempotentPost`.\n */\nexport async function markrRecurringSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringSwapParams,\n): Promise<WrappedSwapTransactionResponse> {\n const endpoint = '/recurring/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = WrappedSwapTransactionResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\nexport async function markrListRecurringOrders(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: ListRecurringOrdersParams,\n): Promise<ListRecurringOrdersResponse> {\n // `URLSearchParams` URL-encodes each value — defense-in-depth against a\n // caller reaching from JS (no TS brand check) and passing an `address` like\n // `0x…&adminKey=x` that would otherwise inject extra query params.\n const searchParams = new URLSearchParams({ address: params.address });\n if (params.chainId !== undefined) searchParams.set('chainId', String(params.chainId));\n if (params.status !== undefined) searchParams.set('status', params.status);\n\n const endpoint = `/recurring/orders?${searchParams.toString()}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = ListRecurringOrdersResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"/recurring/orders\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Shared implementation for the three calldata-returning order-action\n * endpoints (`cancel`, `pause`, `unpause`). All three share:\n * - identical body shape `{ address, chainId }`\n * - identical response shape (`WrappedSwapTransactionResponse`)\n * - identical bytes32 `orderId` validation (path-traversal defense before\n * URL interpolation — catches stale UUIDs / malformed values as a clear\n * `InvalidParamsError` at the SDK boundary rather than an opaque 404\n * from the orchestrator)\n * - identical idempotent-POST retry policy\n *\n * Kept private so all three exported helpers go through the same wire\n * contract — adding a new field (or tightening a check) is a one-place\n * change here.\n */\nasync function postRecurringOrderAction(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n action: 'cancel' | 'pause' | 'unpause',\n params: { orderId: `0x${string}`; address: string; chainId: number },\n): Promise<WrappedSwapTransactionResponse> {\n if (!ORDER_ID_REGEX.test(params.orderId)) {\n throw new InvalidParamsError(\n 'Invalid recurring orderId',\n `Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(params.orderId)}`,\n );\n }\n\n const endpoint = `/recurring/orders/${encodeURIComponent(params.orderId)}/${action}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify({ address: params.address, chainId: params.chainId }),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = WrappedSwapTransactionResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the calldata to cancel the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — cancellation only takes effect after\n * the TX confirms, at which point Markr transitions the order's `status`\n * to `'cancelled'`.\n *\n * Only `'active'` and `'paused'` orders can be cancelled per the docs; an\n * attempt to cancel a `'completed'` (or otherwise non-cancellable) order\n * surfaces as `HttpError(400)` from the orchestrator.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple per the Markr\n * docs — retried on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1orders~1{orderId}~1cancel/post\n */\nexport function markrPrepareCancellation(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'cancel', params);\n}\n\n/**\n * Fetches the calldata to pause the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — the schedule only transitions to\n * `status: 'paused'` once the TX confirms and Markr observes the on-chain\n * event.\n *\n * Only `'active'` orders can be paused per the docs; an attempt to pause a\n * non-`'active'` order surfaces as `HttpError(400)` from the orchestrator.\n *\n * Pausing preserves the existing ERC-20 allowance — when the user later\n * unpauses, no re-approval is required and no new native schedule fee is\n * charged. This is the key UX benefit of pause over cancel-and-recreate.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1pause/post\n */\nexport function markrPreparePause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'pause', params);\n}\n\n/**\n * Fetches the calldata to unpause (resume) the recurring schedule\n * identified by `orderId`. The returned `WrappedSwapTransactionResponse` is\n * signed and broadcast on-chain by the caller — the schedule only\n * transitions back to `status: 'active'` once the TX confirms and Markr\n * observes the on-chain event.\n *\n * Only `'paused'` orders can be unpaused per the docs; an attempt to\n * unpause a non-`'paused'` order surfaces as `HttpError(400)` from the\n * orchestrator.\n *\n * Resumes execution from where the schedule left off — remaining fills\n * continue on the original `frequency` cadence and the orchestrator\n * recomputes `nextExecutionAt` after the TX confirms.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1unpause/post\n */\nexport function markrPrepareUnpause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'unpause', params);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_api.js","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_api.ts"],"sourcesContent":["import { AbortedError, InvalidParamsError, ResponseValidationError } from '../../../errors';\nimport { combineUrlPathnames, fetchJson } from '../../fetch-utilities';\nimport { WrappedSwapTransactionResponseSchema, type WrappedSwapTransactionResponse } from '../_schema';\nimport { getAuthHeaders, type ApiOptions } from '../_api';\nimport {\n ListRecurringOrdersResponseSchema,\n RecurringQuoteResponseSchema,\n RecurringSwapResponseSchema,\n} from './_schema';\nimport {\n RECURRING_UNLIMITED_ORDERS_SENTINEL,\n type ListRecurringOrdersParams,\n type ListRecurringOrdersResponse,\n type RecurringOrderActionApiParams,\n type RecurringQuoteParams,\n type RecurringQuoteResponse,\n type RecurringSwapParams,\n type RecurringSwapResponse,\n} from './types';\n\n/** bytes32 hex — same regex as `RecurringOrderSchema.orderId`. */\nconst ORDER_ID_REGEX = /^0x[0-9a-fA-F]{64}$/;\n\n/**\n * Retry policy for `/recurring/swap` and the order-action endpoints\n * (`/recurring/orders/{}/cancel`, `/recurring/orders/{}/pause`,\n * `/recurring/orders/{}/unpause`) — Markr documents all of them as\n * idempotent on the same params, so transient network / 5xx failures are\n * safe to replay. Mirrors the `markrGetCrossChainStatus` pattern but admits\n * POST (whereas the GET variant only whitelists safe verbs) and does NOT\n * retry 404 (for these endpoints, 404 means the resource genuinely doesn't\n * exist).\n */\nconst IDEMPOTENT_POST_RETRY_COUNT = 3;\n\nfunction retryIdempotentPost(ctx: { error?: unknown; response?: Response }): boolean {\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) return false;\n return true;\n }\n const status = ctx.response?.status ?? 0;\n return status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599);\n}\n\n/**\n * Translate the SDK-public `numberOfOrders` value into Markr's wire shape:\n * - `Infinity` or `-1` → wire sentinel `-1` (unlimited).\n * - Integer in `[2, 365]` → passthrough (matches Markr's documented bounds).\n * - Anything else (`NaN`, `0`, `1`, negatives other than `-1`, non-integers,\n * values > 365, `-Infinity`) → `InvalidParamsError` at the SDK boundary.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1quote/post — Markr's\n * `numberOfOrders` spec: `-1` for infinite, or integer 2-365 inclusive.\n */\nfunction translateNumberOfOrders(input: number): number {\n if (input === Infinity || input === RECURRING_UNLIMITED_ORDERS_SENTINEL) {\n return RECURRING_UNLIMITED_ORDERS_SENTINEL;\n }\n\n if (!Number.isInteger(input) || input < 2 || input > 365) {\n throw new InvalidParamsError(\n 'Invalid numberOfOrders',\n `Expected an integer in [2, 365], Infinity, or -1 (unlimited). Got: ${String(input)}`,\n );\n }\n\n return input;\n}\n\nexport async function markrRecurringQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringQuoteParams,\n): Promise<RecurringQuoteResponse> {\n const endpoint = '/recurring/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const body = {\n appId: params.appId,\n chainId: params.chainId,\n tokenIn: params.tokenIn,\n tokenInDecimals: params.tokenInDecimals,\n tokenOut: params.tokenOut,\n tokenOutDecimals: params.tokenOutDecimals,\n // Inline `.toString()` — `JSON.stringify` cannot serialize bigint, so the\n // wire shape stays a decimal string.\n amount: params.amount.toString(),\n numberOfOrders: translateNumberOfOrders(params.numberOfOrders),\n frequency: params.frequency,\n slippage: params.slippage,\n };\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(body),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = RecurringQuoteResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the first-fill swap calldata for a previously quoted recurring swap.\n *\n * Returns an **ordered array** of `RecurringSwapTransaction` steps per the\n * Markr `/recurring/swap` doc:\n * - ERC-20 `tokenIn`: `[createOrder]` (1 element). Caller must have already\n * approved `totalAmountIn` of the ERC-20 to the RecurringSwaps router.\n * - Native `tokenIn` (`0x0…`): `[wrap, createOrder]` (2 elements). Caller\n * must approve the wrapped-native ERC-20 to RecurringSwaps **between**\n * the wrap and createOrder signatures (the SDK inserts that step itself —\n * Markr does not return an approval entry).\n *\n * The SDK signs and broadcasts each entry in array order; relay-driven\n * fills follow `createOrder` on Markr's schedule cadence.\n *\n * Idempotent on the same `uuid` per the Markr docs — retried on transient\n * network / 5xx failures via `retryIdempotentPost`.\n */\nexport async function markrRecurringSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: RecurringSwapParams,\n): Promise<RecurringSwapResponse> {\n const endpoint = '/recurring/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = RecurringSwapResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\nexport async function markrListRecurringOrders(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: ListRecurringOrdersParams,\n): Promise<ListRecurringOrdersResponse> {\n // `URLSearchParams` URL-encodes each value — defense-in-depth against a\n // caller reaching from JS (no TS brand check) and passing an `address` like\n // `0x…&adminKey=x` that would otherwise inject extra query params.\n const searchParams = new URLSearchParams({ address: params.address });\n if (params.chainId !== undefined) searchParams.set('chainId', String(params.chainId));\n if (params.status !== undefined) searchParams.set('status', params.status);\n\n const endpoint = `/recurring/orders?${searchParams.toString()}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = ListRecurringOrdersResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"/recurring/orders\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Shared implementation for the three calldata-returning order-action\n * endpoints (`cancel`, `pause`, `unpause`). All three share:\n * - identical body shape `{ address, chainId }`\n * - identical response shape (`WrappedSwapTransactionResponse`)\n * - identical bytes32 `orderId` validation (path-traversal defense before\n * URL interpolation — catches stale UUIDs / malformed values as a clear\n * `InvalidParamsError` at the SDK boundary rather than an opaque 404\n * from the orchestrator)\n * - identical idempotent-POST retry policy\n *\n * Kept private so all three exported helpers go through the same wire\n * contract — adding a new field (or tightening a check) is a one-place\n * change here.\n */\nasync function postRecurringOrderAction(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n action: 'cancel' | 'pause' | 'unpause',\n params: { orderId: `0x${string}`; address: string; chainId: number },\n): Promise<WrappedSwapTransactionResponse> {\n if (!ORDER_ID_REGEX.test(params.orderId)) {\n throw new InvalidParamsError(\n 'Invalid recurring orderId',\n `Expected 0x-prefixed bytes32 hex (66 chars). Got: ${JSON.stringify(params.orderId)}`,\n );\n }\n\n const endpoint = `/recurring/orders/${encodeURIComponent(params.orderId)}/${action}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify({ address: params.address, chainId: params.chainId }),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n retries: IDEMPOTENT_POST_RETRY_COUNT,\n retryOn: retryIdempotentPost,\n });\n\n const parsed = WrappedSwapTransactionResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Fetches the calldata to cancel the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — cancellation only takes effect after\n * the TX confirms, at which point Markr transitions the order's `status`\n * to `'cancelled'`.\n *\n * Only `'active'` and `'paused'` orders can be cancelled per the docs; an\n * attempt to cancel a `'completed'` (or otherwise non-cancellable) order\n * surfaces as `HttpError(400)` from the orchestrator.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple per the Markr\n * docs — retried on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#/paths/~1recurring~1orders~1{orderId}~1cancel/post\n */\nexport function markrPrepareCancellation(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'cancel', params);\n}\n\n/**\n * Fetches the calldata to pause the recurring schedule identified by\n * `orderId`. The returned `WrappedSwapTransactionResponse` is signed and\n * broadcast on-chain by the caller — the schedule only transitions to\n * `status: 'paused'` once the TX confirms and Markr observes the on-chain\n * event.\n *\n * Only `'active'` orders can be paused per the docs; an attempt to pause a\n * non-`'active'` order surfaces as `HttpError(400)` from the orchestrator.\n *\n * Pausing preserves the existing ERC-20 allowance — when the user later\n * unpauses, no re-approval is required and no new native schedule fee is\n * charged. This is the key UX benefit of pause over cancel-and-recreate.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1pause/post\n */\nexport function markrPreparePause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'pause', params);\n}\n\n/**\n * Fetches the calldata to unpause (resume) the recurring schedule\n * identified by `orderId`. The returned `WrappedSwapTransactionResponse` is\n * signed and broadcast on-chain by the caller — the schedule only\n * transitions back to `status: 'active'` once the TX confirms and Markr\n * observes the on-chain event.\n *\n * Only `'paused'` orders can be unpaused per the docs; an attempt to\n * unpause a non-`'paused'` order surfaces as `HttpError(400)` from the\n * orchestrator.\n *\n * Resumes execution from where the schedule left off — remaining fills\n * continue on the original `frequency` cadence and the orchestrator\n * recomputes `nextExecutionAt` after the TX confirms.\n *\n * Idempotent on the same `(orderId, address, chainId)` triple — retried\n * on transient network / 5xx failures via `retryIdempotentPost`.\n *\n * @see https://orchestrator-docs.markr.io/#tag/Recurring-Swaps/paths/~1recurring~1orders~1{orderId}~1unpause/post\n */\nexport function markrPrepareUnpause(\n apiOptions: ApiOptions,\n params: RecurringOrderActionApiParams,\n): Promise<WrappedSwapTransactionResponse> {\n return postRecurringOrderAction(apiOptions, 'unpause', params);\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- function e(e){let t=new Map;for(let n of e.values())typeof n.chainId!=`number`||!n.recurring||t.set(n.chainId,{minFrequencySeconds:n.recurring.minFrequencySeconds,supportedTokens:n.recurring.supportedTokens});return t}exports.deriveRecurringChainInfo=e;
1
+ function e(e){let t=new Map;for(let n of e.values())typeof n.chainId!=`number`||!n.recurring||t.set(n.chainId,{minFrequencySeconds:n.recurring.minFrequencySeconds,...n.wrappedNativeAddress?{wrappedNativeAddress:n.wrappedNativeAddress}:{}});return t}exports.deriveRecurringChainInfo=e;
2
2
  //# sourceMappingURL=_chain-info.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_chain-info.cjs","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_chain-info.ts"],"sourcesContent":["import type { SupportedChainsMap } from '../_utils';\nimport type { RecurringChainInfoEntry, RecurringChainInfoMap } from './types';\n\n/**\n * Derives a `RecurringChainInfoMap` (chainId → recurring metadata) from a\n * `SupportedChainsMap` already populated by `getSupportedChains`. Skips chains\n * that do not carry a `recurring` block in the `/info/chains` response.\n *\n * Pure function — no fetch. The original `/info/chains` response is the single\n * source for both maps (no second `/info/chains` call).\n */\nexport function deriveRecurringChainInfo(supportedChains: SupportedChainsMap): RecurringChainInfoMap {\n const recurringChainInfo: Map<number, RecurringChainInfoEntry> = new Map();\n\n for (const entry of supportedChains.values()) {\n if (typeof entry.chainId !== 'number' || !entry.recurring) {\n continue;\n }\n\n recurringChainInfo.set(entry.chainId, {\n minFrequencySeconds: entry.recurring.minFrequencySeconds,\n supportedTokens: entry.recurring.supportedTokens,\n });\n }\n\n return recurringChainInfo;\n}\n"],"mappings":"AAWA,SAAgB,EAAyB,EAA4D,CACnG,IAAM,EAA2D,IAAI,IAErE,IAAK,IAAM,KAAS,EAAgB,QAAQ,CACtC,OAAO,EAAM,SAAY,UAAY,CAAC,EAAM,WAIhD,EAAmB,IAAI,EAAM,QAAS,CACpC,oBAAqB,EAAM,UAAU,oBACrC,gBAAiB,EAAM,UAAU,gBAClC,CAAC,CAGJ,OAAO"}
1
+ {"version":3,"file":"_chain-info.cjs","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_chain-info.ts"],"sourcesContent":["import type { SupportedChainsMap } from '../_utils';\nimport type { RecurringChainInfoEntry, RecurringChainInfoMap } from './types';\n\n/**\n * Derives a `RecurringChainInfoMap` (chainId → recurring metadata) from a\n * `SupportedChainsMap` already populated by `getSupportedChains`. Skips chains\n * that do not carry a `recurring` block in the `/info/chains` response.\n *\n * Pure function — no fetch. The original `/info/chains` response is the single\n * source for both maps (no second `/info/chains` call).\n */\nexport function deriveRecurringChainInfo(supportedChains: SupportedChainsMap): RecurringChainInfoMap {\n const recurringChainInfo: Map<number, RecurringChainInfoEntry> = new Map();\n\n for (const entry of supportedChains.values()) {\n if (typeof entry.chainId !== 'number' || !entry.recurring) {\n continue;\n }\n\n recurringChainInfo.set(entry.chainId, {\n minFrequencySeconds: entry.recurring.minFrequencySeconds,\n // `wrapped_token` may be absent on a chain that lists recurring (rare —\n // Markr publishes it for all EVM chains it serves). When undefined,\n // eligibility + the quote boundary fail closed on native input.\n ...(entry.wrappedNativeAddress ? { wrappedNativeAddress: entry.wrappedNativeAddress } : {}),\n });\n }\n\n return recurringChainInfo;\n}\n"],"mappings":"AAWA,SAAgB,EAAyB,EAA4D,CACnG,IAAM,EAA2D,IAAI,IAErE,IAAK,IAAM,KAAS,EAAgB,QAAQ,CACtC,OAAO,EAAM,SAAY,UAAY,CAAC,EAAM,WAIhD,EAAmB,IAAI,EAAM,QAAS,CACpC,oBAAqB,EAAM,UAAU,oBAIrC,GAAI,EAAM,qBAAuB,CAAE,qBAAsB,EAAM,qBAAsB,CAAG,EAAE,CAC3F,CAAC,CAGJ,OAAO"}
@@ -1,2 +1,2 @@
1
- function e(e){let t=new Map;for(let n of e.values())typeof n.chainId!=`number`||!n.recurring||t.set(n.chainId,{minFrequencySeconds:n.recurring.minFrequencySeconds,supportedTokens:n.recurring.supportedTokens});return t}export{e as deriveRecurringChainInfo};
1
+ function e(e){let t=new Map;for(let n of e.values())typeof n.chainId!=`number`||!n.recurring||t.set(n.chainId,{minFrequencySeconds:n.recurring.minFrequencySeconds,...n.wrappedNativeAddress?{wrappedNativeAddress:n.wrappedNativeAddress}:{}});return t}export{e as deriveRecurringChainInfo};
2
2
  //# sourceMappingURL=_chain-info.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_chain-info.js","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_chain-info.ts"],"sourcesContent":["import type { SupportedChainsMap } from '../_utils';\nimport type { RecurringChainInfoEntry, RecurringChainInfoMap } from './types';\n\n/**\n * Derives a `RecurringChainInfoMap` (chainId → recurring metadata) from a\n * `SupportedChainsMap` already populated by `getSupportedChains`. Skips chains\n * that do not carry a `recurring` block in the `/info/chains` response.\n *\n * Pure function — no fetch. The original `/info/chains` response is the single\n * source for both maps (no second `/info/chains` call).\n */\nexport function deriveRecurringChainInfo(supportedChains: SupportedChainsMap): RecurringChainInfoMap {\n const recurringChainInfo: Map<number, RecurringChainInfoEntry> = new Map();\n\n for (const entry of supportedChains.values()) {\n if (typeof entry.chainId !== 'number' || !entry.recurring) {\n continue;\n }\n\n recurringChainInfo.set(entry.chainId, {\n minFrequencySeconds: entry.recurring.minFrequencySeconds,\n supportedTokens: entry.recurring.supportedTokens,\n });\n }\n\n return recurringChainInfo;\n}\n"],"mappings":"AAWA,SAAgB,EAAyB,EAA4D,CACnG,IAAM,EAA2D,IAAI,IAErE,IAAK,IAAM,KAAS,EAAgB,QAAQ,CACtC,OAAO,EAAM,SAAY,UAAY,CAAC,EAAM,WAIhD,EAAmB,IAAI,EAAM,QAAS,CACpC,oBAAqB,EAAM,UAAU,oBACrC,gBAAiB,EAAM,UAAU,gBAClC,CAAC,CAGJ,OAAO"}
1
+ {"version":3,"file":"_chain-info.js","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_chain-info.ts"],"sourcesContent":["import type { SupportedChainsMap } from '../_utils';\nimport type { RecurringChainInfoEntry, RecurringChainInfoMap } from './types';\n\n/**\n * Derives a `RecurringChainInfoMap` (chainId → recurring metadata) from a\n * `SupportedChainsMap` already populated by `getSupportedChains`. Skips chains\n * that do not carry a `recurring` block in the `/info/chains` response.\n *\n * Pure function — no fetch. The original `/info/chains` response is the single\n * source for both maps (no second `/info/chains` call).\n */\nexport function deriveRecurringChainInfo(supportedChains: SupportedChainsMap): RecurringChainInfoMap {\n const recurringChainInfo: Map<number, RecurringChainInfoEntry> = new Map();\n\n for (const entry of supportedChains.values()) {\n if (typeof entry.chainId !== 'number' || !entry.recurring) {\n continue;\n }\n\n recurringChainInfo.set(entry.chainId, {\n minFrequencySeconds: entry.recurring.minFrequencySeconds,\n // `wrapped_token` may be absent on a chain that lists recurring (rare —\n // Markr publishes it for all EVM chains it serves). When undefined,\n // eligibility + the quote boundary fail closed on native input.\n ...(entry.wrappedNativeAddress ? { wrappedNativeAddress: entry.wrappedNativeAddress } : {}),\n });\n }\n\n return recurringChainInfo;\n}\n"],"mappings":"AAWA,SAAgB,EAAyB,EAA4D,CACnG,IAAM,EAA2D,IAAI,IAErE,IAAK,IAAM,KAAS,EAAgB,QAAQ,CACtC,OAAO,EAAM,SAAY,UAAY,CAAC,EAAM,WAIhD,EAAmB,IAAI,EAAM,QAAS,CACpC,oBAAqB,EAAM,UAAU,oBAIrC,GAAI,EAAM,qBAAuB,CAAE,qBAAsB,EAAM,qBAAsB,CAAG,EAAE,CAC3F,CAAC,CAGJ,OAAO"}
@@ -1,2 +1,2 @@
1
- require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`./types.cjs`);let t=require(`viem`);function n(n){let{recurringChainInfo:r,fromTokenAddress:i,toTokenAddress:a,sourceChainId:o,targetChainId:s,ownerAddress:c,amount:l}=n;if(!c)return{eligible:!1,reason:e.RecurringEligibilityReason.NoEvmAddress};if(o!==s)return{eligible:!1,reason:e.RecurringEligibilityReason.CrossChain};let u=r.get(o);if(!u||u.supportedTokens.length===0)return{eligible:!1,reason:e.RecurringEligibilityReason.UnsupportedSourceChain};let d=u.supportedTokens.find(e=>(0,t.isAddressEqual)(e.address,i));return!d||(0,t.isAddressEqual)(a,i)?{eligible:!1,reason:e.RecurringEligibilityReason.UnsupportedToken}:l!==void 0&&l<BigInt(d.minimumAmount)?{eligible:!1,reason:e.RecurringEligibilityReason.AmountBelowMinimum,minimumAmount:d.minimumAmount}:{eligible:!0,minimumAmount:d.minimumAmount,minIntervalSeconds:u.minFrequencySeconds}}exports.checkRecurringEligibility=n;
1
+ require(`../../../_virtual/_rolldown/runtime.cjs`),require(`../../../constants.cjs`);const e=require(`./types.cjs`);let t=require(`viem`);function n(n){let{recurringChainInfo:r,fromTokenAddress:i,toTokenAddress:a,sourceChainId:o,targetChainId:s,ownerAddress:c}=n;if(!c)return{eligible:!1,reason:e.RecurringEligibilityReason.NoEvmAddress};if(o!==s)return{eligible:!1,reason:e.RecurringEligibilityReason.CrossChain};let l=r.get(o);if(!l)return{eligible:!1,reason:e.RecurringEligibilityReason.UnsupportedSourceChain};if((0,t.isAddressEqual)(i,`0x0000000000000000000000000000000000000000`)){if(!l.wrappedNativeAddress)return{eligible:!1,reason:e.RecurringEligibilityReason.UnsupportedToken};if((0,t.isAddressEqual)(a,l.wrappedNativeAddress))return{eligible:!1,reason:e.RecurringEligibilityReason.NativeToWrappedNative}}return(0,t.isAddressEqual)(a,i)?{eligible:!1,reason:e.RecurringEligibilityReason.UnsupportedToken}:{eligible:!0,minIntervalSeconds:l.minFrequencySeconds}}exports.checkRecurringEligibility=n;
2
2
  //# sourceMappingURL=_eligibility.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_eligibility.cjs","names":["RecurringEligibilityReason"],"sources":["../../../../src/transfer-service/markr/recurring/_eligibility.ts"],"sourcesContent":["import { isAddressEqual } from 'viem';\nimport { RecurringEligibilityReason } from './types';\nimport type { CheckRecurringEligibilityParams, RecurringChainInfoEntry, RecurringEligibility } from './types';\n\n/**\n * Pure check that a `(fromToken, toToken, ownerAddress)` triple is supported\n * for recurring swaps against a cached `/info/chains` response.\n *\n * Mirrors the branch order of the mobile `useRecurringEligibility` hook so the\n * `reason` codes returned here can drive UI strings unchanged.\n *\n * ## What this checks (static, no I/O)\n *\n * - Owner has an EVM address → `NoEvmAddress`\n * - Source and target chains match (same-chain only) → `CrossChain`\n * - Chain is in the cached recurring chain-info map → `UnsupportedSourceChain`\n * - `fromTokenAddress` is in the chain's supported list → `UnsupportedToken`\n * - `toTokenAddress` differs from `fromTokenAddress` → `UnsupportedToken`\n * - When `amount` is provided, `amount >= minimumAmount` `AmountBelowMinimum`\n * (the failure variant carries `minimumAmount` so error UIs can say\n * \"Min is X\" without a second `getRecurringChainInfo()` lookup)\n *\n * ## What this does NOT check\n *\n * - **Wallet balance.** This helper has no wallet/RPC context — it cannot\n * compare `amount × numberOfOrders` against the user's `tokenIn` balance,\n * their native-gas balance for the first-fill TX, or any other on-chain\n * state. Balance checks are the **consumer's** responsibility, same as the\n * one-shot swap flow.\n * - **Post-fill failures** (e.g. a scheduled fill reverts because the user\n * later moved funds). These surface server-side via\n * `RecurringOrder.failures[].reasons: ReadonlyArray<string>` — open-ended\n * strings, NOT this enum. Markr emits literals like `\"Insufficient balance\"`\n * or `\"Slippage tolerance exceeded\"`; consumers substring-match per the\n * AC4 auto-cancel plan and call {@link RecurringNamespace.prepareCancellation}\n * when applicable. Kept as `string` (with `(string & NonNullable<unknown>)`\n * widening) so a new server-side reason doesn't brick already-shipped clients.\n * - **Server-side quote rejection** (slippage, target-token unsupported,\n * liquidity, etc.) — Markr decides at `/recurring/quote` time. Surface\n * those as `HttpError(400)` / `HttpError(422)` from\n * {@link RecurringNamespace.quote}.\n *\n * Net: this helper covers the \"can the user *attempt* a recurring swap with\n * this token pair at this size\" question. Anything that requires a wallet,\n * RPC, or live orchestrator round-trip lives elsewhere.\n */\nexport function checkRecurringEligibility(params: CheckRecurringEligibilityParams): RecurringEligibility {\n const { recurringChainInfo, fromTokenAddress, toTokenAddress, sourceChainId, targetChainId, ownerAddress, amount } =\n params;\n\n if (!ownerAddress) {\n return { eligible: false, reason: RecurringEligibilityReason.NoEvmAddress };\n }\n\n if (sourceChainId !== targetChainId) {\n return { eligible: false, reason: RecurringEligibilityReason.CrossChain };\n }\n\n const chain: RecurringChainInfoEntry | undefined = recurringChainInfo.get(sourceChainId);\n\n if (!chain || chain.supportedTokens.length === 0) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedSourceChain };\n }\n\n // `isAddressEqual` does a case-insensitive compare without per-call string\n // allocations (matches the established pattern in `markr/_utils.ts`). The\n // `EvmAddress` brand type guarantees the inputs are well-formed; if a JS\n // consumer reaches in with a malformed value, the resulting throw is the\n // right failure mode (loud > silently \"unsupported-token\").\n const supportedToken = chain.supportedTokens.find((token) => isAddressEqual(token.address, fromTokenAddress));\n\n if (!supportedToken) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n\n // The target token only needs to exist on the same chain; recurring swaps\n // are one-way (tokenIn → tokenOut) and Markr decides target eligibility\n // server-side at /recurring/quote time. Reject the obvious nonsense\n // (`toTokenAddress` matching `fromTokenAddress`) up front to fail fast.\n if (isAddressEqual(toTokenAddress, fromTokenAddress)) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n\n if (amount !== undefined && amount < BigInt(supportedToken.minimumAmount)) {\n return {\n eligible: false,\n reason: RecurringEligibilityReason.AmountBelowMinimum,\n minimumAmount: supportedToken.minimumAmount,\n };\n }\n\n return {\n eligible: true,\n minimumAmount: supportedToken.minimumAmount,\n minIntervalSeconds: chain.minFrequencySeconds,\n };\n}\n"],"mappings":"wGA8CA,SAAgB,EAA0B,EAA+D,CACvG,GAAM,CAAE,qBAAoB,mBAAkB,iBAAgB,gBAAe,gBAAe,eAAc,UACxG,EAEF,GAAI,CAAC,EACH,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,aAAc,CAG7E,GAAI,IAAkB,EACpB,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,WAAY,CAG3E,IAAM,EAA6C,EAAmB,IAAI,EAAc,CAExF,GAAI,CAAC,GAAS,EAAM,gBAAgB,SAAW,EAC7C,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,uBAAwB,CAQvF,IAAM,EAAiB,EAAM,gBAAgB,KAAM,IAAA,EAAA,EAAA,gBAAyB,EAAM,QAAS,EAAiB,CAAC,CAsB7G,MApBI,CAAC,IAQL,EAAA,EAAA,gBAAmB,EAAgB,EAAiB,CAC3C,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,iBAAkB,CAG7E,IAAW,IAAA,IAAa,EAAS,OAAO,EAAe,cAAc,CAChE,CACL,SAAU,GACV,OAAQA,EAAAA,2BAA2B,mBACnC,cAAe,EAAe,cAC/B,CAGI,CACL,SAAU,GACV,cAAe,EAAe,cAC9B,mBAAoB,EAAM,oBAC3B"}
1
+ {"version":3,"file":"_eligibility.cjs","names":["RecurringEligibilityReason"],"sources":["../../../../src/transfer-service/markr/recurring/_eligibility.ts"],"sourcesContent":["import { isAddressEqual } from 'viem';\nimport { ERC_ZERO_ADDRESS } from '../../../constants';\nimport { RecurringEligibilityReason } from './types';\nimport type { CheckRecurringEligibilityParams, RecurringChainInfoEntry, RecurringEligibility } from './types';\n\n/**\n * Pure check that a `(fromToken, toToken, ownerAddress)` triple is supported\n * for recurring swaps against a cached `/info/chains` response.\n *\n * Mirrors the branch order of the mobile `useRecurringEligibility` hook so the\n * `reason` codes returned here can drive UI strings unchanged.\n *\n * ## What this checks (static, no I/O)\n *\n * - Owner has an EVM address → `NoEvmAddress`\n * - Source and target chains match (same-chain only) → `CrossChain`\n * - Chain has recurring enabled in the chain-info map → `UnsupportedSourceChain`\n * - Native input chain has a wrapped-native published → `UnsupportedToken`\n * - Native input → target is not the wrapped-native → `NativeToWrappedNative`\n * - `toTokenAddress` differs from `fromTokenAddress` → `UnsupportedToken`\n *\n * Markr no longer publishes a per-chain supported-token list (the old\n * `/info/chains[].recurring.supportedTokens`), so every ERC-20 — and the\n * chain's native asset is treated as recurring-eligible here. Token-level\n * support (and any per-order minimum) is decided server-side at\n * `/recurring/quote` time.\n *\n * ## What this does NOT check\n *\n * - **Wallet balance.** This helper has no wallet/RPC context — it cannot\n * compare `amount × numberOfOrders` against the user's `tokenIn` balance,\n * their native-gas balance for the first-fill TX, or any other on-chain\n * state. Balance checks are the **consumer's** responsibility, same as the\n * one-shot swap flow.\n * - **Post-fill failures** (e.g. a scheduled fill reverts because the user\n * later moved funds). These surface server-side via\n * `RecurringOrder.failures[].reasons: ReadonlyArray<string>` — open-ended\n * strings, NOT this enum. Markr emits literals like `\"Insufficient balance\"`\n * or `\"Slippage tolerance exceeded\"`; consumers substring-match per the\n * AC4 auto-cancel plan and call {@link RecurringNamespace.prepareCancellation}\n * when applicable. Kept as `string` (with `(string & NonNullable<unknown>)`\n * widening) so a new server-side reason doesn't brick already-shipped clients.\n * - **Server-side quote rejection** (slippage, target-token unsupported,\n * liquidity, etc.) — Markr decides at `/recurring/quote` time. Surface\n * those as `HttpError(400)` / `HttpError(422)` from\n * {@link RecurringNamespace.quote}.\n *\n * Net: this helper covers the \"can the user *attempt* a recurring swap with\n * this token pair\" question. Anything that requires a wallet, RPC, or live\n * orchestrator round-trip lives elsewhere.\n */\nexport function checkRecurringEligibility(params: CheckRecurringEligibilityParams): RecurringEligibility {\n const { recurringChainInfo, fromTokenAddress, toTokenAddress, sourceChainId, targetChainId, ownerAddress } = params;\n\n if (!ownerAddress) {\n return { eligible: false, reason: RecurringEligibilityReason.NoEvmAddress };\n }\n\n if (sourceChainId !== targetChainId) {\n return { eligible: false, reason: RecurringEligibilityReason.CrossChain };\n }\n\n // Presence in the chain-info map is the recurring-enabled signal — the map\n // is built only for chains that carried a `recurring` block in\n // `/info/chains`. Markr no longer publishes a per-chain supported-token\n // list, so there is nothing token-level to assert here.\n const chain: RecurringChainInfoEntry | undefined = recurringChainInfo.get(sourceChainId);\n\n if (!chain) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedSourceChain };\n }\n\n // Native input is passed through to Markr verbatim (the recurring API\n // accepts the zero address as `tokenIn`); the SDK only uses the chain's\n // wrapped-native address for the on-chain wrap leg + WAVAX allowance in\n // `executeFirstFill`. The wrapped-native is read off the chain entry\n // (sourced from `/info/chains[].wrapped_token`) — no SDK-side static map.\n //\n // Two native-specific checks live here:\n // 1. Fail closed when Markr didn't publish a wrapped-native for the\n // chain we couldn't execute the schedule even if it's eligible.\n // 2. Reject native → wrapped-native (e.g. AVAX → WAVAX) with a dedicated\n // reason economically just a scheduled wrap, not a swap. Consumer\n // UI should steer the user to a one-shot wrap instead of failing with\n // a generic \"unsupported token\" message.\n const isNativeInput = isAddressEqual(fromTokenAddress, ERC_ZERO_ADDRESS);\n if (isNativeInput) {\n if (!chain.wrappedNativeAddress) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n if (isAddressEqual(toTokenAddress, chain.wrappedNativeAddress)) {\n return { eligible: false, reason: RecurringEligibilityReason.NativeToWrappedNative };\n }\n }\n\n // Recurring swaps are one-way (tokenIn → tokenOut) and Markr decides\n // token-level eligibility server-side at /recurring/quote time. Reject the\n // obvious nonsense (`toTokenAddress` matching `fromTokenAddress`) up front\n // to fail fast. `isAddressEqual` does a case-insensitive compare without\n // per-call string allocations (matches the pattern in `markr/_utils.ts`).\n if (isAddressEqual(toTokenAddress, fromTokenAddress)) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n\n return {\n eligible: true,\n minIntervalSeconds: chain.minFrequencySeconds,\n };\n}\n"],"mappings":"0IAmDA,SAAgB,EAA0B,EAA+D,CACvG,GAAM,CAAE,qBAAoB,mBAAkB,iBAAgB,gBAAe,gBAAe,gBAAiB,EAE7G,GAAI,CAAC,EACH,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,aAAc,CAG7E,GAAI,IAAkB,EACpB,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,WAAY,CAO3E,IAAM,EAA6C,EAAmB,IAAI,EAAc,CAExF,GAAI,CAAC,EACH,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,uBAAwB,CAiBvF,IAAA,EAAA,EAAA,gBADqC,EAAA,6CAAmC,CACrD,CACjB,GAAI,CAAC,EAAM,qBACT,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,iBAAkB,CAEjF,IAAA,EAAA,EAAA,gBAAmB,EAAgB,EAAM,qBAAqB,CAC5D,MAAO,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,sBAAuB,CAaxF,OAJA,EAAA,EAAA,gBAAmB,EAAgB,EAAiB,CAC3C,CAAE,SAAU,GAAO,OAAQA,EAAAA,2BAA2B,iBAAkB,CAG1E,CACL,SAAU,GACV,mBAAoB,EAAM,oBAC3B"}
@@ -1,2 +1,2 @@
1
- import{RecurringEligibilityReason as e}from"./types.js";import{isAddressEqual as t}from"viem";function n(n){let{recurringChainInfo:r,fromTokenAddress:i,toTokenAddress:a,sourceChainId:o,targetChainId:s,ownerAddress:c,amount:l}=n;if(!c)return{eligible:!1,reason:e.NoEvmAddress};if(o!==s)return{eligible:!1,reason:e.CrossChain};let u=r.get(o);if(!u||u.supportedTokens.length===0)return{eligible:!1,reason:e.UnsupportedSourceChain};let d=u.supportedTokens.find(e=>t(e.address,i));return!d||t(a,i)?{eligible:!1,reason:e.UnsupportedToken}:l!==void 0&&l<BigInt(d.minimumAmount)?{eligible:!1,reason:e.AmountBelowMinimum,minimumAmount:d.minimumAmount}:{eligible:!0,minimumAmount:d.minimumAmount,minIntervalSeconds:u.minFrequencySeconds}}export{n as checkRecurringEligibility};
1
+ import"../../../constants.js";import{RecurringEligibilityReason as e}from"./types.js";import{isAddressEqual as t}from"viem";function n(n){let{recurringChainInfo:r,fromTokenAddress:i,toTokenAddress:a,sourceChainId:o,targetChainId:s,ownerAddress:c}=n;if(!c)return{eligible:!1,reason:e.NoEvmAddress};if(o!==s)return{eligible:!1,reason:e.CrossChain};let l=r.get(o);if(!l)return{eligible:!1,reason:e.UnsupportedSourceChain};if(t(i,`0x0000000000000000000000000000000000000000`)){if(!l.wrappedNativeAddress)return{eligible:!1,reason:e.UnsupportedToken};if(t(a,l.wrappedNativeAddress))return{eligible:!1,reason:e.NativeToWrappedNative}}return t(a,i)?{eligible:!1,reason:e.UnsupportedToken}:{eligible:!0,minIntervalSeconds:l.minFrequencySeconds}}export{n as checkRecurringEligibility};
2
2
  //# sourceMappingURL=_eligibility.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_eligibility.js","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_eligibility.ts"],"sourcesContent":["import { isAddressEqual } from 'viem';\nimport { RecurringEligibilityReason } from './types';\nimport type { CheckRecurringEligibilityParams, RecurringChainInfoEntry, RecurringEligibility } from './types';\n\n/**\n * Pure check that a `(fromToken, toToken, ownerAddress)` triple is supported\n * for recurring swaps against a cached `/info/chains` response.\n *\n * Mirrors the branch order of the mobile `useRecurringEligibility` hook so the\n * `reason` codes returned here can drive UI strings unchanged.\n *\n * ## What this checks (static, no I/O)\n *\n * - Owner has an EVM address → `NoEvmAddress`\n * - Source and target chains match (same-chain only) → `CrossChain`\n * - Chain is in the cached recurring chain-info map → `UnsupportedSourceChain`\n * - `fromTokenAddress` is in the chain's supported list → `UnsupportedToken`\n * - `toTokenAddress` differs from `fromTokenAddress` → `UnsupportedToken`\n * - When `amount` is provided, `amount >= minimumAmount` `AmountBelowMinimum`\n * (the failure variant carries `minimumAmount` so error UIs can say\n * \"Min is X\" without a second `getRecurringChainInfo()` lookup)\n *\n * ## What this does NOT check\n *\n * - **Wallet balance.** This helper has no wallet/RPC context — it cannot\n * compare `amount × numberOfOrders` against the user's `tokenIn` balance,\n * their native-gas balance for the first-fill TX, or any other on-chain\n * state. Balance checks are the **consumer's** responsibility, same as the\n * one-shot swap flow.\n * - **Post-fill failures** (e.g. a scheduled fill reverts because the user\n * later moved funds). These surface server-side via\n * `RecurringOrder.failures[].reasons: ReadonlyArray<string>` — open-ended\n * strings, NOT this enum. Markr emits literals like `\"Insufficient balance\"`\n * or `\"Slippage tolerance exceeded\"`; consumers substring-match per the\n * AC4 auto-cancel plan and call {@link RecurringNamespace.prepareCancellation}\n * when applicable. Kept as `string` (with `(string & NonNullable<unknown>)`\n * widening) so a new server-side reason doesn't brick already-shipped clients.\n * - **Server-side quote rejection** (slippage, target-token unsupported,\n * liquidity, etc.) — Markr decides at `/recurring/quote` time. Surface\n * those as `HttpError(400)` / `HttpError(422)` from\n * {@link RecurringNamespace.quote}.\n *\n * Net: this helper covers the \"can the user *attempt* a recurring swap with\n * this token pair at this size\" question. Anything that requires a wallet,\n * RPC, or live orchestrator round-trip lives elsewhere.\n */\nexport function checkRecurringEligibility(params: CheckRecurringEligibilityParams): RecurringEligibility {\n const { recurringChainInfo, fromTokenAddress, toTokenAddress, sourceChainId, targetChainId, ownerAddress, amount } =\n params;\n\n if (!ownerAddress) {\n return { eligible: false, reason: RecurringEligibilityReason.NoEvmAddress };\n }\n\n if (sourceChainId !== targetChainId) {\n return { eligible: false, reason: RecurringEligibilityReason.CrossChain };\n }\n\n const chain: RecurringChainInfoEntry | undefined = recurringChainInfo.get(sourceChainId);\n\n if (!chain || chain.supportedTokens.length === 0) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedSourceChain };\n }\n\n // `isAddressEqual` does a case-insensitive compare without per-call string\n // allocations (matches the established pattern in `markr/_utils.ts`). The\n // `EvmAddress` brand type guarantees the inputs are well-formed; if a JS\n // consumer reaches in with a malformed value, the resulting throw is the\n // right failure mode (loud > silently \"unsupported-token\").\n const supportedToken = chain.supportedTokens.find((token) => isAddressEqual(token.address, fromTokenAddress));\n\n if (!supportedToken) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n\n // The target token only needs to exist on the same chain; recurring swaps\n // are one-way (tokenIn → tokenOut) and Markr decides target eligibility\n // server-side at /recurring/quote time. Reject the obvious nonsense\n // (`toTokenAddress` matching `fromTokenAddress`) up front to fail fast.\n if (isAddressEqual(toTokenAddress, fromTokenAddress)) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n\n if (amount !== undefined && amount < BigInt(supportedToken.minimumAmount)) {\n return {\n eligible: false,\n reason: RecurringEligibilityReason.AmountBelowMinimum,\n minimumAmount: supportedToken.minimumAmount,\n };\n }\n\n return {\n eligible: true,\n minimumAmount: supportedToken.minimumAmount,\n minIntervalSeconds: chain.minFrequencySeconds,\n };\n}\n"],"mappings":"8FA8CA,SAAgB,EAA0B,EAA+D,CACvG,GAAM,CAAE,qBAAoB,mBAAkB,iBAAgB,gBAAe,gBAAe,eAAc,UACxG,EAEF,GAAI,CAAC,EACH,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,aAAc,CAG7E,GAAI,IAAkB,EACpB,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,WAAY,CAG3E,IAAM,EAA6C,EAAmB,IAAI,EAAc,CAExF,GAAI,CAAC,GAAS,EAAM,gBAAgB,SAAW,EAC7C,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,uBAAwB,CAQvF,IAAM,EAAiB,EAAM,gBAAgB,KAAM,GAAU,EAAe,EAAM,QAAS,EAAiB,CAAC,CAsB7G,MApBI,CAAC,GAQD,EAAe,EAAgB,EAAiB,CAC3C,CAAE,SAAU,GAAO,OAAQ,EAA2B,iBAAkB,CAG7E,IAAW,IAAA,IAAa,EAAS,OAAO,EAAe,cAAc,CAChE,CACL,SAAU,GACV,OAAQ,EAA2B,mBACnC,cAAe,EAAe,cAC/B,CAGI,CACL,SAAU,GACV,cAAe,EAAe,cAC9B,mBAAoB,EAAM,oBAC3B"}
1
+ {"version":3,"file":"_eligibility.js","names":[],"sources":["../../../../src/transfer-service/markr/recurring/_eligibility.ts"],"sourcesContent":["import { isAddressEqual } from 'viem';\nimport { ERC_ZERO_ADDRESS } from '../../../constants';\nimport { RecurringEligibilityReason } from './types';\nimport type { CheckRecurringEligibilityParams, RecurringChainInfoEntry, RecurringEligibility } from './types';\n\n/**\n * Pure check that a `(fromToken, toToken, ownerAddress)` triple is supported\n * for recurring swaps against a cached `/info/chains` response.\n *\n * Mirrors the branch order of the mobile `useRecurringEligibility` hook so the\n * `reason` codes returned here can drive UI strings unchanged.\n *\n * ## What this checks (static, no I/O)\n *\n * - Owner has an EVM address → `NoEvmAddress`\n * - Source and target chains match (same-chain only) → `CrossChain`\n * - Chain has recurring enabled in the chain-info map → `UnsupportedSourceChain`\n * - Native input chain has a wrapped-native published → `UnsupportedToken`\n * - Native input → target is not the wrapped-native → `NativeToWrappedNative`\n * - `toTokenAddress` differs from `fromTokenAddress` → `UnsupportedToken`\n *\n * Markr no longer publishes a per-chain supported-token list (the old\n * `/info/chains[].recurring.supportedTokens`), so every ERC-20 — and the\n * chain's native asset is treated as recurring-eligible here. Token-level\n * support (and any per-order minimum) is decided server-side at\n * `/recurring/quote` time.\n *\n * ## What this does NOT check\n *\n * - **Wallet balance.** This helper has no wallet/RPC context — it cannot\n * compare `amount × numberOfOrders` against the user's `tokenIn` balance,\n * their native-gas balance for the first-fill TX, or any other on-chain\n * state. Balance checks are the **consumer's** responsibility, same as the\n * one-shot swap flow.\n * - **Post-fill failures** (e.g. a scheduled fill reverts because the user\n * later moved funds). These surface server-side via\n * `RecurringOrder.failures[].reasons: ReadonlyArray<string>` — open-ended\n * strings, NOT this enum. Markr emits literals like `\"Insufficient balance\"`\n * or `\"Slippage tolerance exceeded\"`; consumers substring-match per the\n * AC4 auto-cancel plan and call {@link RecurringNamespace.prepareCancellation}\n * when applicable. Kept as `string` (with `(string & NonNullable<unknown>)`\n * widening) so a new server-side reason doesn't brick already-shipped clients.\n * - **Server-side quote rejection** (slippage, target-token unsupported,\n * liquidity, etc.) — Markr decides at `/recurring/quote` time. Surface\n * those as `HttpError(400)` / `HttpError(422)` from\n * {@link RecurringNamespace.quote}.\n *\n * Net: this helper covers the \"can the user *attempt* a recurring swap with\n * this token pair\" question. Anything that requires a wallet, RPC, or live\n * orchestrator round-trip lives elsewhere.\n */\nexport function checkRecurringEligibility(params: CheckRecurringEligibilityParams): RecurringEligibility {\n const { recurringChainInfo, fromTokenAddress, toTokenAddress, sourceChainId, targetChainId, ownerAddress } = params;\n\n if (!ownerAddress) {\n return { eligible: false, reason: RecurringEligibilityReason.NoEvmAddress };\n }\n\n if (sourceChainId !== targetChainId) {\n return { eligible: false, reason: RecurringEligibilityReason.CrossChain };\n }\n\n // Presence in the chain-info map is the recurring-enabled signal — the map\n // is built only for chains that carried a `recurring` block in\n // `/info/chains`. Markr no longer publishes a per-chain supported-token\n // list, so there is nothing token-level to assert here.\n const chain: RecurringChainInfoEntry | undefined = recurringChainInfo.get(sourceChainId);\n\n if (!chain) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedSourceChain };\n }\n\n // Native input is passed through to Markr verbatim (the recurring API\n // accepts the zero address as `tokenIn`); the SDK only uses the chain's\n // wrapped-native address for the on-chain wrap leg + WAVAX allowance in\n // `executeFirstFill`. The wrapped-native is read off the chain entry\n // (sourced from `/info/chains[].wrapped_token`) — no SDK-side static map.\n //\n // Two native-specific checks live here:\n // 1. Fail closed when Markr didn't publish a wrapped-native for the\n // chain we couldn't execute the schedule even if it's eligible.\n // 2. Reject native → wrapped-native (e.g. AVAX → WAVAX) with a dedicated\n // reason economically just a scheduled wrap, not a swap. Consumer\n // UI should steer the user to a one-shot wrap instead of failing with\n // a generic \"unsupported token\" message.\n const isNativeInput = isAddressEqual(fromTokenAddress, ERC_ZERO_ADDRESS);\n if (isNativeInput) {\n if (!chain.wrappedNativeAddress) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n if (isAddressEqual(toTokenAddress, chain.wrappedNativeAddress)) {\n return { eligible: false, reason: RecurringEligibilityReason.NativeToWrappedNative };\n }\n }\n\n // Recurring swaps are one-way (tokenIn → tokenOut) and Markr decides\n // token-level eligibility server-side at /recurring/quote time. Reject the\n // obvious nonsense (`toTokenAddress` matching `fromTokenAddress`) up front\n // to fail fast. `isAddressEqual` does a case-insensitive compare without\n // per-call string allocations (matches the pattern in `markr/_utils.ts`).\n if (isAddressEqual(toTokenAddress, fromTokenAddress)) {\n return { eligible: false, reason: RecurringEligibilityReason.UnsupportedToken };\n }\n\n return {\n eligible: true,\n minIntervalSeconds: chain.minFrequencySeconds,\n };\n}\n"],"mappings":"4HAmDA,SAAgB,EAA0B,EAA+D,CACvG,GAAM,CAAE,qBAAoB,mBAAkB,iBAAgB,gBAAe,gBAAe,gBAAiB,EAE7G,GAAI,CAAC,EACH,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,aAAc,CAG7E,GAAI,IAAkB,EACpB,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,WAAY,CAO3E,IAAM,EAA6C,EAAmB,IAAI,EAAc,CAExF,GAAI,CAAC,EACH,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,uBAAwB,CAiBvF,GADsB,EAAe,EAAA,6CAAmC,CACrD,CACjB,GAAI,CAAC,EAAM,qBACT,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,iBAAkB,CAEjF,GAAI,EAAe,EAAgB,EAAM,qBAAqB,CAC5D,MAAO,CAAE,SAAU,GAAO,OAAQ,EAA2B,sBAAuB,CAaxF,OAJI,EAAe,EAAgB,EAAiB,CAC3C,CAAE,SAAU,GAAO,OAAQ,EAA2B,iBAAkB,CAG1E,CACL,SAAU,GACV,mBAAoB,EAAM,oBAC3B"}