@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.
- package/README.md +272 -0
- package/dist/config.cjs +21 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +8 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +8 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +20 -0
- package/dist/config.mjs.map +1 -0
- package/dist/index.cjs +7564 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +261 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +263 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +7529 -0
- package/dist/index.mjs.map +1 -0
- package/dist/metadata.cjs +22 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +7 -0
- package/dist/metadata.d.cts.map +1 -0
- package/dist/metadata.d.mts +7 -0
- package/dist/metadata.d.mts.map +1 -0
- package/dist/metadata.mjs +21 -0
- package/dist/metadata.mjs.map +1 -0
- package/dist/networks-BrV516-R.d.cts +15 -0
- package/dist/networks-BrV516-R.d.cts.map +1 -0
- package/dist/networks-C0MmhJcu.d.mts +15 -0
- package/dist/networks-C0MmhJcu.d.mts.map +1 -0
- package/dist/networks-DgUFSTiC.cjs +76 -0
- package/dist/networks-DgUFSTiC.cjs.map +1 -0
- package/dist/networks-QbEPbaGT.mjs +46 -0
- package/dist/networks-QbEPbaGT.mjs.map +1 -0
- package/dist/networks.cjs +8 -0
- package/dist/networks.d.cts +2 -0
- package/dist/networks.d.mts +2 -0
- package/dist/networks.mjs +3 -0
- package/dist/vite-config.cjs +43 -0
- package/dist/vite-config.cjs.map +1 -0
- package/dist/vite-config.d.cts +35 -0
- package/dist/vite-config.d.cts.map +1 -0
- package/dist/vite-config.d.mts +35 -0
- package/dist/vite-config.d.mts.map +1 -0
- package/dist/vite-config.mjs +42 -0
- package/dist/vite-config.mjs.map +1 -0
- package/package.json +114 -0
- package/src/__tests__/getDefaultServiceConfig.test.ts +105 -0
- package/src/access-control/actions.ts +214 -0
- package/src/access-control/feature-detection.ts +238 -0
- package/src/access-control/index.ts +54 -0
- package/src/access-control/indexer-client.ts +1474 -0
- package/src/access-control/onchain-reader.ts +446 -0
- package/src/access-control/service.ts +1431 -0
- package/src/access-control/validation.ts +256 -0
- package/src/adapter.ts +659 -0
- package/src/config.ts +43 -0
- package/src/configuration/__tests__/explorer.test.ts +80 -0
- package/src/configuration/__tests__/rpc.test.ts +355 -0
- package/src/configuration/execution.ts +83 -0
- package/src/configuration/explorer.ts +105 -0
- package/src/configuration/index.ts +5 -0
- package/src/configuration/network-services.ts +210 -0
- package/src/configuration/rpc.ts +270 -0
- package/src/configuration.ts +2 -0
- package/src/contract/__tests__/complete-type-coverage.test.ts +78 -0
- package/src/contract/index.ts +3 -0
- package/src/contract/loader.ts +498 -0
- package/src/contract/transformer.ts +1 -0
- package/src/contract/type.ts +65 -0
- package/src/index.ts +23 -0
- package/src/mapping/constants.ts +89 -0
- package/src/mapping/enum-metadata.ts +237 -0
- package/src/mapping/field-generator.ts +296 -0
- package/src/mapping/index.ts +5 -0
- package/src/mapping/struct-fields.ts +106 -0
- package/src/mapping/tuple-components.ts +43 -0
- package/src/mapping/type-coverage-validator.ts +151 -0
- package/src/mapping/type-mapper.ts +203 -0
- package/src/metadata.ts +16 -0
- package/src/networks/README.md +84 -0
- package/src/networks/index.ts +19 -0
- package/src/networks/mainnet.ts +20 -0
- package/src/networks/testnet.ts +20 -0
- package/src/networks.ts +2 -0
- package/src/query/handler.ts +411 -0
- package/src/query/index.ts +4 -0
- package/src/query/view-checker.ts +32 -0
- package/src/sac/spec-cache.ts +68 -0
- package/src/sac/spec-source.ts +35 -0
- package/src/sac/xdr.ts +101 -0
- package/src/transaction/components/AdvancedInfo.tsx +34 -0
- package/src/transaction/components/FeeConfiguration.tsx +41 -0
- package/src/transaction/components/StellarRelayerOptions.tsx +60 -0
- package/src/transaction/components/TransactionTiming.tsx +77 -0
- package/src/transaction/components/index.ts +5 -0
- package/src/transaction/components/useStellarRelayerOptions.ts +114 -0
- package/src/transaction/eoa.ts +229 -0
- package/src/transaction/execution-strategy.ts +33 -0
- package/src/transaction/formatter.ts +296 -0
- package/src/transaction/index.ts +4 -0
- package/src/transaction/relayer.ts +575 -0
- package/src/transaction/sender.ts +156 -0
- package/src/transform/index.ts +4 -0
- package/src/transform/input-parser.ts +9 -0
- package/src/transform/output-formatter.ts +133 -0
- package/src/transform/parsers/complex-parser.ts +157 -0
- package/src/transform/parsers/generic-parser.ts +171 -0
- package/src/transform/parsers/index.ts +86 -0
- package/src/transform/parsers/primitive-parser.ts +123 -0
- package/src/transform/parsers/scval-converter.ts +405 -0
- package/src/transform/parsers/struct-parser.ts +324 -0
- package/src/transform/parsers/types.ts +35 -0
- package/src/types/__tests__/artifacts.test.ts +89 -0
- package/src/types/artifacts.ts +19 -0
- package/src/utils/__tests__/artifacts.test.ts +77 -0
- package/src/utils/artifacts.ts +30 -0
- package/src/utils/formatting.ts +122 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/input-parsing.ts +336 -0
- package/src/utils/safe-type-parser.ts +303 -0
- package/src/utils/stellar-types.ts +35 -0
- package/src/utils/type-detection.ts +163 -0
- package/src/utils/xdr-ordering.ts +36 -0
- package/src/validation/__tests__/address.test.ts +267 -0
- package/src/validation/address.ts +136 -0
- package/src/validation/eoa.ts +33 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/relayer.ts +13 -0
- package/src/vite-config.ts +67 -0
- package/src/wallet/README.md +93 -0
- package/src/wallet/__tests__/connection.test.ts +72 -0
- package/src/wallet/components/StellarWalletUiRoot.tsx +161 -0
- package/src/wallet/components/account/AccountDisplay.tsx +50 -0
- package/src/wallet/components/connect/ConnectButton.tsx +100 -0
- package/src/wallet/components/connect/ConnectorDialog.tsx +125 -0
- package/src/wallet/components/index.ts +3 -0
- package/src/wallet/connection.ts +151 -0
- package/src/wallet/context/StellarWalletContext.ts +32 -0
- package/src/wallet/context/index.ts +4 -0
- package/src/wallet/context/useStellarWalletContext.ts +17 -0
- package/src/wallet/hooks/facade-hooks.ts +31 -0
- package/src/wallet/hooks/index.ts +7 -0
- package/src/wallet/hooks/useStellarAccount.ts +27 -0
- package/src/wallet/hooks/useStellarConnect.ts +60 -0
- package/src/wallet/hooks/useStellarDisconnect.ts +47 -0
- package/src/wallet/hooks/useUiKitConfig.ts +40 -0
- package/src/wallet/implementation/wallets-kit-implementation.ts +379 -0
- package/src/wallet/index.ts +11 -0
- package/src/wallet/services/__tests__/configResolutionService.test.ts +163 -0
- package/src/wallet/services/configResolutionService.ts +65 -0
- package/src/wallet/stellar-wallets-kit/StellarWalletsKitConnectButton.tsx +82 -0
- package/src/wallet/stellar-wallets-kit/__mocks__/@creit.tech/stellar-wallets-kit.ts +48 -0
- package/src/wallet/stellar-wallets-kit/__tests__/export-service.test.ts +93 -0
- package/src/wallet/stellar-wallets-kit/__tests__/stellarUiKitManager.test.ts +0 -0
- package/src/wallet/stellar-wallets-kit/config-generator.ts +75 -0
- package/src/wallet/stellar-wallets-kit/export-service.ts +19 -0
- package/src/wallet/stellar-wallets-kit/index.ts +3 -0
- package/src/wallet/stellar-wallets-kit/stellarUiKitManager.ts +235 -0
- package/src/wallet/types.ts +19 -0
- package/src/wallet/utils/__tests__/filterWalletComponents.test.ts +150 -0
- package/src/wallet/utils/__tests__/uiKitService.test.ts +189 -0
- package/src/wallet/utils/filterWalletComponents.ts +89 -0
- package/src/wallet/utils/index.ts +3 -0
- package/src/wallet/utils/stellarWalletImplementationManager.ts +118 -0
- 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
|
+
}
|