@t402/stacks 2.4.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 (40) hide show
  1. package/README.md +178 -0
  2. package/dist/exact-direct/client/index.cjs +167 -0
  3. package/dist/exact-direct/client/index.cjs.map +1 -0
  4. package/dist/exact-direct/client/index.d.cts +39 -0
  5. package/dist/exact-direct/client/index.d.ts +39 -0
  6. package/dist/exact-direct/client/index.mjs +139 -0
  7. package/dist/exact-direct/client/index.mjs.map +1 -0
  8. package/dist/exact-direct/facilitator/index.cjs +395 -0
  9. package/dist/exact-direct/facilitator/index.cjs.map +1 -0
  10. package/dist/exact-direct/facilitator/index.d.cts +55 -0
  11. package/dist/exact-direct/facilitator/index.d.ts +55 -0
  12. package/dist/exact-direct/facilitator/index.mjs +367 -0
  13. package/dist/exact-direct/facilitator/index.mjs.map +1 -0
  14. package/dist/exact-direct/server/index.cjs +247 -0
  15. package/dist/exact-direct/server/index.cjs.map +1 -0
  16. package/dist/exact-direct/server/index.d.cts +109 -0
  17. package/dist/exact-direct/server/index.d.ts +109 -0
  18. package/dist/exact-direct/server/index.mjs +218 -0
  19. package/dist/exact-direct/server/index.mjs.map +1 -0
  20. package/dist/index.cjs +261 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +126 -0
  23. package/dist/index.d.ts +126 -0
  24. package/dist/index.mjs +212 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/types-Bxzo3eQ1.d.cts +172 -0
  27. package/dist/types-Bxzo3eQ1.d.ts +172 -0
  28. package/package.json +102 -0
  29. package/src/constants.ts +66 -0
  30. package/src/exact-direct/client/index.ts +5 -0
  31. package/src/exact-direct/client/scheme.ts +115 -0
  32. package/src/exact-direct/facilitator/index.ts +4 -0
  33. package/src/exact-direct/facilitator/scheme.ts +308 -0
  34. package/src/exact-direct/server/index.ts +9 -0
  35. package/src/exact-direct/server/register.ts +57 -0
  36. package/src/exact-direct/server/scheme.ts +216 -0
  37. package/src/index.ts +78 -0
  38. package/src/tokens.ts +96 -0
  39. package/src/types.ts +184 -0
  40. package/src/utils.ts +198 -0
package/src/utils.ts ADDED
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Stacks Utility Functions
3
+ */
4
+
5
+ import type { StacksTransactionResult, ParsedTokenTransfer } from "./types.js";
6
+
7
+ /**
8
+ * Validate a Stacks principal address format
9
+ * Stacks addresses start with SP (mainnet) or ST (testnet)
10
+ * followed by alphanumeric characters (base58-like encoding)
11
+ */
12
+ export function isValidPrincipal(address: string): boolean {
13
+ if (!address || typeof address !== "string") {
14
+ return false;
15
+ }
16
+
17
+ // Standard principal: SP/ST prefix + base58 characters (33-41 chars total)
18
+ // Contract principal: standard-principal.contract-name
19
+ const parts = address.split(".");
20
+ const principal = parts[0];
21
+
22
+ // Check principal format: SP or ST prefix + alphanumeric (base58 chars)
23
+ const principalRegex = /^(SP|ST)[0-9A-HJ-NP-Za-km-z]{33,41}$/;
24
+ if (!principalRegex.test(principal)) {
25
+ return false;
26
+ }
27
+
28
+ // If it's a contract principal, validate contract name
29
+ if (parts.length === 2) {
30
+ const contractName = parts[1];
31
+ // Contract names: 1-128 chars, alphanumeric + hyphen + underscore
32
+ const contractNameRegex = /^[a-zA-Z][a-zA-Z0-9\-_]{0,127}$/;
33
+ return contractNameRegex.test(contractName);
34
+ }
35
+
36
+ // Standard principal (no contract part) or exactly one dot for contract
37
+ return parts.length === 1;
38
+ }
39
+
40
+ /**
41
+ * Validate a Stacks transaction ID format
42
+ * Transaction IDs are 0x-prefixed 64-character hex strings
43
+ */
44
+ export function isValidTxId(hash: string): boolean {
45
+ if (!hash || typeof hash !== "string") {
46
+ return false;
47
+ }
48
+ return /^0x[a-fA-F0-9]{64}$/.test(hash);
49
+ }
50
+
51
+ /**
52
+ * Compare two Stacks principals (case-sensitive)
53
+ */
54
+ export function comparePrincipals(a: string, b: string): boolean {
55
+ return a === b;
56
+ }
57
+
58
+ /**
59
+ * Format an amount with decimals for display
60
+ */
61
+ export function formatAmount(amount: string, decimals: number): string {
62
+ const amountBigInt = BigInt(amount);
63
+ const divisor = BigInt(10 ** decimals);
64
+ const wholePart = amountBigInt / divisor;
65
+ const fractionalPart = amountBigInt % divisor;
66
+
67
+ if (fractionalPart === 0n) {
68
+ return wholePart.toString();
69
+ }
70
+
71
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
72
+ const trimmedFractional = fractionalStr.replace(/0+$/, "");
73
+ return `${wholePart}.${trimmedFractional}`;
74
+ }
75
+
76
+ /**
77
+ * Parse an amount string to the smallest unit (with decimals applied)
78
+ */
79
+ export function parseAmount(amount: string, decimals: number): string {
80
+ const parts = amount.split(".");
81
+ const wholePart = parts[0] || "0";
82
+ const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
83
+ return BigInt(wholePart + fractionalPart).toString();
84
+ }
85
+
86
+ /**
87
+ * Extract token transfer details from a Stacks transaction result
88
+ * Looks for ft_transfer events matching the expected contract
89
+ */
90
+ export function extractTokenTransfer(
91
+ result: StacksTransactionResult,
92
+ contractAddress?: string,
93
+ ): ParsedTokenTransfer | null {
94
+ if (result.txStatus !== "success") {
95
+ return null;
96
+ }
97
+
98
+ // Check if this is a contract-call transaction
99
+ if (result.txType !== "contract_call") {
100
+ return null;
101
+ }
102
+
103
+ // Check contract call is a transfer function
104
+ if (result.contractCall) {
105
+ const { contractId, functionName } = result.contractCall;
106
+
107
+ if (functionName !== "transfer") {
108
+ return null;
109
+ }
110
+
111
+ // If contractAddress specified, verify it matches
112
+ if (contractAddress && contractId !== contractAddress) {
113
+ return null;
114
+ }
115
+
116
+ // Look for ft_transfer event
117
+ const transferEvent = result.events.find(
118
+ (e) => e.eventType === "fungible_token_asset" && e.asset?.assetEventType === "transfer",
119
+ );
120
+
121
+ if (transferEvent?.asset) {
122
+ return {
123
+ contractAddress: contractId,
124
+ from: transferEvent.asset.sender,
125
+ to: transferEvent.asset.recipient,
126
+ amount: transferEvent.asset.amount,
127
+ success: true,
128
+ };
129
+ }
130
+
131
+ // Fallback: extract from function args if events not available
132
+ if (result.contractCall.functionArgs.length >= 3) {
133
+ const amountArg = result.contractCall.functionArgs[0];
134
+ const senderArg = result.contractCall.functionArgs[1];
135
+ const recipientArg = result.contractCall.functionArgs[2];
136
+
137
+ // Parse principal from repr (format: 'SP...')
138
+ const senderMatch = senderArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);
139
+ const recipientMatch = recipientArg?.repr?.match(/^'?(S[PT][0-9A-HJ-NP-Za-km-z]+)/);
140
+ const amountMatch = amountArg?.repr?.match(/^u(\d+)$/);
141
+
142
+ if (senderMatch && recipientMatch && amountMatch) {
143
+ return {
144
+ contractAddress: contractId,
145
+ from: senderMatch[1],
146
+ to: recipientMatch[1],
147
+ amount: amountMatch[1],
148
+ success: true,
149
+ };
150
+ }
151
+ }
152
+ }
153
+
154
+ return null;
155
+ }
156
+
157
+ /**
158
+ * Extract token transfer from post conditions (alternative method)
159
+ */
160
+ export function extractTokenTransferFromPostConditions(
161
+ result: StacksTransactionResult,
162
+ contractAddress?: string,
163
+ ): ParsedTokenTransfer | null {
164
+ if (result.txStatus !== "success") {
165
+ return null;
166
+ }
167
+
168
+ // Look for fungible token post conditions
169
+ for (const pc of result.postConditions) {
170
+ if (pc.asset) {
171
+ const assetContractAddress = `${pc.asset.contractAddress}.${pc.asset.contractName}`;
172
+
173
+ if (contractAddress && assetContractAddress !== contractAddress) {
174
+ continue;
175
+ }
176
+
177
+ // Find corresponding ft_transfer event for recipient
178
+ const transferEvent = result.events.find(
179
+ (e) =>
180
+ e.eventType === "fungible_token_asset" &&
181
+ e.asset?.assetEventType === "transfer" &&
182
+ e.asset?.sender === pc.principal.address,
183
+ );
184
+
185
+ if (transferEvent?.asset) {
186
+ return {
187
+ contractAddress: assetContractAddress,
188
+ from: transferEvent.asset.sender,
189
+ to: transferEvent.asset.recipient,
190
+ amount: transferEvent.asset.amount,
191
+ success: true,
192
+ };
193
+ }
194
+ }
195
+ }
196
+
197
+ return null;
198
+ }