@t402/stacks 2.4.0 → 2.6.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 (38) hide show
  1. package/dist/{exact-direct → cjs/exact-direct}/client/index.d.ts +1 -1
  2. package/dist/{exact-direct/client/index.cjs → cjs/exact-direct/client/index.js} +5 -3
  3. package/dist/cjs/exact-direct/client/index.js.map +1 -0
  4. package/dist/{exact-direct → cjs/exact-direct}/facilitator/index.d.ts +1 -1
  5. package/dist/{exact-direct/facilitator/index.cjs → cjs/exact-direct/facilitator/index.js} +8 -6
  6. package/dist/cjs/exact-direct/facilitator/index.js.map +1 -0
  7. package/dist/{exact-direct/server/index.cjs → cjs/exact-direct/server/index.js} +6 -4
  8. package/dist/cjs/exact-direct/server/index.js.map +1 -0
  9. package/dist/{index.d.ts → cjs/index.d.ts} +2 -2
  10. package/dist/{index.cjs → cjs/index.js} +4 -4
  11. package/dist/cjs/index.js.map +1 -0
  12. package/dist/{types-Bxzo3eQ1.d.cts → cjs/types-1_F3N77p.d.ts} +1 -1
  13. package/dist/{index.mjs → esm/chunk-RN7W523U.mjs} +21 -15
  14. package/dist/esm/chunk-RN7W523U.mjs.map +1 -0
  15. package/dist/{exact-direct/client/index.d.cts → esm/exact-direct/client/index.d.mts} +1 -1
  16. package/dist/esm/exact-direct/client/index.mjs +69 -0
  17. package/dist/esm/exact-direct/client/index.mjs.map +1 -0
  18. package/dist/{exact-direct/facilitator/index.d.cts → esm/exact-direct/facilitator/index.d.mts} +1 -1
  19. package/dist/{exact-direct → esm/exact-direct}/facilitator/index.mjs +17 -164
  20. package/dist/esm/exact-direct/facilitator/index.mjs.map +1 -0
  21. package/dist/{exact-direct → esm/exact-direct}/server/index.mjs +13 -72
  22. package/dist/esm/exact-direct/server/index.mjs.map +1 -0
  23. package/dist/{index.d.cts → esm/index.d.mts} +2 -2
  24. package/dist/esm/index.mjs +51 -0
  25. package/dist/esm/index.mjs.map +1 -0
  26. package/dist/{types-Bxzo3eQ1.d.ts → esm/types-1_F3N77p.d.mts} +1 -1
  27. package/package.json +18 -16
  28. package/dist/exact-direct/client/index.cjs.map +0 -1
  29. package/dist/exact-direct/client/index.mjs +0 -139
  30. package/dist/exact-direct/client/index.mjs.map +0 -1
  31. package/dist/exact-direct/facilitator/index.cjs.map +0 -1
  32. package/dist/exact-direct/facilitator/index.mjs.map +0 -1
  33. package/dist/exact-direct/server/index.cjs.map +0 -1
  34. package/dist/exact-direct/server/index.mjs.map +0 -1
  35. package/dist/index.cjs.map +0 -1
  36. package/dist/index.mjs.map +0 -1
  37. /package/dist/{exact-direct → cjs/exact-direct}/server/index.d.ts +0 -0
  38. /package/dist/{exact-direct/server/index.d.cts → esm/exact-direct/server/index.d.mts} +0 -0
@@ -1,139 +0,0 @@
1
- // src/constants.ts
2
- var STACKS_CAIP2_NAMESPACE = "stacks";
3
- var STACKS_MAINNET_CAIP2 = "stacks:1";
4
- var STACKS_TESTNET_CAIP2 = "stacks:2147483648";
5
- var SCHEME_EXACT_DIRECT = "exact-direct";
6
- var DEFAULT_MAINNET_API = "https://api.mainnet.hiro.so";
7
- var DEFAULT_TESTNET_API = "https://api.testnet.hiro.so";
8
- var STACKS_NETWORKS = {
9
- [STACKS_MAINNET_CAIP2]: {
10
- name: "Stacks Mainnet",
11
- caip2: STACKS_MAINNET_CAIP2,
12
- apiUrl: DEFAULT_MAINNET_API,
13
- chainId: 1,
14
- addressPrefix: "SP",
15
- isTestnet: false
16
- },
17
- [STACKS_TESTNET_CAIP2]: {
18
- name: "Stacks Testnet",
19
- caip2: STACKS_TESTNET_CAIP2,
20
- apiUrl: DEFAULT_TESTNET_API,
21
- chainId: 2147483648,
22
- addressPrefix: "ST",
23
- isTestnet: true
24
- }
25
- };
26
-
27
- // src/tokens.ts
28
- var SUSDC_MAINNET = {
29
- contractAddress: "SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc",
30
- symbol: "sUSDC",
31
- name: "Stacks USDC",
32
- decimals: 6,
33
- issuer: "Stacks"
34
- };
35
- var SUSDC_TESTNET = {
36
- contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc",
37
- symbol: "sUSDC",
38
- name: "Test Stacks USDC",
39
- decimals: 6
40
- };
41
- var TOKEN_REGISTRY = {
42
- [STACKS_MAINNET_CAIP2]: {
43
- sUSDC: SUSDC_MAINNET
44
- },
45
- [STACKS_TESTNET_CAIP2]: {
46
- sUSDC: SUSDC_TESTNET
47
- }
48
- };
49
- var DEFAULT_TOKENS = {
50
- [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,
51
- [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET
52
- };
53
- function getTokenConfig(network, symbol = "sUSDC") {
54
- return TOKEN_REGISTRY[network]?.[symbol];
55
- }
56
- function getContractAddress(network, symbol = "sUSDC") {
57
- return getTokenConfig(network, symbol)?.contractAddress;
58
- }
59
-
60
- // src/utils.ts
61
- function isValidPrincipal(address) {
62
- if (!address || typeof address !== "string") {
63
- return false;
64
- }
65
- const parts = address.split(".");
66
- const principal = parts[0];
67
- const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;
68
- if (!principalRegex.test(principal)) {
69
- return false;
70
- }
71
- if (parts.length === 2) {
72
- const contractName = parts[1];
73
- const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\-_]{0,127}$/;
74
- return contractNameRegex.test(contractName);
75
- }
76
- return parts.length === 1;
77
- }
78
-
79
- // src/exact-direct/client/scheme.ts
80
- var ExactDirectStacksClient = class {
81
- scheme = SCHEME_EXACT_DIRECT;
82
- signer;
83
- constructor(config) {
84
- this.signer = config.signer;
85
- }
86
- /**
87
- * Create a payment payload by executing the transfer
88
- */
89
- async createPaymentPayload(t402Version, requirements) {
90
- this.validateRequirements(requirements);
91
- const { network, amount, payTo, extra } = requirements;
92
- const symbol = extra?.assetSymbol || "sUSDC";
93
- const contractAddress = extra?.contractAddress ?? getContractAddress(network, symbol);
94
- if (!contractAddress) {
95
- throw new Error(`Unknown asset ${symbol} on network ${network}`);
96
- }
97
- const from = await this.signer.getAddress();
98
- const { txId } = await this.signer.transferToken(contractAddress, payTo, amount);
99
- const stacksPayload = {
100
- txId,
101
- from,
102
- to: payTo,
103
- amount,
104
- contractAddress
105
- };
106
- return {
107
- t402Version,
108
- payload: stacksPayload
109
- };
110
- }
111
- /**
112
- * Validate payment requirements
113
- */
114
- validateRequirements(requirements) {
115
- if (requirements.scheme !== SCHEME_EXACT_DIRECT) {
116
- throw new Error(
117
- `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`
118
- );
119
- }
120
- if (!requirements.network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`)) {
121
- throw new Error(`Invalid network: ${requirements.network}`);
122
- }
123
- if (!isValidPrincipal(requirements.payTo)) {
124
- throw new Error(`Invalid payTo address: ${requirements.payTo}`);
125
- }
126
- const amount = BigInt(requirements.amount);
127
- if (amount <= 0n) {
128
- throw new Error(`Invalid amount: ${requirements.amount}`);
129
- }
130
- }
131
- };
132
- function createExactDirectStacksClient(config) {
133
- return new ExactDirectStacksClient(config);
134
- }
135
- export {
136
- ExactDirectStacksClient,
137
- createExactDirectStacksClient
138
- };
139
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/client/scheme.ts"],"sourcesContent":["/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n","/**\n * Stacks Exact-Direct Client Scheme\n *\n * In the exact-direct scheme, the client executes the SIP-010 token transfer\n * directly and provides the transaction ID as proof of payment.\n */\n\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkClient,\n} from \"@t402/core/types\";\nimport type { ClientStacksSigner, ExactDirectStacksPayload } from \"../../types.js\";\nimport { SCHEME_EXACT_DIRECT, STACKS_CAIP2_NAMESPACE } from \"../../constants.js\";\nimport { getContractAddress } from \"../../tokens.js\";\nimport { isValidPrincipal } from \"../../utils.js\";\n\n/**\n * Configuration for the exact-direct client\n */\nexport interface ExactDirectStacksClientConfig {\n /** Signer for executing transactions */\n signer: ClientStacksSigner;\n}\n\n/**\n * Exact-direct client scheme for Stacks\n */\nexport class ExactDirectStacksClient implements SchemeNetworkClient {\n readonly scheme = SCHEME_EXACT_DIRECT;\n private readonly signer: ClientStacksSigner;\n\n constructor(config: ExactDirectStacksClientConfig) {\n this.signer = config.signer;\n }\n\n /**\n * Create a payment payload by executing the transfer\n */\n async createPaymentPayload(\n t402Version: number,\n requirements: PaymentRequirements,\n ): Promise<Pick<PaymentPayload, \"t402Version\" | \"payload\">> {\n // Validate requirements\n this.validateRequirements(requirements);\n\n const { network, amount, payTo, extra } = requirements;\n\n // Get contract address from extra or use default sUSDC\n const symbol = (extra?.assetSymbol as string) || \"sUSDC\";\n const contractAddress =\n (extra?.contractAddress as string) ?? getContractAddress(network, symbol);\n\n if (!contractAddress) {\n throw new Error(`Unknown asset ${symbol} on network ${network}`);\n }\n\n // Get sender address\n const from = await this.signer.getAddress();\n\n // Execute the transfer\n const { txId } = await this.signer.transferToken(contractAddress, payTo, amount);\n\n // Build the payload\n const stacksPayload: ExactDirectStacksPayload = {\n txId,\n from,\n to: payTo,\n amount,\n contractAddress,\n };\n\n return {\n t402Version,\n payload: stacksPayload,\n };\n }\n\n /**\n * Validate payment requirements\n */\n private validateRequirements(requirements: PaymentRequirements): void {\n // Check scheme\n if (requirements.scheme !== SCHEME_EXACT_DIRECT) {\n throw new Error(\n `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`,\n );\n }\n\n // Check network\n if (!requirements.network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`)) {\n throw new Error(`Invalid network: ${requirements.network}`);\n }\n\n // Check payTo address\n if (!isValidPrincipal(requirements.payTo)) {\n throw new Error(`Invalid payTo address: ${requirements.payTo}`);\n }\n\n // Check amount\n const amount = BigInt(requirements.amount);\n if (amount <= 0n) {\n throw new Error(`Invalid amount: ${requirements.amount}`);\n }\n }\n}\n\n/**\n * Create an exact-direct client for Stacks\n */\nexport function createExactDirectStacksClient(\n config: ExactDirectStacksClientConfig,\n): ExactDirectStacksClient {\n return new ExactDirectStacksClient(config);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/exact-direct/facilitator/index.ts","../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/facilitator/scheme.ts"],"sourcesContent":["export {\n ExactDirectStacksFacilitator,\n createExactDirectStacksFacilitator,\n} from \"./scheme.js\";\n","/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n","/**\n * Stacks Exact-Direct Facilitator Scheme\n *\n * Verifies that a Stacks SIP-010 token transfer was executed correctly\n * by querying the Hiro API.\n */\n\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@t402/core/types\";\nimport { STACKS_CAIP2_NAMESPACE, SCHEME_EXACT_DIRECT, getNetworkConfig } from \"../../constants.js\";\nimport { getDefaultToken } from \"../../tokens.js\";\nimport type {\n ExactDirectStacksPayload,\n FacilitatorStacksSigner,\n StacksFacilitatorConfig,\n} from \"../../types.js\";\nimport {\n comparePrincipals,\n extractTokenTransfer,\n extractTokenTransferFromPostConditions,\n isValidTxId,\n isValidPrincipal,\n} from \"../../utils.js\";\n\n// Default configuration\nconst DEFAULT_MAX_TRANSACTION_AGE = 3600; // 1 hour\nconst DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in ms\n\n/**\n * Exact-direct facilitator scheme for Stacks\n */\nexport class ExactDirectStacksFacilitator implements SchemeNetworkFacilitator {\n readonly scheme = SCHEME_EXACT_DIRECT;\n readonly caipFamily = `${STACKS_CAIP2_NAMESPACE}:*`;\n\n private readonly signer: FacilitatorStacksSigner;\n private readonly config: Required<StacksFacilitatorConfig>;\n private readonly usedTransactions = new Map<string, number>();\n\n constructor(\n signer: FacilitatorStacksSigner,\n config: StacksFacilitatorConfig = {},\n ) {\n this.signer = signer;\n this.config = {\n maxTransactionAge: config.maxTransactionAge ?? DEFAULT_MAX_TRANSACTION_AGE,\n usedTxCacheDuration:\n config.usedTxCacheDuration ?? DEFAULT_CACHE_DURATION,\n };\n\n // Start cleanup interval\n this.startCleanupInterval();\n }\n\n /**\n * Get extra data for payment requirements\n */\n getExtra(network: Network): Record<string, unknown> | undefined {\n const config = getNetworkConfig(network);\n if (!config) return undefined;\n\n const token = getDefaultToken(network);\n return {\n contractAddress: token?.contractAddress,\n assetSymbol: token?.symbol,\n assetDecimals: token?.decimals,\n networkName: config.name,\n };\n }\n\n /**\n * Get facilitator signer addresses for a network\n */\n getSigners(network: Network): string[] {\n return this.signer.getAddresses(network);\n }\n\n /**\n * Verify a payment payload\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const network = requirements.network;\n\n // Validate scheme\n if (payload.accepted.scheme !== SCHEME_EXACT_DIRECT) {\n return {\n isValid: false,\n invalidReason: `invalid_scheme: expected ${SCHEME_EXACT_DIRECT}, got ${payload.accepted.scheme}`,\n };\n }\n\n // Validate network\n if (payload.accepted.network !== network) {\n return {\n isValid: false,\n invalidReason: `network_mismatch: expected ${network}, got ${payload.accepted.network}`,\n };\n }\n\n // Parse payload\n const stacksPayload = payload.payload as unknown as ExactDirectStacksPayload;\n\n // Validate required fields\n if (!stacksPayload.txId) {\n return {\n isValid: false,\n invalidReason: \"missing_tx_id\",\n };\n }\n\n // Validate tx ID format\n if (!isValidTxId(stacksPayload.txId)) {\n return {\n isValid: false,\n invalidReason: \"invalid_tx_id_format\",\n };\n }\n\n // Validate from address\n if (!stacksPayload.from) {\n return {\n isValid: false,\n invalidReason: \"missing_from_address\",\n };\n }\n\n if (!isValidPrincipal(stacksPayload.from)) {\n return {\n isValid: false,\n invalidReason: \"invalid_from_address\",\n payer: stacksPayload.from,\n };\n }\n\n // Check for replay attack\n if (this.isTransactionUsed(stacksPayload.txId)) {\n return {\n isValid: false,\n invalidReason: \"transaction_already_used\",\n payer: stacksPayload.from,\n };\n }\n\n // Query transaction\n const txResult = await this.signer.queryTransaction(stacksPayload.txId);\n\n if (!txResult) {\n return {\n isValid: false,\n invalidReason: \"transaction_not_found\",\n payer: stacksPayload.from,\n };\n }\n\n // Verify transaction was successful\n if (txResult.txStatus !== \"success\") {\n return {\n isValid: false,\n invalidReason: `transaction_failed: status=${txResult.txStatus}`,\n payer: stacksPayload.from,\n };\n }\n\n // Check transaction age\n if (this.config.maxTransactionAge > 0) {\n const txTime = txResult.burnBlockTime * 1000; // Convert to milliseconds\n const age = (Date.now() - txTime) / 1000;\n if (age > this.config.maxTransactionAge) {\n return {\n isValid: false,\n invalidReason: `transaction_too_old: ${Math.round(age)} seconds`,\n payer: stacksPayload.from,\n };\n }\n }\n\n // Extract transfer details\n const expectedContract = (requirements.extra?.contractAddress as string) ??\n stacksPayload.contractAddress;\n\n const transfer =\n extractTokenTransfer(txResult, expectedContract) ||\n extractTokenTransferFromPostConditions(txResult, expectedContract);\n\n if (!transfer) {\n return {\n isValid: false,\n invalidReason: \"not_token_transfer\",\n payer: stacksPayload.from,\n };\n }\n\n // Verify contract address\n if (expectedContract && !comparePrincipals(transfer.contractAddress, expectedContract)) {\n return {\n isValid: false,\n invalidReason: `contract_mismatch: expected ${expectedContract}, got ${transfer.contractAddress}`,\n payer: stacksPayload.from,\n };\n }\n\n // Verify recipient\n if (!comparePrincipals(transfer.to, requirements.payTo)) {\n return {\n isValid: false,\n invalidReason: `recipient_mismatch: expected ${requirements.payTo}, got ${transfer.to}`,\n payer: stacksPayload.from,\n };\n }\n\n // Verify amount\n const txAmount = BigInt(transfer.amount);\n const requiredAmount = BigInt(requirements.amount);\n if (txAmount < requiredAmount) {\n return {\n isValid: false,\n invalidReason: `insufficient_amount: expected ${requirements.amount}, got ${transfer.amount}`,\n payer: stacksPayload.from,\n };\n }\n\n // Mark transaction as used\n this.markTransactionUsed(stacksPayload.txId);\n\n return {\n isValid: true,\n payer: stacksPayload.from,\n };\n }\n\n /**\n * Settle a payment (for exact-direct, the transfer is already complete)\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // Verify first\n const verifyResult = await this.verify(payload, requirements);\n\n if (!verifyResult.isValid) {\n return {\n success: false,\n errorReason: verifyResult.invalidReason || \"verification_failed\",\n payer: verifyResult.payer,\n transaction: \"\",\n network: requirements.network,\n };\n }\n\n const stacksPayload = payload.payload as unknown as ExactDirectStacksPayload;\n\n // For exact-direct, settlement is already complete\n return {\n success: true,\n transaction: stacksPayload.txId,\n network: requirements.network,\n payer: verifyResult.payer,\n };\n }\n\n /**\n * Check if a transaction has been used\n */\n private isTransactionUsed(txId: string): boolean {\n return this.usedTransactions.has(txId);\n }\n\n /**\n * Mark a transaction as used\n */\n private markTransactionUsed(txId: string): void {\n this.usedTransactions.set(txId, Date.now());\n }\n\n /**\n * Start the cleanup interval for used transactions cache\n */\n private startCleanupInterval(): void {\n setInterval(() => {\n const cutoff = Date.now() - this.config.usedTxCacheDuration;\n for (const [txId, timestamp] of this.usedTransactions) {\n if (timestamp < cutoff) {\n this.usedTransactions.delete(txId);\n }\n }\n }, 60 * 60 * 1000); // Run every hour\n }\n}\n\n/**\n * Create an exact-direct facilitator for Stacks\n */\nexport function createExactDirectStacksFacilitator(\n signer: FacilitatorStacksSigner,\n config: StacksFacilitatorConfig = {},\n): ExactDirectStacksFacilitator {\n return new ExactDirectStacksFacilitator(signer, config);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/facilitator/scheme.ts"],"sourcesContent":["/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n","/**\n * Stacks Exact-Direct Facilitator Scheme\n *\n * Verifies that a Stacks SIP-010 token transfer was executed correctly\n * by querying the Hiro API.\n */\n\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@t402/core/types\";\nimport { STACKS_CAIP2_NAMESPACE, SCHEME_EXACT_DIRECT, getNetworkConfig } from \"../../constants.js\";\nimport { getDefaultToken } from \"../../tokens.js\";\nimport type {\n ExactDirectStacksPayload,\n FacilitatorStacksSigner,\n StacksFacilitatorConfig,\n} from \"../../types.js\";\nimport {\n comparePrincipals,\n extractTokenTransfer,\n extractTokenTransferFromPostConditions,\n isValidTxId,\n isValidPrincipal,\n} from \"../../utils.js\";\n\n// Default configuration\nconst DEFAULT_MAX_TRANSACTION_AGE = 3600; // 1 hour\nconst DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in ms\n\n/**\n * Exact-direct facilitator scheme for Stacks\n */\nexport class ExactDirectStacksFacilitator implements SchemeNetworkFacilitator {\n readonly scheme = SCHEME_EXACT_DIRECT;\n readonly caipFamily = `${STACKS_CAIP2_NAMESPACE}:*`;\n\n private readonly signer: FacilitatorStacksSigner;\n private readonly config: Required<StacksFacilitatorConfig>;\n private readonly usedTransactions = new Map<string, number>();\n\n constructor(\n signer: FacilitatorStacksSigner,\n config: StacksFacilitatorConfig = {},\n ) {\n this.signer = signer;\n this.config = {\n maxTransactionAge: config.maxTransactionAge ?? DEFAULT_MAX_TRANSACTION_AGE,\n usedTxCacheDuration:\n config.usedTxCacheDuration ?? DEFAULT_CACHE_DURATION,\n };\n\n // Start cleanup interval\n this.startCleanupInterval();\n }\n\n /**\n * Get extra data for payment requirements\n */\n getExtra(network: Network): Record<string, unknown> | undefined {\n const config = getNetworkConfig(network);\n if (!config) return undefined;\n\n const token = getDefaultToken(network);\n return {\n contractAddress: token?.contractAddress,\n assetSymbol: token?.symbol,\n assetDecimals: token?.decimals,\n networkName: config.name,\n };\n }\n\n /**\n * Get facilitator signer addresses for a network\n */\n getSigners(network: Network): string[] {\n return this.signer.getAddresses(network);\n }\n\n /**\n * Verify a payment payload\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const network = requirements.network;\n\n // Validate scheme\n if (payload.accepted.scheme !== SCHEME_EXACT_DIRECT) {\n return {\n isValid: false,\n invalidReason: `invalid_scheme: expected ${SCHEME_EXACT_DIRECT}, got ${payload.accepted.scheme}`,\n };\n }\n\n // Validate network\n if (payload.accepted.network !== network) {\n return {\n isValid: false,\n invalidReason: `network_mismatch: expected ${network}, got ${payload.accepted.network}`,\n };\n }\n\n // Parse payload\n const stacksPayload = payload.payload as unknown as ExactDirectStacksPayload;\n\n // Validate required fields\n if (!stacksPayload.txId) {\n return {\n isValid: false,\n invalidReason: \"missing_tx_id\",\n };\n }\n\n // Validate tx ID format\n if (!isValidTxId(stacksPayload.txId)) {\n return {\n isValid: false,\n invalidReason: \"invalid_tx_id_format\",\n };\n }\n\n // Validate from address\n if (!stacksPayload.from) {\n return {\n isValid: false,\n invalidReason: \"missing_from_address\",\n };\n }\n\n if (!isValidPrincipal(stacksPayload.from)) {\n return {\n isValid: false,\n invalidReason: \"invalid_from_address\",\n payer: stacksPayload.from,\n };\n }\n\n // Check for replay attack\n if (this.isTransactionUsed(stacksPayload.txId)) {\n return {\n isValid: false,\n invalidReason: \"transaction_already_used\",\n payer: stacksPayload.from,\n };\n }\n\n // Query transaction\n const txResult = await this.signer.queryTransaction(stacksPayload.txId);\n\n if (!txResult) {\n return {\n isValid: false,\n invalidReason: \"transaction_not_found\",\n payer: stacksPayload.from,\n };\n }\n\n // Verify transaction was successful\n if (txResult.txStatus !== \"success\") {\n return {\n isValid: false,\n invalidReason: `transaction_failed: status=${txResult.txStatus}`,\n payer: stacksPayload.from,\n };\n }\n\n // Check transaction age\n if (this.config.maxTransactionAge > 0) {\n const txTime = txResult.burnBlockTime * 1000; // Convert to milliseconds\n const age = (Date.now() - txTime) / 1000;\n if (age > this.config.maxTransactionAge) {\n return {\n isValid: false,\n invalidReason: `transaction_too_old: ${Math.round(age)} seconds`,\n payer: stacksPayload.from,\n };\n }\n }\n\n // Extract transfer details\n const expectedContract = (requirements.extra?.contractAddress as string) ??\n stacksPayload.contractAddress;\n\n const transfer =\n extractTokenTransfer(txResult, expectedContract) ||\n extractTokenTransferFromPostConditions(txResult, expectedContract);\n\n if (!transfer) {\n return {\n isValid: false,\n invalidReason: \"not_token_transfer\",\n payer: stacksPayload.from,\n };\n }\n\n // Verify contract address\n if (expectedContract && !comparePrincipals(transfer.contractAddress, expectedContract)) {\n return {\n isValid: false,\n invalidReason: `contract_mismatch: expected ${expectedContract}, got ${transfer.contractAddress}`,\n payer: stacksPayload.from,\n };\n }\n\n // Verify recipient\n if (!comparePrincipals(transfer.to, requirements.payTo)) {\n return {\n isValid: false,\n invalidReason: `recipient_mismatch: expected ${requirements.payTo}, got ${transfer.to}`,\n payer: stacksPayload.from,\n };\n }\n\n // Verify amount\n const txAmount = BigInt(transfer.amount);\n const requiredAmount = BigInt(requirements.amount);\n if (txAmount < requiredAmount) {\n return {\n isValid: false,\n invalidReason: `insufficient_amount: expected ${requirements.amount}, got ${transfer.amount}`,\n payer: stacksPayload.from,\n };\n }\n\n // Mark transaction as used\n this.markTransactionUsed(stacksPayload.txId);\n\n return {\n isValid: true,\n payer: stacksPayload.from,\n };\n }\n\n /**\n * Settle a payment (for exact-direct, the transfer is already complete)\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // Verify first\n const verifyResult = await this.verify(payload, requirements);\n\n if (!verifyResult.isValid) {\n return {\n success: false,\n errorReason: verifyResult.invalidReason || \"verification_failed\",\n payer: verifyResult.payer,\n transaction: \"\",\n network: requirements.network,\n };\n }\n\n const stacksPayload = payload.payload as unknown as ExactDirectStacksPayload;\n\n // For exact-direct, settlement is already complete\n return {\n success: true,\n transaction: stacksPayload.txId,\n network: requirements.network,\n payer: verifyResult.payer,\n };\n }\n\n /**\n * Check if a transaction has been used\n */\n private isTransactionUsed(txId: string): boolean {\n return this.usedTransactions.has(txId);\n }\n\n /**\n * Mark a transaction as used\n */\n private markTransactionUsed(txId: string): void {\n this.usedTransactions.set(txId, Date.now());\n }\n\n /**\n * Start the cleanup interval for used transactions cache\n */\n private startCleanupInterval(): void {\n setInterval(() => {\n const cutoff = Date.now() - this.config.usedTxCacheDuration;\n for (const [txId, timestamp] of this.usedTransactions) {\n if (timestamp < cutoff) {\n this.usedTransactions.delete(txId);\n }\n }\n }, 60 * 60 * 1000); // Run every hour\n }\n}\n\n/**\n * Create an exact-direct facilitator for Stacks\n */\nexport function createExactDirectStacksFacilitator(\n signer: FacilitatorStacksSigner,\n config: StacksFacilitatorConfig = {},\n): ExactDirectStacksFacilitator {\n return new ExactDirectStacksFacilitator(signer, config);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/exact-direct/server/index.ts","../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/server/scheme.ts","../../../src/exact-direct/server/register.ts"],"sourcesContent":["export {\n ExactDirectStacksServer,\n createExactDirectStacksServer,\n} from \"./scheme.js\";\n\nexport {\n registerExactDirectStacksServer,\n type StacksServerRegistrationConfig,\n} from \"./register.js\";\n","/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n","/**\n * Stacks Exact-Direct Server Scheme\n *\n * Handles price parsing and payment requirement enhancement for\n * Stacks payments using the exact-direct scheme.\n */\n\nimport type {\n SchemeNetworkServer,\n PaymentRequirements,\n Price,\n AssetAmount,\n Network,\n MoneyParser,\n} from \"@t402/core/types\";\nimport { SCHEME_EXACT_DIRECT, isStacksNetwork } from \"../../constants.js\";\nimport { getDefaultToken, getTokenConfig, TOKEN_REGISTRY } from \"../../tokens.js\";\nimport { parseAmount } from \"../../utils.js\";\n\n/**\n * Configuration for ExactDirectStacksServer\n */\nexport interface ExactDirectStacksServerConfig {\n /** Preferred token symbol (e.g., \"sUSDC\"). Defaults to network's default token. */\n preferredToken?: string;\n}\n\n/**\n * Stacks Exact-Direct Server\n *\n * Implements the server-side price parsing and payment requirements enhancement.\n */\nexport class ExactDirectStacksServer implements SchemeNetworkServer {\n readonly scheme = SCHEME_EXACT_DIRECT;\n private moneyParsers: MoneyParser[] = [];\n private config: ExactDirectStacksServerConfig;\n\n constructor(config: ExactDirectStacksServerConfig = {}) {\n this.config = config;\n }\n\n /**\n * Register a custom money parser in the parser chain.\n */\n registerMoneyParser(parser: MoneyParser): ExactDirectStacksServer {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parse price into Stacks-specific amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // Validate network\n if (!isStacksNetwork(network)) {\n throw new Error(`Invalid Stacks network: ${network}`);\n }\n\n // If already an AssetAmount, return it directly\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price);\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Enhance payment requirements with Stacks-specific details\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n t402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n facilitatorExtensions: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameters\n void facilitatorExtensions;\n\n // Start with existing extra fields\n const extra = { ...paymentRequirements.extra };\n\n // Add any facilitator-provided extra fields\n if (supportedKind.extra?.contractAddress) {\n extra.contractAddress = supportedKind.extra.contractAddress;\n }\n if (supportedKind.extra?.assetSymbol) {\n extra.assetSymbol = supportedKind.extra.assetSymbol;\n }\n if (supportedKind.extra?.assetDecimals) {\n extra.assetDecimals = supportedKind.extra.assetDecimals;\n }\n if (supportedKind.extra?.networkName) {\n extra.networkName = supportedKind.extra.networkName;\n }\n\n return {\n ...paymentRequirements,\n extra,\n };\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Default money conversion implementation.\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const token = this.getDefaultAsset(network);\n\n // Convert decimal amount to token amount\n const tokenAmount = parseAmount(amount.toString(), token.decimals);\n\n return {\n amount: tokenAmount.toString(),\n asset: this.createAssetIdentifier(network, token.contractAddress),\n extra: {\n symbol: token.symbol,\n name: token.name,\n decimals: token.decimals,\n contractAddress: token.contractAddress,\n },\n };\n }\n\n /**\n * Create a CAIP-19 asset identifier for Stacks tokens\n */\n private createAssetIdentifier(\n network: Network,\n contractAddress: string,\n ): string {\n return `${network}/sip010:${contractAddress}`;\n }\n\n /**\n * Get the default asset info for a network.\n */\n private getDefaultAsset(\n network: Network,\n ): { contractAddress: string; symbol: string; name: string; decimals: number } {\n // If a preferred token is configured, try to use it\n if (this.config.preferredToken) {\n const preferred = getTokenConfig(network, this.config.preferredToken);\n if (preferred) return preferred;\n }\n\n // Use the network's default token\n const defaultToken = getDefaultToken(network);\n if (defaultToken) return defaultToken;\n\n throw new Error(`No tokens configured for network ${network}`);\n }\n\n /**\n * Get all supported networks\n */\n static getSupportedNetworks(): string[] {\n return Object.keys(TOKEN_REGISTRY);\n }\n\n /**\n * Check if a network is supported\n */\n static isNetworkSupported(network: string): boolean {\n return network in TOKEN_REGISTRY;\n }\n}\n\n/**\n * Create an exact-direct server for Stacks\n */\nexport function createExactDirectStacksServer(\n config: ExactDirectStacksServerConfig = {},\n): ExactDirectStacksServer {\n return new ExactDirectStacksServer(config);\n}\n","/**\n * Registration function for Stacks exact-direct server\n */\n\nimport { t402ResourceServer } from \"@t402/core/server\";\nimport type { Network } from \"@t402/core/types\";\nimport { STACKS_CAIP2_NAMESPACE } from \"../../constants.js\";\nimport { ExactDirectStacksServer, type ExactDirectStacksServerConfig } from \"./scheme.js\";\n\n/**\n * Configuration for registering Stacks server schemes\n */\nexport interface StacksServerRegistrationConfig extends ExactDirectStacksServerConfig {\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (stacks:*)\n */\n networks?: Network[];\n}\n\n/**\n * Registers Stacks exact-direct payment scheme to a t402ResourceServer instance.\n *\n * @param server - The t402ResourceServer instance to register schemes to\n * @param config - Configuration for Stacks server registration\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactDirectStacksServer } from \"@t402/stacks/exact-direct/server\";\n * import { t402ResourceServer } from \"@t402/core/server\";\n *\n * const server = new t402ResourceServer();\n * registerExactDirectStacksServer(server, {\n * networks: [\"stacks:1\"]\n * });\n * ```\n */\nexport function registerExactDirectStacksServer(\n server: t402ResourceServer,\n config: StacksServerRegistrationConfig = {},\n): t402ResourceServer {\n const scheme = new ExactDirectStacksServer(config);\n\n // Register scheme\n if (config.networks && config.networks.length > 0) {\n // Register specific networks\n config.networks.forEach((network) => {\n server.register(network, scheme);\n });\n } else {\n // Register wildcard for all Stacks networks\n server.register(`${STACKS_CAIP2_NAMESPACE}:*`, scheme);\n }\n\n return server;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/server/scheme.ts","../../../src/exact-direct/server/register.ts"],"sourcesContent":["/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n","/**\n * Stacks Exact-Direct Server Scheme\n *\n * Handles price parsing and payment requirement enhancement for\n * Stacks payments using the exact-direct scheme.\n */\n\nimport type {\n SchemeNetworkServer,\n PaymentRequirements,\n Price,\n AssetAmount,\n Network,\n MoneyParser,\n} from \"@t402/core/types\";\nimport { SCHEME_EXACT_DIRECT, isStacksNetwork } from \"../../constants.js\";\nimport { getDefaultToken, getTokenConfig, TOKEN_REGISTRY } from \"../../tokens.js\";\nimport { parseAmount } from \"../../utils.js\";\n\n/**\n * Configuration for ExactDirectStacksServer\n */\nexport interface ExactDirectStacksServerConfig {\n /** Preferred token symbol (e.g., \"sUSDC\"). Defaults to network's default token. */\n preferredToken?: string;\n}\n\n/**\n * Stacks Exact-Direct Server\n *\n * Implements the server-side price parsing and payment requirements enhancement.\n */\nexport class ExactDirectStacksServer implements SchemeNetworkServer {\n readonly scheme = SCHEME_EXACT_DIRECT;\n private moneyParsers: MoneyParser[] = [];\n private config: ExactDirectStacksServerConfig;\n\n constructor(config: ExactDirectStacksServerConfig = {}) {\n this.config = config;\n }\n\n /**\n * Register a custom money parser in the parser chain.\n */\n registerMoneyParser(parser: MoneyParser): ExactDirectStacksServer {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parse price into Stacks-specific amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // Validate network\n if (!isStacksNetwork(network)) {\n throw new Error(`Invalid Stacks network: ${network}`);\n }\n\n // If already an AssetAmount, return it directly\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price);\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Enhance payment requirements with Stacks-specific details\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n t402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n facilitatorExtensions: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameters\n void facilitatorExtensions;\n\n // Start with existing extra fields\n const extra = { ...paymentRequirements.extra };\n\n // Add any facilitator-provided extra fields\n if (supportedKind.extra?.contractAddress) {\n extra.contractAddress = supportedKind.extra.contractAddress;\n }\n if (supportedKind.extra?.assetSymbol) {\n extra.assetSymbol = supportedKind.extra.assetSymbol;\n }\n if (supportedKind.extra?.assetDecimals) {\n extra.assetDecimals = supportedKind.extra.assetDecimals;\n }\n if (supportedKind.extra?.networkName) {\n extra.networkName = supportedKind.extra.networkName;\n }\n\n return {\n ...paymentRequirements,\n extra,\n };\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Default money conversion implementation.\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const token = this.getDefaultAsset(network);\n\n // Convert decimal amount to token amount\n const tokenAmount = parseAmount(amount.toString(), token.decimals);\n\n return {\n amount: tokenAmount.toString(),\n asset: this.createAssetIdentifier(network, token.contractAddress),\n extra: {\n symbol: token.symbol,\n name: token.name,\n decimals: token.decimals,\n contractAddress: token.contractAddress,\n },\n };\n }\n\n /**\n * Create a CAIP-19 asset identifier for Stacks tokens\n */\n private createAssetIdentifier(\n network: Network,\n contractAddress: string,\n ): string {\n return `${network}/sip010:${contractAddress}`;\n }\n\n /**\n * Get the default asset info for a network.\n */\n private getDefaultAsset(\n network: Network,\n ): { contractAddress: string; symbol: string; name: string; decimals: number } {\n // If a preferred token is configured, try to use it\n if (this.config.preferredToken) {\n const preferred = getTokenConfig(network, this.config.preferredToken);\n if (preferred) return preferred;\n }\n\n // Use the network's default token\n const defaultToken = getDefaultToken(network);\n if (defaultToken) return defaultToken;\n\n throw new Error(`No tokens configured for network ${network}`);\n }\n\n /**\n * Get all supported networks\n */\n static getSupportedNetworks(): string[] {\n return Object.keys(TOKEN_REGISTRY);\n }\n\n /**\n * Check if a network is supported\n */\n static isNetworkSupported(network: string): boolean {\n return network in TOKEN_REGISTRY;\n }\n}\n\n/**\n * Create an exact-direct server for Stacks\n */\nexport function createExactDirectStacksServer(\n config: ExactDirectStacksServerConfig = {},\n): ExactDirectStacksServer {\n return new ExactDirectStacksServer(config);\n}\n","/**\n * Registration function for Stacks exact-direct server\n */\n\nimport { t402ResourceServer } from \"@t402/core/server\";\nimport type { Network } from \"@t402/core/types\";\nimport { STACKS_CAIP2_NAMESPACE } from \"../../constants.js\";\nimport { ExactDirectStacksServer, type ExactDirectStacksServerConfig } from \"./scheme.js\";\n\n/**\n * Configuration for registering Stacks server schemes\n */\nexport interface StacksServerRegistrationConfig extends ExactDirectStacksServerConfig {\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (stacks:*)\n */\n networks?: Network[];\n}\n\n/**\n * Registers Stacks exact-direct payment scheme to a t402ResourceServer instance.\n *\n * @param server - The t402ResourceServer instance to register schemes to\n * @param config - Configuration for Stacks server registration\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * import { registerExactDirectStacksServer } from \"@t402/stacks/exact-direct/server\";\n * import { t402ResourceServer } from \"@t402/core/server\";\n *\n * const server = new t402ResourceServer();\n * registerExactDirectStacksServer(server, {\n * networks: [\"stacks:1\"]\n * });\n * ```\n */\nexport function registerExactDirectStacksServer(\n server: t402ResourceServer,\n config: StacksServerRegistrationConfig = {},\n): t402ResourceServer {\n const scheme = new ExactDirectStacksServer(config);\n\n // Register scheme\n if (config.networks && config.networks.length > 0) {\n // Register specific networks\n config.networks.forEach((network) => {\n server.register(network, scheme);\n });\n } else {\n // Register wildcard for all Stacks networks\n server.register(`${STACKS_CAIP2_NAMESPACE}:*`, scheme);\n }\n\n return server;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/tokens.ts","../src/utils.ts"],"sourcesContent":["/**\n * @t402/stacks - Stacks mechanism for T402\n *\n * This package provides support for SIP-010 token payments on Stacks (Bitcoin L2)\n * using the exact-direct scheme.\n *\n * @example\n * ```typescript\n * // Client usage\n * import { createExactDirectStacksClient } from '@t402/stacks/exact-direct/client';\n *\n * const client = createExactDirectStacksClient({\n * signer: myStacksSigner,\n * });\n *\n * // Server usage\n * import { registerExactDirectStacksServer } from '@t402/stacks/exact-direct/server';\n *\n * registerExactDirectStacksServer(server);\n *\n * // Facilitator usage\n * import { createExactDirectStacksFacilitator } from '@t402/stacks/exact-direct/facilitator';\n *\n * const facilitator = createExactDirectStacksFacilitator(signer);\n * ```\n */\n\n// Re-export constants\nexport {\n STACKS_CAIP2_NAMESPACE,\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n SCHEME_EXACT_DIRECT,\n DEFAULT_MAINNET_API,\n DEFAULT_TESTNET_API,\n STACKS_NETWORKS,\n getNetworkConfig,\n isStacksNetwork,\n type StacksNetworkConfig,\n} from \"./constants.js\";\n\n// Re-export token registry\nexport {\n SUSDC_MAINNET,\n SUSDC_TESTNET,\n TOKEN_REGISTRY,\n DEFAULT_TOKENS,\n getTokenConfig,\n getDefaultToken,\n getContractAddress,\n type TokenConfig,\n} from \"./tokens.js\";\n\n// Re-export types\nexport type {\n ExactDirectStacksPayload,\n StacksTransactionResult,\n StacksContractCall,\n StacksFunctionArg,\n StacksPostCondition,\n StacksEvent,\n ParsedTokenTransfer,\n FacilitatorStacksSigner,\n ClientStacksSigner,\n StacksServerConfig,\n StacksFacilitatorConfig,\n} from \"./types.js\";\n\n// Re-export utilities\nexport {\n isValidPrincipal,\n isValidTxId,\n comparePrincipals,\n formatAmount,\n parseAmount,\n extractTokenTransfer,\n extractTokenTransferFromPostConditions,\n} from \"./utils.js\";\n","/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/tokens.ts","../src/utils.ts"],"sourcesContent":["/**\n * Stacks T402 Constants\n *\n * Stacks is a Bitcoin Layer 2 that brings smart contracts and DeFi\n * to Bitcoin. SIP-010 is the fungible token standard on Stacks.\n */\n\n// CAIP-2 namespace for Stacks\nexport const STACKS_CAIP2_NAMESPACE = \"stacks\";\n\n// CAIP-2 network identifiers\n// Stacks Mainnet (chain ID: 1)\nexport const STACKS_MAINNET_CAIP2 = \"stacks:1\";\n\n// Stacks Testnet (chain ID: 2147483648)\nexport const STACKS_TESTNET_CAIP2 = \"stacks:2147483648\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default API endpoints (Hiro API)\nexport const DEFAULT_MAINNET_API = \"https://api.mainnet.hiro.so\";\nexport const DEFAULT_TESTNET_API = \"https://api.testnet.hiro.so\";\n\n// Network configurations\nexport interface StacksNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly apiUrl: string;\n readonly chainId: number;\n readonly addressPrefix: string;\n readonly isTestnet: boolean;\n}\n\nexport const STACKS_NETWORKS: Record<string, StacksNetworkConfig> = {\n [STACKS_MAINNET_CAIP2]: {\n name: \"Stacks Mainnet\",\n caip2: STACKS_MAINNET_CAIP2,\n apiUrl: DEFAULT_MAINNET_API,\n chainId: 1,\n addressPrefix: \"SP\",\n isTestnet: false,\n },\n [STACKS_TESTNET_CAIP2]: {\n name: \"Stacks Testnet\",\n caip2: STACKS_TESTNET_CAIP2,\n apiUrl: DEFAULT_TESTNET_API,\n chainId: 2147483648,\n addressPrefix: \"ST\",\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): StacksNetworkConfig | undefined {\n return STACKS_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Stacks network\n */\nexport function isStacksNetwork(network: string): boolean {\n return network.startsWith(`${STACKS_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Stacks Token Registry\n *\n * On Stacks, tokens are SIP-010 fungible tokens identified by\n * their contract address (principal.contract-name).\n */\n\nimport {\n STACKS_MAINNET_CAIP2,\n STACKS_TESTNET_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Stacks SIP-010 tokens\n */\nexport interface TokenConfig {\n /** Contract address (principal.contract-name) */\n readonly contractAddress: string;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Token issuer */\n readonly issuer?: string;\n}\n\n/**\n * sUSDC on Stacks Mainnet\n * Contract: SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_MAINNET: TokenConfig = {\n contractAddress: \"SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Stacks USDC\",\n decimals: 6,\n issuer: \"Stacks\",\n};\n\n/**\n * sUSDC on Stacks Testnet\n * Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\n * Decimals: 6\n */\nexport const SUSDC_TESTNET: TokenConfig = {\n contractAddress: \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc\",\n symbol: \"sUSDC\",\n name: \"Test Stacks USDC\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [STACKS_MAINNET_CAIP2]: {\n sUSDC: SUSDC_MAINNET,\n },\n [STACKS_TESTNET_CAIP2]: {\n sUSDC: SUSDC_TESTNET,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [STACKS_MAINNET_CAIP2]: SUSDC_MAINNET,\n [STACKS_TESTNET_CAIP2]: SUSDC_TESTNET,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"sUSDC\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get contract address for a token on a network\n */\nexport function getContractAddress(network: string, symbol: string = \"sUSDC\"): string | undefined {\n return getTokenConfig(network, symbol)?.contractAddress;\n}\n","/**\n * Stacks Utility Functions\n */\n\nimport type { StacksTransactionResult, ParsedTokenTransfer } from \"./types.js\";\n\n/**\n * Validate a Stacks principal address format\n * Stacks addresses start with SP (mainnet) or ST (testnet)\n * followed by alphanumeric characters (base58-like encoding)\n */\nexport function isValidPrincipal(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)\n // Contract principal: standard-principal.contract-name\n const parts = address.split(\".\");\n const principal = parts[0];\n\n // Check principal format: SP or ST prefix + alphanumeric (base58 chars)\n const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;\n if (!principalRegex.test(principal)) {\n return false;\n }\n\n // If it's a contract principal, validate contract name\n if (parts.length === 2) {\n const contractName = parts[1];\n // Contract names: 1-128 chars, alphanumeric + hyphen + underscore\n const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\\-_]{0,127}$/;\n return contractNameRegex.test(contractName);\n }\n\n // Standard principal (no contract part) or exactly one dot for contract\n return parts.length === 1;\n}\n\n/**\n * Validate a Stacks transaction ID format\n * Transaction IDs are 0x-prefixed 64-character hex strings\n */\nexport function isValidTxId(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Compare two Stacks principals (case-sensitive)\n */\nexport function comparePrincipals(a: string, b: string): boolean {\n return a === b;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract token transfer details from a Stacks transaction result\n * Looks for ft_transfer events matching the expected contract\n */\nexport function extractTokenTransfer(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Check if this is a contract-call transaction\n if (result.txType !== \"contract_call\") {\n return null;\n }\n\n // Check contract call is a transfer function\n if (result.contractCall) {\n const { contractId, functionName } = result.contractCall;\n\n if (functionName !== \"transfer\") {\n return null;\n }\n\n // If contractAddress specified, verify it matches\n if (contractAddress && contractId !== contractAddress) {\n return null;\n }\n\n // Look for ft_transfer event\n const transferEvent = result.events.find(\n (e) => e.eventType === \"fungible_token_asset\" && e.asset?.assetEventType === \"transfer\",\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: contractId,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n\n // Fallback: extract from function args if events not available\n if (result.contractCall.functionArgs.length >= 3) {\n const amountArg = result.contractCall.functionArgs[0];\n const senderArg = result.contractCall.functionArgs[1];\n const recipientArg = result.contractCall.functionArgs[2];\n\n // Parse principal from repr (format: 'SP...')\n const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);\n const amountMatch = amountArg?.repr?.match(/^u(\\d+)$/);\n\n if (senderMatch && recipientMatch && amountMatch) {\n return {\n contractAddress: contractId,\n from: senderMatch[1],\n to: recipientMatch[1],\n amount: amountMatch[1],\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract token transfer from post conditions (alternative method)\n */\nexport function extractTokenTransferFromPostConditions(\n result: StacksTransactionResult,\n contractAddress?: string,\n): ParsedTokenTransfer | null {\n if (result.txStatus !== \"success\") {\n return null;\n }\n\n // Look for fungible token post conditions\n for (const pc of result.postConditions) {\n if (pc.asset) {\n const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;\n\n if (contractAddress && assetContractAddress !== contractAddress) {\n continue;\n }\n\n // Find corresponding ft_transfer event for recipient\n const transferEvent = result.events.find(\n (e) =>\n e.eventType === \"fungible_token_asset\" &&\n e.asset?.assetEventType === \"transfer\" &&\n e.asset?.sender === pc.principal.address,\n );\n\n if (transferEvent?.asset) {\n return {\n contractAddress: assetContractAddress,\n from: transferEvent.asset.sender,\n to: transferEvent.asset.recipient,\n amount: transferEvent.asset.amount,\n success: true,\n };\n }\n }\n }\n\n return null;\n}\n"],"mappings":"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","names":[]}