@openzeppelin/adapter-stellar 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +272 -0
  2. package/dist/config.cjs +21 -0
  3. package/dist/config.cjs.map +1 -0
  4. package/dist/config.d.cts +8 -0
  5. package/dist/config.d.cts.map +1 -0
  6. package/dist/config.d.mts +8 -0
  7. package/dist/config.d.mts.map +1 -0
  8. package/dist/config.mjs +20 -0
  9. package/dist/config.mjs.map +1 -0
  10. package/dist/index.cjs +7564 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +261 -0
  13. package/dist/index.d.cts.map +1 -0
  14. package/dist/index.d.mts +263 -0
  15. package/dist/index.d.mts.map +1 -0
  16. package/dist/index.mjs +7529 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/dist/metadata.cjs +22 -0
  19. package/dist/metadata.cjs.map +1 -0
  20. package/dist/metadata.d.cts +7 -0
  21. package/dist/metadata.d.cts.map +1 -0
  22. package/dist/metadata.d.mts +7 -0
  23. package/dist/metadata.d.mts.map +1 -0
  24. package/dist/metadata.mjs +21 -0
  25. package/dist/metadata.mjs.map +1 -0
  26. package/dist/networks-BrV516-R.d.cts +15 -0
  27. package/dist/networks-BrV516-R.d.cts.map +1 -0
  28. package/dist/networks-C0MmhJcu.d.mts +15 -0
  29. package/dist/networks-C0MmhJcu.d.mts.map +1 -0
  30. package/dist/networks-DgUFSTiC.cjs +76 -0
  31. package/dist/networks-DgUFSTiC.cjs.map +1 -0
  32. package/dist/networks-QbEPbaGT.mjs +46 -0
  33. package/dist/networks-QbEPbaGT.mjs.map +1 -0
  34. package/dist/networks.cjs +8 -0
  35. package/dist/networks.d.cts +2 -0
  36. package/dist/networks.d.mts +2 -0
  37. package/dist/networks.mjs +3 -0
  38. package/dist/vite-config.cjs +43 -0
  39. package/dist/vite-config.cjs.map +1 -0
  40. package/dist/vite-config.d.cts +35 -0
  41. package/dist/vite-config.d.cts.map +1 -0
  42. package/dist/vite-config.d.mts +35 -0
  43. package/dist/vite-config.d.mts.map +1 -0
  44. package/dist/vite-config.mjs +42 -0
  45. package/dist/vite-config.mjs.map +1 -0
  46. package/package.json +114 -0
  47. package/src/__tests__/getDefaultServiceConfig.test.ts +105 -0
  48. package/src/access-control/actions.ts +214 -0
  49. package/src/access-control/feature-detection.ts +238 -0
  50. package/src/access-control/index.ts +54 -0
  51. package/src/access-control/indexer-client.ts +1474 -0
  52. package/src/access-control/onchain-reader.ts +446 -0
  53. package/src/access-control/service.ts +1431 -0
  54. package/src/access-control/validation.ts +256 -0
  55. package/src/adapter.ts +659 -0
  56. package/src/config.ts +43 -0
  57. package/src/configuration/__tests__/explorer.test.ts +80 -0
  58. package/src/configuration/__tests__/rpc.test.ts +355 -0
  59. package/src/configuration/execution.ts +83 -0
  60. package/src/configuration/explorer.ts +105 -0
  61. package/src/configuration/index.ts +5 -0
  62. package/src/configuration/network-services.ts +210 -0
  63. package/src/configuration/rpc.ts +270 -0
  64. package/src/configuration.ts +2 -0
  65. package/src/contract/__tests__/complete-type-coverage.test.ts +78 -0
  66. package/src/contract/index.ts +3 -0
  67. package/src/contract/loader.ts +498 -0
  68. package/src/contract/transformer.ts +1 -0
  69. package/src/contract/type.ts +65 -0
  70. package/src/index.ts +23 -0
  71. package/src/mapping/constants.ts +89 -0
  72. package/src/mapping/enum-metadata.ts +237 -0
  73. package/src/mapping/field-generator.ts +296 -0
  74. package/src/mapping/index.ts +5 -0
  75. package/src/mapping/struct-fields.ts +106 -0
  76. package/src/mapping/tuple-components.ts +43 -0
  77. package/src/mapping/type-coverage-validator.ts +151 -0
  78. package/src/mapping/type-mapper.ts +203 -0
  79. package/src/metadata.ts +16 -0
  80. package/src/networks/README.md +84 -0
  81. package/src/networks/index.ts +19 -0
  82. package/src/networks/mainnet.ts +20 -0
  83. package/src/networks/testnet.ts +20 -0
  84. package/src/networks.ts +2 -0
  85. package/src/query/handler.ts +411 -0
  86. package/src/query/index.ts +4 -0
  87. package/src/query/view-checker.ts +32 -0
  88. package/src/sac/spec-cache.ts +68 -0
  89. package/src/sac/spec-source.ts +35 -0
  90. package/src/sac/xdr.ts +101 -0
  91. package/src/transaction/components/AdvancedInfo.tsx +34 -0
  92. package/src/transaction/components/FeeConfiguration.tsx +41 -0
  93. package/src/transaction/components/StellarRelayerOptions.tsx +60 -0
  94. package/src/transaction/components/TransactionTiming.tsx +77 -0
  95. package/src/transaction/components/index.ts +5 -0
  96. package/src/transaction/components/useStellarRelayerOptions.ts +114 -0
  97. package/src/transaction/eoa.ts +229 -0
  98. package/src/transaction/execution-strategy.ts +33 -0
  99. package/src/transaction/formatter.ts +296 -0
  100. package/src/transaction/index.ts +4 -0
  101. package/src/transaction/relayer.ts +575 -0
  102. package/src/transaction/sender.ts +156 -0
  103. package/src/transform/index.ts +4 -0
  104. package/src/transform/input-parser.ts +9 -0
  105. package/src/transform/output-formatter.ts +133 -0
  106. package/src/transform/parsers/complex-parser.ts +157 -0
  107. package/src/transform/parsers/generic-parser.ts +171 -0
  108. package/src/transform/parsers/index.ts +86 -0
  109. package/src/transform/parsers/primitive-parser.ts +123 -0
  110. package/src/transform/parsers/scval-converter.ts +405 -0
  111. package/src/transform/parsers/struct-parser.ts +324 -0
  112. package/src/transform/parsers/types.ts +35 -0
  113. package/src/types/__tests__/artifacts.test.ts +89 -0
  114. package/src/types/artifacts.ts +19 -0
  115. package/src/utils/__tests__/artifacts.test.ts +77 -0
  116. package/src/utils/artifacts.ts +30 -0
  117. package/src/utils/formatting.ts +122 -0
  118. package/src/utils/index.ts +6 -0
  119. package/src/utils/input-parsing.ts +336 -0
  120. package/src/utils/safe-type-parser.ts +303 -0
  121. package/src/utils/stellar-types.ts +35 -0
  122. package/src/utils/type-detection.ts +163 -0
  123. package/src/utils/xdr-ordering.ts +36 -0
  124. package/src/validation/__tests__/address.test.ts +267 -0
  125. package/src/validation/address.ts +136 -0
  126. package/src/validation/eoa.ts +33 -0
  127. package/src/validation/index.ts +3 -0
  128. package/src/validation/relayer.ts +13 -0
  129. package/src/vite-config.ts +67 -0
  130. package/src/wallet/README.md +93 -0
  131. package/src/wallet/__tests__/connection.test.ts +72 -0
  132. package/src/wallet/components/StellarWalletUiRoot.tsx +161 -0
  133. package/src/wallet/components/account/AccountDisplay.tsx +50 -0
  134. package/src/wallet/components/connect/ConnectButton.tsx +100 -0
  135. package/src/wallet/components/connect/ConnectorDialog.tsx +125 -0
  136. package/src/wallet/components/index.ts +3 -0
  137. package/src/wallet/connection.ts +151 -0
  138. package/src/wallet/context/StellarWalletContext.ts +32 -0
  139. package/src/wallet/context/index.ts +4 -0
  140. package/src/wallet/context/useStellarWalletContext.ts +17 -0
  141. package/src/wallet/hooks/facade-hooks.ts +31 -0
  142. package/src/wallet/hooks/index.ts +7 -0
  143. package/src/wallet/hooks/useStellarAccount.ts +27 -0
  144. package/src/wallet/hooks/useStellarConnect.ts +60 -0
  145. package/src/wallet/hooks/useStellarDisconnect.ts +47 -0
  146. package/src/wallet/hooks/useUiKitConfig.ts +40 -0
  147. package/src/wallet/implementation/wallets-kit-implementation.ts +379 -0
  148. package/src/wallet/index.ts +11 -0
  149. package/src/wallet/services/__tests__/configResolutionService.test.ts +163 -0
  150. package/src/wallet/services/configResolutionService.ts +65 -0
  151. package/src/wallet/stellar-wallets-kit/StellarWalletsKitConnectButton.tsx +82 -0
  152. package/src/wallet/stellar-wallets-kit/__mocks__/@creit.tech/stellar-wallets-kit.ts +48 -0
  153. package/src/wallet/stellar-wallets-kit/__tests__/export-service.test.ts +93 -0
  154. package/src/wallet/stellar-wallets-kit/__tests__/stellarUiKitManager.test.ts +0 -0
  155. package/src/wallet/stellar-wallets-kit/config-generator.ts +75 -0
  156. package/src/wallet/stellar-wallets-kit/export-service.ts +19 -0
  157. package/src/wallet/stellar-wallets-kit/index.ts +3 -0
  158. package/src/wallet/stellar-wallets-kit/stellarUiKitManager.ts +235 -0
  159. package/src/wallet/types.ts +19 -0
  160. package/src/wallet/utils/__tests__/filterWalletComponents.test.ts +150 -0
  161. package/src/wallet/utils/__tests__/uiKitService.test.ts +189 -0
  162. package/src/wallet/utils/filterWalletComponents.ts +89 -0
  163. package/src/wallet/utils/index.ts +3 -0
  164. package/src/wallet/utils/stellarWalletImplementationManager.ts +118 -0
  165. package/src/wallet/utils/uiKitService.ts +74 -0
@@ -0,0 +1,324 @@
1
+ import { nativeToScVal, xdr } from '@stellar/stellar-sdk';
2
+
3
+ import { isEnumValue, type FunctionParameter } from '@openzeppelin/ui-types';
4
+ import { isPlainObject, logger } from '@openzeppelin/ui-utils';
5
+
6
+ import { convertStellarTypeToScValType } from '../../utils/formatting';
7
+ import { isLikelyEnumType } from '../../utils/type-detection';
8
+ import { compareScValsByXdr } from '../../utils/xdr-ordering';
9
+ import { parseGenericType } from './generic-parser';
10
+
11
+ const SYSTEM_LOG_TAG = 'StructParser';
12
+
13
+ function isTupleStructSchema(schema: FunctionParameter | undefined): boolean {
14
+ if (!schema?.components || schema.components.length === 0) {
15
+ return false;
16
+ }
17
+
18
+ return schema.components.every((component, index) => component.name === index.toString());
19
+ }
20
+
21
+ /**
22
+ * Determines if a field value needs parsing through parseStellarInput.
23
+ * Returns false for already-processed values (like Uint8Array for bytes).
24
+ */
25
+ function needsParsing(value: unknown, fieldType: string): boolean {
26
+ // Skip parsing for already-processed Uint8Array values for bytes types
27
+ if ((fieldType === 'Bytes' || fieldType.startsWith('BytesN<')) && value instanceof Uint8Array) {
28
+ return false;
29
+ }
30
+
31
+ // For Vec types, check if it's already an array of properly typed values
32
+ if (fieldType.startsWith('Vec<')) {
33
+ if (!Array.isArray(value)) {
34
+ return true; // Needs parsing if not an array
35
+ }
36
+
37
+ // Extract inner type to check array elements
38
+ const innerTypeMatch = fieldType.match(/Vec<(.+)>$/);
39
+ if (innerTypeMatch) {
40
+ // If all elements are already properly typed, skip parsing
41
+ // For now, let's always parse Vec types to ensure validation
42
+ return true;
43
+ }
44
+ }
45
+
46
+ // For Map types, always parse to convert UI MapEntry format to Stellar SDK format
47
+ if (fieldType.startsWith('Map<')) {
48
+ return true; // Map data needs conversion from UI format to Stellar SDK format
49
+ }
50
+
51
+ // For primitive types, parse if value is string (form input)
52
+ if (typeof value === 'string') {
53
+ return true;
54
+ }
55
+
56
+ // For complex objects, they might need recursive parsing
57
+ if (isPlainObject(value)) {
58
+ return true;
59
+ }
60
+
61
+ // For other cases, skip parsing (assume already processed)
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Converts a struct object to ScVal using Laboratory pattern with schema-based type resolution.
67
+ *
68
+ * @param structObj - The plain object representing the struct
69
+ * @param parameterType - The Stellar parameter type (for error messages)
70
+ * @param paramSchema - Parameter schema with struct field definitions (required)
71
+ * @param parseInnerValue - Function to recursively parse inner values
72
+ * @param convertToScVal - Optional function to recursively convert values to ScVal (for tuple structs)
73
+ * @returns ScVal ready for contract calls
74
+ * @throws Error if schema information is missing for any field
75
+ */
76
+ export function convertStructToScVal(
77
+ structObj: Record<string, unknown>,
78
+ parameterType: string,
79
+ paramSchema: FunctionParameter | undefined,
80
+ parseInnerValue: (val: unknown, type: string) => unknown,
81
+ convertToScVal?: (
82
+ value: unknown,
83
+ type: string,
84
+ schema?: FunctionParameter,
85
+ parseValue?: (val: unknown, type: string) => unknown
86
+ ) => xdr.ScVal
87
+ ): xdr.ScVal {
88
+ // Laboratory-style struct conversion with proper type hint handling
89
+
90
+ // Check if this is a tuple struct (numeric field names like "0", "1", "2")
91
+ // Tuple structs in Soroban need to be serialized as vectors, not maps
92
+ if (isTupleStructSchema(paramSchema) && paramSchema && convertToScVal) {
93
+ const tupleValues = paramSchema.components!.map((component, index) => {
94
+ const key = component.name ?? index.toString();
95
+ let elementValue = structObj[key];
96
+
97
+ if (typeof elementValue === 'undefined') {
98
+ throw new Error(
99
+ `Missing tuple value for "${key}" in struct type "${parameterType}". Received: ${JSON.stringify(structObj)}`
100
+ );
101
+ }
102
+
103
+ // If the element is a string and its type is likely an enum, wrap it as { tag: value }
104
+ if (typeof elementValue === 'string' && isLikelyEnumType(component.type)) {
105
+ elementValue = { tag: elementValue };
106
+ }
107
+
108
+ return convertToScVal(elementValue, component.type, component, parseInnerValue);
109
+ });
110
+
111
+ return xdr.ScVal.scvVec(tupleValues);
112
+ }
113
+
114
+ // Struct conversion using Laboratory pattern with schema-based type resolution
115
+ // See: laboratory/src/helpers/sorobanUtils.ts convertObjectToScVal
116
+ const convertedValue: Record<string, unknown> = {};
117
+ const typeHints: Record<string, unknown> = {};
118
+
119
+ // For structs with Vec/Map fields, ensure proper parsing but use nativeToScVal approach
120
+
121
+ // Laboratory pattern: preserve raw values, provide type hints as arrays
122
+ // Type hints format: [keyType, valueType] where keyType is always "symbol"
123
+ for (const [fieldName, fieldValue] of Object.entries(structObj)) {
124
+ // Use schema information for accurate type mapping
125
+ let fieldType: string | undefined;
126
+ if (paramSchema?.components) {
127
+ const fieldSchema = paramSchema.components.find(
128
+ (comp: FunctionParameter) => comp.name === fieldName
129
+ );
130
+ fieldType = fieldSchema?.type;
131
+ }
132
+
133
+ if (fieldType) {
134
+ // Parse the field value using the correct type to ensure validation and conversion
135
+ // Only parse if the value appears to be raw form input (strings/primitives)
136
+ // Skip parsing for already-processed values (like Uint8Array for bytes)
137
+ let parsedValue: unknown;
138
+
139
+ if (needsParsing(fieldValue, fieldType)) {
140
+ parsedValue = parseInnerValue(fieldValue, fieldType);
141
+ } else {
142
+ parsedValue = fieldValue;
143
+ }
144
+
145
+ if (typeof parsedValue === 'string' && isLikelyEnumType(fieldType)) {
146
+ parsedValue = { tag: parsedValue };
147
+ }
148
+
149
+ // Handle Map fields specially - convert from SDK format to plain object with type hints
150
+ if (fieldType.startsWith('Map<') && Array.isArray(parsedValue)) {
151
+ // Extract key and value types
152
+ const mapTypeMatch = fieldType.match(/Map<(.+),\s*(.+)>$/);
153
+ const mapKeyType = mapTypeMatch ? mapTypeMatch[1] : 'ScSymbol';
154
+ const mapValueType = mapTypeMatch ? mapTypeMatch[2] : 'Bytes';
155
+
156
+ // parsedValue is in Stellar SDK format: [{0: {value, type}, 1: {value, type}}, ...]
157
+ // Convert to plain object for nativeToScVal
158
+ const mapObject: Record<string, unknown> = {};
159
+ const mapTypeHints: Record<string, string[]> = {};
160
+
161
+ (
162
+ parsedValue as Array<{
163
+ 0: { value: unknown; type: string };
164
+ 1: { value: unknown; type: string };
165
+ }>
166
+ ).forEach((entry) => {
167
+ // Parse the key and value
168
+ const processedKey = parseInnerValue(entry[0].value, entry[0].type || mapKeyType);
169
+ const processedVal = parseInnerValue(entry[1].value, entry[1].type || mapValueType);
170
+
171
+ const keyString = typeof processedKey === 'string' ? processedKey : String(processedKey);
172
+ mapObject[keyString] = processedVal;
173
+
174
+ // Set type hints for each entry using the actual types
175
+ const keyScValType = convertStellarTypeToScValType(entry[0].type || mapKeyType);
176
+ const valueScValType = convertStellarTypeToScValType(entry[1].type || mapValueType);
177
+
178
+ // Laboratory pattern: [keyType, valueType] for each map entry
179
+ mapTypeHints[keyString] = [
180
+ Array.isArray(keyScValType)
181
+ ? keyScValType[0]
182
+ : keyScValType === 'map-special'
183
+ ? 'symbol'
184
+ : keyScValType,
185
+ Array.isArray(valueScValType)
186
+ ? valueScValType[0]
187
+ : valueScValType === 'map-special'
188
+ ? 'bytes'
189
+ : valueScValType,
190
+ ];
191
+ });
192
+
193
+ convertedValue[fieldName] = mapObject;
194
+ // Provide nested type hints for Map field (Laboratory pattern)
195
+ typeHints[fieldName] = ['symbol', mapTypeHints];
196
+ } else {
197
+ // Check if this is a Vec or other generic type that needs special handling
198
+ if (convertToScVal && (fieldType.startsWith('Vec<') || isEnumValue(parsedValue))) {
199
+ const fieldSchema = paramSchema?.components?.find((c) => c.name === fieldName);
200
+ // Use valueToScVal for Vec and enum fields to ensure proper conversion
201
+ convertedValue[fieldName] = convertToScVal(
202
+ parsedValue,
203
+ fieldType,
204
+ fieldSchema,
205
+ parseInnerValue
206
+ );
207
+ typeHints[fieldName] = ['symbol', 'scval'];
208
+ } else {
209
+ convertedValue[fieldName] = parsedValue;
210
+
211
+ // Use exact type from schema for non-Map fields
212
+ const scValType = convertStellarTypeToScValType(fieldType);
213
+ if (scValType !== 'map-special') {
214
+ typeHints[fieldName] = ['symbol', Array.isArray(scValType) ? scValType[0] : scValType];
215
+ }
216
+ }
217
+ }
218
+ } else {
219
+ // Schema is required for struct field type resolution
220
+ throw new Error(
221
+ `Missing schema information for struct field "${fieldName}" in struct type "${parameterType}". ` +
222
+ `Schema-based type resolution is required for accurate ScVal conversion.`
223
+ );
224
+ }
225
+ }
226
+
227
+ logger.debug(SYSTEM_LOG_TAG, 'convertStructToScVal final values:', {
228
+ parameterType,
229
+ convertedValue,
230
+ typeHints,
231
+ });
232
+
233
+ // Check if any fields are enums that need special conversion
234
+ const hasEnumFields = paramSchema?.components?.some((comp) => {
235
+ const fieldValue = convertedValue[comp.name];
236
+ return isEnumValue(fieldValue);
237
+ });
238
+
239
+ let scVal: xdr.ScVal;
240
+
241
+ if (hasEnumFields && convertToScVal && paramSchema?.components) {
242
+ // Manually build the ScMap with proper enum conversion
243
+ const mapEntries: xdr.ScMapEntry[] = [];
244
+
245
+ for (const fieldSchema of paramSchema.components) {
246
+ const fieldName = fieldSchema.name;
247
+ const fieldValue = convertedValue[fieldName];
248
+
249
+ // Create key
250
+ const keyScVal = nativeToScVal(fieldName, { type: 'symbol' });
251
+
252
+ // Create value - check if it's an enum
253
+ let valueScVal: xdr.ScVal;
254
+ if (isEnumValue(fieldValue)) {
255
+ valueScVal = convertToScVal(fieldValue, fieldSchema.type, fieldSchema, parseInnerValue);
256
+ } else {
257
+ // Use nativeToScVal for non-enum fields
258
+ const fieldTypeHint = typeHints[fieldName];
259
+ if (fieldTypeHint && Array.isArray(fieldTypeHint) && fieldTypeHint.length > 1) {
260
+ valueScVal = nativeToScVal(fieldValue, { type: fieldTypeHint[1] });
261
+ } else {
262
+ valueScVal = nativeToScVal(fieldValue);
263
+ }
264
+ }
265
+
266
+ mapEntries.push(
267
+ new xdr.ScMapEntry({
268
+ key: keyScVal,
269
+ val: valueScVal,
270
+ })
271
+ );
272
+ }
273
+
274
+ // Sort map entries by XDR-encoded keys (required by Soroban)
275
+ const sortedMapEntries = mapEntries.sort((a, b) => compareScValsByXdr(a.key(), b.key()));
276
+
277
+ scVal = xdr.ScVal.scvMap(sortedMapEntries);
278
+ } else {
279
+ scVal = nativeToScVal(convertedValue, { type: typeHints });
280
+ }
281
+
282
+ logger.debug(SYSTEM_LOG_TAG, 'convertStructToScVal generated ScVal:', {
283
+ parameterType,
284
+ scValType: scVal.switch().name,
285
+ scValValue: scVal.value(),
286
+ });
287
+
288
+ return scVal;
289
+ }
290
+
291
+ /**
292
+ * Checks if the given value and type represent a struct that can be handled by this parser.
293
+ *
294
+ * @param value - The value to check
295
+ * @param parameterType - The Stellar parameter type
296
+ * @returns True if this is a struct type
297
+ */
298
+ export function isStructType(value: unknown, parameterType: string): boolean {
299
+ // Check if this is a struct type (object that doesn't match enum pattern or generic pattern)
300
+ // Exclude special object types like Uint8Array, Date, etc.
301
+ if (!isPlainObject(value)) {
302
+ return false;
303
+ }
304
+
305
+ // Skip if it's a generic type
306
+ const genericInfo = parseGenericType(parameterType);
307
+ if (genericInfo) {
308
+ return false;
309
+ }
310
+
311
+ // Skip if it looks like an enum (has 'tag', 'enum', or 'values' properties)
312
+ const obj = value as Record<string, unknown>;
313
+ if ('tag' in obj || 'enum' in obj || 'values' in obj) {
314
+ return false;
315
+ }
316
+
317
+ // Check if it's a plain object with simple constructor
318
+ return (
319
+ !(value instanceof Uint8Array) &&
320
+ !(value instanceof Date) &&
321
+ typeof value.constructor === 'function' &&
322
+ value.constructor === Object
323
+ );
324
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Type definitions for Stellar/Soroban contract argument parsing.
3
+ * These types support both simple form inputs and complex structures.
4
+ */
5
+
6
+ /**
7
+ * Enhanced argument structure for complex Soroban contract calls.
8
+ * Supports both simple form inputs and complex structures.
9
+ */
10
+ export interface SorobanArgumentValue {
11
+ value: string | number | boolean | SorobanArgumentValue | SorobanArgumentValue[];
12
+ type: string;
13
+ }
14
+
15
+ export interface SorobanEnumValue {
16
+ tag?: string;
17
+ values?: SorobanArgumentValue[];
18
+ enum?: string | number;
19
+ }
20
+
21
+ export interface SorobanMapEntry {
22
+ '0': SorobanArgumentValue; // key
23
+ '1': SorobanArgumentValue; // value
24
+ }
25
+
26
+ export type SorobanComplexValue =
27
+ | SorobanArgumentValue
28
+ | SorobanArgumentValue[]
29
+ | SorobanArgumentValue[][]
30
+ | SorobanEnumValue
31
+ | SorobanMapEntry[]
32
+ | SorobanMapEntry[][]
33
+ | SorobanMapEntry
34
+ | Record<string, SorobanArgumentValue>
35
+ | Record<string, SorobanArgumentValue>[];
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Unit tests for Stellar contract artifacts types and validation
3
+ */
4
+ import { describe, expect, it } from 'vitest';
5
+
6
+ import { isStellarContractArtifacts, type StellarContractArtifacts } from '../artifacts';
7
+
8
+ describe('Stellar Contract Artifacts', () => {
9
+ describe('isStellarContractArtifacts', () => {
10
+ it('should return true for valid artifacts with contractAddress', () => {
11
+ const artifacts: StellarContractArtifacts = {
12
+ contractAddress: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7',
13
+ };
14
+
15
+ const result = isStellarContractArtifacts(artifacts);
16
+
17
+ expect(result).toBe(true);
18
+ });
19
+
20
+ it('should return false for null', () => {
21
+ const result = isStellarContractArtifacts(null);
22
+ expect(result).toBe(false);
23
+ });
24
+
25
+ it('should return false for undefined', () => {
26
+ const result = isStellarContractArtifacts(undefined);
27
+ expect(result).toBe(false);
28
+ });
29
+
30
+ it('should return false for non-object types', () => {
31
+ expect(isStellarContractArtifacts('string')).toBe(false);
32
+ expect(isStellarContractArtifacts(123)).toBe(false);
33
+ expect(isStellarContractArtifacts(true)).toBe(false);
34
+ expect(isStellarContractArtifacts([])).toBe(false);
35
+ });
36
+
37
+ it('should return false for object without contractAddress', () => {
38
+ const artifacts = {
39
+ someOtherProperty: 'value',
40
+ };
41
+
42
+ const result = isStellarContractArtifacts(artifacts);
43
+
44
+ expect(result).toBe(false);
45
+ });
46
+
47
+ it('should return false for object with non-string contractAddress', () => {
48
+ const artifacts = {
49
+ contractAddress: 123,
50
+ };
51
+
52
+ const result = isStellarContractArtifacts(artifacts);
53
+
54
+ expect(result).toBe(false);
55
+ });
56
+
57
+ it('should return true even with extra properties', () => {
58
+ const artifacts = {
59
+ contractAddress: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7',
60
+ extraProperty: 'should be ignored',
61
+ anotherExtra: 42,
62
+ };
63
+
64
+ const result = isStellarContractArtifacts(artifacts);
65
+
66
+ expect(result).toBe(true);
67
+ });
68
+
69
+ it('should handle empty contractAddress string', () => {
70
+ const artifacts = {
71
+ contractAddress: '',
72
+ };
73
+
74
+ const result = isStellarContractArtifacts(artifacts);
75
+
76
+ expect(result).toBe(true); // Type guard only checks type, not validity
77
+ });
78
+
79
+ it('should handle contract address starting with C', () => {
80
+ const artifacts = {
81
+ contractAddress: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7',
82
+ };
83
+
84
+ const result = isStellarContractArtifacts(artifacts);
85
+
86
+ expect(result).toBe(true);
87
+ });
88
+ });
89
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Stellar-specific contract artifacts interface
3
+ * Defines the structure of data needed to load Stellar contracts
4
+ */
5
+ export interface StellarContractArtifacts {
6
+ /** The deployed contract ID (required, C...) */
7
+ contractAddress: string;
8
+ }
9
+
10
+ /**
11
+ * Type guard to check if an object matches StellarContractArtifacts structure
12
+ */
13
+ export function isStellarContractArtifacts(obj: unknown): obj is StellarContractArtifacts {
14
+ return (
15
+ typeof obj === 'object' &&
16
+ obj !== null &&
17
+ typeof (obj as Record<string, unknown>).contractAddress === 'string'
18
+ );
19
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Unit tests for Stellar contract artifacts utility functions
3
+ */
4
+ import { describe, expect, it } from 'vitest';
5
+
6
+ import { validateAndConvertStellarArtifacts } from '../artifacts';
7
+
8
+ describe('validateAndConvertStellarArtifacts', () => {
9
+ it('should convert string address to artifacts object', () => {
10
+ const address = 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7';
11
+
12
+ const result = validateAndConvertStellarArtifacts(address);
13
+
14
+ expect(result).toEqual({
15
+ contractAddress: address,
16
+ });
17
+ });
18
+
19
+ it('should return valid artifacts object as-is', () => {
20
+ const artifacts = {
21
+ contractAddress: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7',
22
+ };
23
+
24
+ const result = validateAndConvertStellarArtifacts(artifacts);
25
+
26
+ expect(result).toBe(artifacts);
27
+ });
28
+
29
+ it('should throw error for invalid artifacts object', () => {
30
+ const invalidArtifacts = {
31
+ someOtherProperty: 'value',
32
+ };
33
+
34
+ expect(() => validateAndConvertStellarArtifacts(invalidArtifacts)).toThrow(
35
+ 'Invalid contract artifacts provided. Expected an object with contractAddress property.'
36
+ );
37
+ });
38
+
39
+ it('should throw error for artifacts with non-string contractAddress', () => {
40
+ const invalidArtifacts = {
41
+ contractAddress: 123,
42
+ };
43
+
44
+ expect(() => validateAndConvertStellarArtifacts(invalidArtifacts)).toThrow(
45
+ 'Invalid contract artifacts provided. Expected an object with contractAddress property.'
46
+ );
47
+ });
48
+
49
+ it('should accept artifacts with extra properties', () => {
50
+ const artifacts = {
51
+ contractAddress: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7',
52
+ extraProperty: 'should be ignored',
53
+ };
54
+
55
+ const result = validateAndConvertStellarArtifacts(artifacts);
56
+
57
+ expect(result).toEqual(artifacts);
58
+ });
59
+
60
+ it('should handle empty string address', () => {
61
+ const result = validateAndConvertStellarArtifacts('');
62
+
63
+ expect(result).toEqual({
64
+ contractAddress: '',
65
+ });
66
+ });
67
+
68
+ it('should handle contract address starting with C', () => {
69
+ const address = 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQAHHAGCYSG7';
70
+
71
+ const result = validateAndConvertStellarArtifacts(address);
72
+
73
+ expect(result).toEqual({
74
+ contractAddress: address,
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Utility functions for Stellar contract artifacts validation and conversion
3
+ */
4
+ import type { StellarContractArtifacts } from '../types/artifacts';
5
+ import { isStellarContractArtifacts } from '../types/artifacts';
6
+
7
+ /**
8
+ * Validates and converts generic source input to StellarContractArtifacts
9
+ *
10
+ * @param source - Generic contract source (string address or artifacts object)
11
+ * @returns Validated StellarContractArtifacts
12
+ * @throws Error if the source is invalid
13
+ */
14
+ export function validateAndConvertStellarArtifacts(
15
+ source: string | Record<string, unknown>
16
+ ): StellarContractArtifacts {
17
+ if (typeof source === 'string') {
18
+ // If source is a string, assume it's a contract address
19
+ return { contractAddress: source };
20
+ }
21
+
22
+ // Validate that the object has the required structure
23
+ if (!isStellarContractArtifacts(source)) {
24
+ throw new Error(
25
+ 'Invalid contract artifacts provided. Expected an object with contractAddress property.'
26
+ );
27
+ }
28
+
29
+ return source;
30
+ }
@@ -0,0 +1,122 @@
1
+ import {
2
+ extractOptionElementType,
3
+ extractVecElementType,
4
+ isValidTypeString,
5
+ } from './safe-type-parser';
6
+
7
+ /**
8
+ * Custom JSON stringifier that handles BigInt values by converting them to strings.
9
+ * @param value The value to stringify.
10
+ * @param space Adds indentation, white space, and line break characters to the return-value JSON text for readability.
11
+ * @returns A JSON string representing the given value.
12
+ */
13
+ export function stringifyWithBigInt(value: unknown, space?: number | string): string {
14
+ const replacer = (_key: string, val: unknown) => {
15
+ // Check if the value is a BigInt
16
+ if (typeof val === 'bigint') {
17
+ // Convert BigInt to string
18
+ return val.toString();
19
+ }
20
+ // Return the value unchanged for other types
21
+ return val;
22
+ };
23
+
24
+ return JSON.stringify(value, replacer, space);
25
+ }
26
+
27
+ /**
28
+ * Check if a value is a serializable plain object suitable for blockchain output display.
29
+ * Excludes arrays, dates, typed arrays, and objects with custom constructors.
30
+ * @param value The value to check
31
+ * @returns True if the value is a plain object that can be safely serialized
32
+ */
33
+ export function isSerializableObject(value: unknown): boolean {
34
+ return (
35
+ typeof value === 'object' &&
36
+ value !== null &&
37
+ !Array.isArray(value) &&
38
+ !(value instanceof Date) &&
39
+ !(value instanceof Uint8Array) &&
40
+ value.constructor === Object
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Converts Stellar spec types to ScVal type hints.
46
+ * This mapping is used with nativeToScVal to ensure proper type conversion.
47
+ *
48
+ * @param stellarType - The Stellar parameter type (e.g., 'U32', 'Address', 'Bool')
49
+ * @returns The corresponding ScVal type hint (e.g., 'u32', 'address', 'bool')
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * nativeToScVal("0", { type: convertStellarTypeToScValType("U32") }) // → ScU32
54
+ * nativeToScVal("GDQP2...", { type: convertStellarTypeToScValType("Address") }) // → ScAddress
55
+ * ```
56
+ */
57
+ export function convertStellarTypeToScValType(stellarType: string): string | string[] {
58
+ // Input validation
59
+ if (!isValidTypeString(stellarType)) {
60
+ return stellarType.toLowerCase();
61
+ }
62
+
63
+ // Handle Vec types - return array type hint
64
+ if (stellarType.startsWith('Vec<')) {
65
+ const innerType = extractVecElementType(stellarType);
66
+ if (innerType) {
67
+ const innerScValType = convertStellarTypeToScValType(innerType);
68
+ // For Vec types, nativeToScVal expects the inner type as string
69
+ return Array.isArray(innerScValType) ? innerScValType[0] : innerScValType;
70
+ }
71
+ }
72
+
73
+ // Handle Map types - return undefined to signal special handling needed
74
+ if (stellarType.startsWith('Map<')) {
75
+ // Map types need special handling - can't use a simple type hint
76
+ return 'map-special';
77
+ }
78
+
79
+ // Handle Option types - return the inner type
80
+ if (stellarType.startsWith('Option<')) {
81
+ const innerType = extractOptionElementType(stellarType);
82
+ if (innerType) {
83
+ return convertStellarTypeToScValType(innerType);
84
+ }
85
+ }
86
+
87
+ switch (stellarType) {
88
+ case 'Address':
89
+ return 'address';
90
+ case 'U32':
91
+ return 'u32';
92
+ case 'U64':
93
+ return 'u64';
94
+ case 'U128':
95
+ return 'u128';
96
+ case 'U256':
97
+ return 'u256';
98
+ case 'I32':
99
+ return 'i32';
100
+ case 'I64':
101
+ return 'i64';
102
+ case 'I128':
103
+ return 'i128';
104
+ case 'I256':
105
+ return 'i256';
106
+ case 'ScString':
107
+ return 'string';
108
+ case 'ScSymbol':
109
+ return 'symbol';
110
+ case 'Bool':
111
+ return 'bool';
112
+ case 'Bytes':
113
+ return 'bytes';
114
+ case 'DataUrl':
115
+ return 'bytes';
116
+ default:
117
+ if (/^BytesN<\d+>$/.test(stellarType)) {
118
+ return 'bytes';
119
+ }
120
+ return stellarType.toLowerCase();
121
+ }
122
+ }