@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,86 @@
|
|
|
1
|
+
import { isEnumValue } from '@openzeppelin/ui-types';
|
|
2
|
+
import { isPlainObject, logger } from '@openzeppelin/ui-utils';
|
|
3
|
+
|
|
4
|
+
import { isLikelyEnumType } from '../../utils/type-detection';
|
|
5
|
+
import { isGenericType, parseGeneric } from './generic-parser';
|
|
6
|
+
import { isPrimitiveType, parsePrimitive } from './primitive-parser';
|
|
7
|
+
|
|
8
|
+
// Re-export types for backward compatibility
|
|
9
|
+
export type {
|
|
10
|
+
SorobanArgumentValue,
|
|
11
|
+
SorobanEnumValue,
|
|
12
|
+
SorobanMapEntry,
|
|
13
|
+
SorobanComplexValue,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
// Re-export specific functions for backward compatibility
|
|
17
|
+
export { getScValsFromArgs } from './complex-parser';
|
|
18
|
+
export { valueToScVal } from './scval-converter';
|
|
19
|
+
|
|
20
|
+
const SYSTEM_LOG_TAG = 'StellarInputParser';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parses form input values for Stellar/Soroban contracts.
|
|
24
|
+
* Handles both simple UI form inputs and complex structures.
|
|
25
|
+
*
|
|
26
|
+
* @param value - The input value from the form
|
|
27
|
+
* @param parameterType - The Stellar parameter type (e.g., 'Address', 'U128', 'Vec<U32>')
|
|
28
|
+
* @returns The parsed value suitable for nativeToScVal conversion
|
|
29
|
+
*/
|
|
30
|
+
export function parseStellarInput(value: unknown, parameterType: string): unknown {
|
|
31
|
+
try {
|
|
32
|
+
// Handle null/undefined values
|
|
33
|
+
if (value === null || value === undefined) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Try primitive types first (most common)
|
|
38
|
+
if (isPrimitiveType(parameterType)) {
|
|
39
|
+
const result = parsePrimitive(value, parameterType);
|
|
40
|
+
if (result !== null) {
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Try generic types (Vec, Map, Option, Result)
|
|
46
|
+
if (isGenericType(parameterType)) {
|
|
47
|
+
const result = parseGeneric(value, parameterType, parseStellarInput);
|
|
48
|
+
// For generic types, we always return the result (including null for Option<T> with empty/null values)
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Handle remaining custom types and special cases
|
|
53
|
+
// Check if this is an enum type and transform from chain-agnostic to Soroban format
|
|
54
|
+
if (isEnumValue(value) && isLikelyEnumType(parameterType)) {
|
|
55
|
+
// Chain-agnostic enum format: { tag: "VariantName", values?: [...] }
|
|
56
|
+
// Return the chain-agnostic enum value as-is
|
|
57
|
+
// The Stellar-specific transformation will happen in valueToScVal
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if this is a struct type (object with primitive fields)
|
|
62
|
+
if (isPlainObject(value)) {
|
|
63
|
+
// For structs, we need to recursively process each field
|
|
64
|
+
// But since we don't have field type information here, we'll pass it through
|
|
65
|
+
// and handle the conversion in valueToScVal where we have access to contract schema
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Accept array-shaped values for struct-like types (e.g., tuple structs with numeric keys).
|
|
70
|
+
// RHF treats numeric path segments as arrays, so a struct with fields "0", "1" can arrive as an array.
|
|
71
|
+
// Pass through; valueToScVal/convertStructToScVal will serialize correctly using the schema.
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// For other types, try to return raw value with validation
|
|
77
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new Error(`Unsupported parameter type: ${parameterType} with value type ${typeof value}`);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logger.error(SYSTEM_LOG_TAG, 'Failed to parse Stellar input:', error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Address } from '@stellar/stellar-sdk';
|
|
2
|
+
|
|
3
|
+
import { detectBytesEncoding, logger, stringToBytes } from '@openzeppelin/ui-utils';
|
|
4
|
+
|
|
5
|
+
const SYSTEM_LOG_TAG = 'PrimitiveParser';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parses primitive Stellar/Soroban types from form inputs.
|
|
9
|
+
* Handles: Bool, Bytes, Address, ScString, ScSymbol, and numeric types (U32, U64, U128, U256, I32, I64, I128, I256).
|
|
10
|
+
*
|
|
11
|
+
* @param value - The input value from the form
|
|
12
|
+
* @param parameterType - The Stellar parameter type
|
|
13
|
+
* @returns The parsed primitive value
|
|
14
|
+
*/
|
|
15
|
+
export function parsePrimitive(value: unknown, parameterType: string): unknown {
|
|
16
|
+
try {
|
|
17
|
+
// Handle null/undefined values
|
|
18
|
+
if (value === null || value === undefined) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (parameterType) {
|
|
23
|
+
// Boolean: convert string "true"/"false" to actual boolean
|
|
24
|
+
case 'Bool':
|
|
25
|
+
if (typeof value === 'boolean') {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
return value.toLowerCase() === 'true';
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Boolean parameter expected, got ${typeof value}`);
|
|
32
|
+
|
|
33
|
+
// Bytes: handle encoding detection
|
|
34
|
+
case 'Bytes':
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
// Remove 0x prefix if present
|
|
37
|
+
const cleanValue = value.startsWith('0x') ? value.slice(2) : value;
|
|
38
|
+
const encoding = detectBytesEncoding(cleanValue);
|
|
39
|
+
return stringToBytes(cleanValue, encoding);
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Bytes parameter must be a string, got ${typeof value}`);
|
|
42
|
+
|
|
43
|
+
// DataUrl: handle base64 encoded data (similar to Bytes)
|
|
44
|
+
case 'DataUrl':
|
|
45
|
+
if (typeof value === 'string') {
|
|
46
|
+
// DataUrl is typically base64 encoded
|
|
47
|
+
const encoding = detectBytesEncoding(value);
|
|
48
|
+
return stringToBytes(value, encoding);
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`DataUrl parameter must be a string, got ${typeof value}`);
|
|
51
|
+
|
|
52
|
+
// Address: validate format
|
|
53
|
+
case 'Address':
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
try {
|
|
56
|
+
Address.fromString(value); // Validate format
|
|
57
|
+
return value; // Return raw string - nativeToScVal will handle conversion
|
|
58
|
+
} catch {
|
|
59
|
+
throw new Error(`Invalid Stellar address format: ${value}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Address parameter must be a string, got ${typeof value}`);
|
|
63
|
+
|
|
64
|
+
// String types: return as-is
|
|
65
|
+
case 'ScString':
|
|
66
|
+
case 'ScSymbol':
|
|
67
|
+
if (typeof value === 'string') {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`String parameter expected, got ${typeof value}`);
|
|
71
|
+
|
|
72
|
+
default:
|
|
73
|
+
// Handle BytesN<size> patterns
|
|
74
|
+
if (/^BytesN<\d+>$/.test(parameterType)) {
|
|
75
|
+
if (typeof value === 'string') {
|
|
76
|
+
const cleanValue = value.startsWith('0x') ? value.slice(2) : value;
|
|
77
|
+
const encoding = detectBytesEncoding(cleanValue);
|
|
78
|
+
return stringToBytes(cleanValue, encoding);
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Bytes parameter must be a string, got ${typeof value}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Handle numeric types: U32, U64, U128, U256, I32, I64, I128, I256
|
|
84
|
+
if (/^[UI](32|64|128|256)$/.test(parameterType)) {
|
|
85
|
+
if (typeof value === 'string') {
|
|
86
|
+
// Validate it's a valid integer (no decimals, letters)
|
|
87
|
+
if (!/^-?\d+$/.test(value.trim())) {
|
|
88
|
+
throw new Error(`Invalid number format for ${parameterType}: ${value}`);
|
|
89
|
+
}
|
|
90
|
+
return value; // Return raw string - let nativeToScVal handle conversion with type hints
|
|
91
|
+
}
|
|
92
|
+
if (typeof value === 'number') {
|
|
93
|
+
return value.toString(); // Convert to string for consistency
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`Numeric parameter expected for ${parameterType}, got ${typeof value}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null; // Not a primitive type - let other parsers handle it
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.error(SYSTEM_LOG_TAG, `Failed to parse primitive ${parameterType}:`, error);
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Checks if the given parameter type is a primitive type that can be handled by this parser.
|
|
108
|
+
*
|
|
109
|
+
* @param parameterType - The Stellar parameter type
|
|
110
|
+
* @returns True if this is a primitive type
|
|
111
|
+
*/
|
|
112
|
+
export function isPrimitiveType(parameterType: string): boolean {
|
|
113
|
+
return (
|
|
114
|
+
parameterType === 'Bool' ||
|
|
115
|
+
parameterType === 'Bytes' ||
|
|
116
|
+
parameterType === 'DataUrl' ||
|
|
117
|
+
parameterType === 'Address' ||
|
|
118
|
+
parameterType === 'ScString' ||
|
|
119
|
+
parameterType === 'ScSymbol' ||
|
|
120
|
+
/^BytesN<\d+>$/.test(parameterType) ||
|
|
121
|
+
/^[UI](32|64|128|256)$/.test(parameterType)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { nativeToScVal, xdr } from '@stellar/stellar-sdk';
|
|
2
|
+
|
|
3
|
+
import { isEnumValue, type FunctionParameter } from '@openzeppelin/ui-types';
|
|
4
|
+
|
|
5
|
+
import { convertStellarTypeToScValType } from '../../utils/formatting';
|
|
6
|
+
import { convertEnumToScVal } from '../../utils/input-parsing';
|
|
7
|
+
import { isPrimitiveParamType } from '../../utils/stellar-types';
|
|
8
|
+
import { isBytesNType, isLikelyEnumType } from '../../utils/type-detection';
|
|
9
|
+
import { compareScValsByXdr } from '../../utils/xdr-ordering';
|
|
10
|
+
import { parseGenericType } from './generic-parser';
|
|
11
|
+
import { parsePrimitive } from './primitive-parser';
|
|
12
|
+
import { convertStructToScVal, isStructType } from './struct-parser';
|
|
13
|
+
import type { SorobanEnumValue } from './types';
|
|
14
|
+
|
|
15
|
+
// FunctionParameter already includes enumMetadata in its type definition (from @openzeppelin/ui-types)
|
|
16
|
+
// No need for a separate type wrapper
|
|
17
|
+
type EnumAwareFunctionParameter = FunctionParameter;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Converts a value to ScVal with comprehensive generic type support.
|
|
21
|
+
* This should be used in the transaction execution instead of calling nativeToScVal directly.
|
|
22
|
+
*
|
|
23
|
+
* @param value - The parsed value from parseStellarInput
|
|
24
|
+
* @param parameterType - The Stellar parameter type
|
|
25
|
+
* @param paramSchema - Optional parameter schema with struct field definitions
|
|
26
|
+
* @param parseInnerValue - Function to recursively parse inner values (defaults to parseStellarInput)
|
|
27
|
+
* @returns ScVal ready for contract calls
|
|
28
|
+
*/
|
|
29
|
+
export function valueToScVal(
|
|
30
|
+
value: unknown,
|
|
31
|
+
parameterType: string,
|
|
32
|
+
paramSchema?: FunctionParameter,
|
|
33
|
+
parseInnerValue?: (val: unknown, type: string) => unknown
|
|
34
|
+
): xdr.ScVal {
|
|
35
|
+
// Default parseInnerValue to a basic pass-through function if not provided
|
|
36
|
+
// This handles the common case where callers don't need recursive parsing
|
|
37
|
+
const parseValue = parseInnerValue || ((val: unknown) => val);
|
|
38
|
+
const genericInfo = parseGenericType(parameterType);
|
|
39
|
+
|
|
40
|
+
// Helper: detect SorobanArgumentValue wrapper { type, value }
|
|
41
|
+
const isTypedWrapper = (v: unknown): v is { type: string; value: unknown } =>
|
|
42
|
+
!!v &&
|
|
43
|
+
typeof v === 'object' &&
|
|
44
|
+
'type' in (v as Record<string, unknown>) &&
|
|
45
|
+
'value' in (v as Record<string, unknown>);
|
|
46
|
+
|
|
47
|
+
const enumMetadata = (paramSchema as EnumAwareFunctionParameter | undefined)?.enumMetadata;
|
|
48
|
+
const possibleEnumValue =
|
|
49
|
+
typeof value === 'string' && (enumMetadata || isLikelyEnumType(parameterType))
|
|
50
|
+
? { tag: value }
|
|
51
|
+
: value;
|
|
52
|
+
|
|
53
|
+
if (!genericInfo) {
|
|
54
|
+
// Integer-only enums (discriminant enums) → encode as u32 (matches Lab behavior)
|
|
55
|
+
if (enumMetadata && enumMetadata.variants.every((v) => v.type === 'integer')) {
|
|
56
|
+
// Derive numeric discriminant from name, tag, or numeric input
|
|
57
|
+
let numericValue: number | undefined;
|
|
58
|
+
if (typeof value === 'string') {
|
|
59
|
+
const byName = enumMetadata.variants.find((v) => v.name === value);
|
|
60
|
+
numericValue = byName?.value ?? Number(value);
|
|
61
|
+
} else if (typeof value === 'number') {
|
|
62
|
+
numericValue = value;
|
|
63
|
+
} else if (isEnumValue(value)) {
|
|
64
|
+
const byTag = enumMetadata.variants.find((v) => v.name === value.tag);
|
|
65
|
+
numericValue = byTag?.value;
|
|
66
|
+
}
|
|
67
|
+
if (numericValue === undefined || Number.isNaN(numericValue)) {
|
|
68
|
+
const validNames = enumMetadata.variants.map((v) => v.name).join(', ');
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Invalid integer enum value for ${parameterType}: ${String(value)}. Expected one of: ${validNames}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return nativeToScVal(numericValue, { type: 'u32' });
|
|
74
|
+
}
|
|
75
|
+
// If a typed wrapper is provided, convert directly using the wrapped type/value
|
|
76
|
+
if (isTypedWrapper(possibleEnumValue)) {
|
|
77
|
+
const wrapped = possibleEnumValue;
|
|
78
|
+
const parsed = parsePrimitive(wrapped.value, wrapped.type);
|
|
79
|
+
const finalVal = parsed !== null ? parsed : wrapped.value;
|
|
80
|
+
const scValType = convertStellarTypeToScValType(wrapped.type);
|
|
81
|
+
const typeHint = Array.isArray(scValType) ? scValType[0] : scValType;
|
|
82
|
+
return nativeToScVal(finalVal, { type: typeHint });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if this is an enum object (has 'tag' or 'enum' property)
|
|
86
|
+
if (
|
|
87
|
+
isEnumValue(possibleEnumValue) ||
|
|
88
|
+
(typeof possibleEnumValue === 'object' &&
|
|
89
|
+
possibleEnumValue !== null &&
|
|
90
|
+
'enum' in possibleEnumValue)
|
|
91
|
+
) {
|
|
92
|
+
const enumValue = possibleEnumValue as { tag: string; values?: unknown[]; enum?: number };
|
|
93
|
+
|
|
94
|
+
// Handle integer enums
|
|
95
|
+
if ('enum' in enumValue && typeof enumValue.enum === 'number') {
|
|
96
|
+
return nativeToScVal(enumValue.enum, { type: 'u32' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Handle tagged enums with metadata for proper type conversion
|
|
100
|
+
const tagSymbol = nativeToScVal(enumValue.tag, { type: 'symbol' });
|
|
101
|
+
|
|
102
|
+
if (!enumValue.values || enumValue.values.length === 0) {
|
|
103
|
+
// Unit variant - ScVec containing single ScSymbol
|
|
104
|
+
return xdr.ScVal.scvVec([tagSymbol]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const payloadValues = enumValue.values as unknown[];
|
|
108
|
+
|
|
109
|
+
// Tuple variant - convert each payload value with proper types
|
|
110
|
+
let payloadScVals: xdr.ScVal[];
|
|
111
|
+
// Use the variant type from EnumAwareFunctionParameter's enumMetadata
|
|
112
|
+
type EnumVariant = NonNullable<
|
|
113
|
+
EnumAwareFunctionParameter['enumMetadata']
|
|
114
|
+
>['variants'][number];
|
|
115
|
+
let variant: EnumVariant | undefined;
|
|
116
|
+
|
|
117
|
+
if (enumMetadata) {
|
|
118
|
+
variant = enumMetadata.variants.find((variantEntry) => variantEntry.name === enumValue.tag);
|
|
119
|
+
if (!variant || !variant.payloadTypes) {
|
|
120
|
+
// No variant metadata or payloadTypes - use convertEnumToScVal fallback
|
|
121
|
+
return convertEnumToScVal(enumValue as SorobanEnumValue);
|
|
122
|
+
}
|
|
123
|
+
// Convert each payload value with its corresponding type
|
|
124
|
+
// variant is guaranteed to be defined here due to the check above
|
|
125
|
+
const payloadTypes = variant.payloadTypes;
|
|
126
|
+
const payloadComponents = variant.payloadComponents;
|
|
127
|
+
payloadScVals = payloadTypes.map((payloadType, index) => {
|
|
128
|
+
const payloadSchema = payloadComponents?.[index]
|
|
129
|
+
? {
|
|
130
|
+
name: `payload_${index}`,
|
|
131
|
+
type: payloadType,
|
|
132
|
+
components: payloadComponents[index],
|
|
133
|
+
}
|
|
134
|
+
: { name: `payload_${index}`, type: payloadType };
|
|
135
|
+
|
|
136
|
+
const val = payloadValues[index];
|
|
137
|
+
return valueToScVal(val, payloadType, payloadSchema, parseValue);
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
// No enum metadata - use convertEnumToScVal fallback
|
|
141
|
+
return convertEnumToScVal(enumValue as SorobanEnumValue);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// For single Tuple payload, wrap all payload ScVals in another ScVec
|
|
145
|
+
// Example: Some((Address, i128)) → ScVec([Symbol("Some"), ScVec([Address, I128])])
|
|
146
|
+
if (variant?.isSingleTuplePayload) {
|
|
147
|
+
const tuplePayloadVec = xdr.ScVal.scvVec(payloadScVals);
|
|
148
|
+
return xdr.ScVal.scvVec([tagSymbol, tuplePayloadVec]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Return ScVec with tag symbol followed by payload values
|
|
152
|
+
return xdr.ScVal.scvVec([tagSymbol, ...payloadScVals]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check if this is a struct or tuple type
|
|
156
|
+
// Accept array-shaped values for tuple-structs when schema components are provided
|
|
157
|
+
if (
|
|
158
|
+
Array.isArray(possibleEnumValue) &&
|
|
159
|
+
paramSchema?.components &&
|
|
160
|
+
paramSchema.components.length
|
|
161
|
+
) {
|
|
162
|
+
// Runtime validation: ensure array length matches schema components
|
|
163
|
+
if (possibleEnumValue.length !== paramSchema.components.length) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Tuple-struct value length (${possibleEnumValue.length}) does not match schema components (${paramSchema.components.length}) for type ${parameterType}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
return convertStructToScVal(
|
|
169
|
+
possibleEnumValue as unknown as Record<string, unknown>,
|
|
170
|
+
parameterType,
|
|
171
|
+
paramSchema,
|
|
172
|
+
parseValue,
|
|
173
|
+
(innerValue, innerType, innerSchema) =>
|
|
174
|
+
valueToScVal(innerValue, innerType, innerSchema, parseInnerValue)
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!isPrimitiveParamType(parameterType) && isStructType(value, parameterType)) {
|
|
179
|
+
return convertStructToScVal(
|
|
180
|
+
value as Record<string, unknown>,
|
|
181
|
+
parameterType,
|
|
182
|
+
paramSchema,
|
|
183
|
+
parseValue,
|
|
184
|
+
(innerValue, innerType, innerSchema) =>
|
|
185
|
+
valueToScVal(innerValue, innerType, innerSchema, parseInnerValue)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Non-generic types - use existing logic
|
|
190
|
+
if (parameterType === 'Bool' || parameterType === 'Bytes') {
|
|
191
|
+
return nativeToScVal(value);
|
|
192
|
+
}
|
|
193
|
+
if (isBytesNType(parameterType)) {
|
|
194
|
+
const match = parameterType.match(/^BytesN<(\d+)>$/);
|
|
195
|
+
if (!match) {
|
|
196
|
+
throw new Error(`Invalid BytesN parameterType format: ${parameterType}`);
|
|
197
|
+
}
|
|
198
|
+
const expectedBytes = Number.parseInt(match[1], 10);
|
|
199
|
+
const decoded = parsePrimitive(value, 'Bytes');
|
|
200
|
+
const bytesValue = decoded instanceof Uint8Array ? decoded : (decoded ?? value);
|
|
201
|
+
|
|
202
|
+
if (
|
|
203
|
+
Number.isFinite(expectedBytes) &&
|
|
204
|
+
bytesValue instanceof Uint8Array &&
|
|
205
|
+
bytesValue.length !== expectedBytes
|
|
206
|
+
) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`BytesN value must be exactly ${expectedBytes} bytes, received ${bytesValue.length}`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return nativeToScVal(bytesValue);
|
|
213
|
+
}
|
|
214
|
+
const scValType = convertStellarTypeToScValType(parameterType);
|
|
215
|
+
const typeHint = Array.isArray(scValType) ? scValType[0] : scValType;
|
|
216
|
+
return nativeToScVal(value, { type: typeHint });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const { baseType, parameters } = genericInfo;
|
|
220
|
+
|
|
221
|
+
switch (baseType) {
|
|
222
|
+
case 'Vec': {
|
|
223
|
+
// Handle Vec<T> types
|
|
224
|
+
const innerType = parameters[0];
|
|
225
|
+
if (Array.isArray(value)) {
|
|
226
|
+
// For enum element types, we need to pass enum metadata
|
|
227
|
+
// Check if paramSchema has enumMetadata or components that should be used for elements
|
|
228
|
+
let elementSchema: FunctionParameter | undefined;
|
|
229
|
+
if (enumMetadata) {
|
|
230
|
+
// This Vec is of enum type - pass the enum metadata to each element
|
|
231
|
+
elementSchema = {
|
|
232
|
+
name: 'element',
|
|
233
|
+
type: innerType,
|
|
234
|
+
enumMetadata,
|
|
235
|
+
} as EnumAwareFunctionParameter;
|
|
236
|
+
} else if (paramSchema?.components) {
|
|
237
|
+
// This Vec is of struct type - pass the components to each element
|
|
238
|
+
elementSchema = {
|
|
239
|
+
name: 'element',
|
|
240
|
+
type: innerType,
|
|
241
|
+
components: paramSchema.components,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const convertedElements = value.map((element) =>
|
|
246
|
+
valueToScVal(element, innerType, elementSchema, parseValue)
|
|
247
|
+
);
|
|
248
|
+
return nativeToScVal(convertedElements);
|
|
249
|
+
}
|
|
250
|
+
return nativeToScVal(value);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
case 'Map': {
|
|
254
|
+
// Handle Map<K,V> types in Stellar SDK format
|
|
255
|
+
if (Array.isArray(value)) {
|
|
256
|
+
// Expect Stellar SDK format: [{ 0: {value, type}, 1: {value, type} }, ...]
|
|
257
|
+
const mapEntries: xdr.ScMapEntry[] = [];
|
|
258
|
+
|
|
259
|
+
value.forEach(
|
|
260
|
+
(entry: { 0: { value: string; type: string }; 1: { value: string; type: string } }) => {
|
|
261
|
+
if (
|
|
262
|
+
typeof entry !== 'object' ||
|
|
263
|
+
entry === null ||
|
|
264
|
+
!entry[0] ||
|
|
265
|
+
!entry[1] ||
|
|
266
|
+
typeof entry[0].value === 'undefined' ||
|
|
267
|
+
typeof entry[1].value === 'undefined'
|
|
268
|
+
) {
|
|
269
|
+
throw new Error('Invalid Stellar SDK map format in valueToScVal');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Process key and value through parsePrimitive for bytes conversion
|
|
273
|
+
let processedKey: unknown = entry[0].value;
|
|
274
|
+
let processedValue: unknown = entry[1].value;
|
|
275
|
+
|
|
276
|
+
// Handle bytes conversion for keys
|
|
277
|
+
const keyPrimitive = parsePrimitive(entry[0].value, entry[0].type);
|
|
278
|
+
if (keyPrimitive !== null) {
|
|
279
|
+
processedKey = keyPrimitive;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Handle bytes conversion for values
|
|
283
|
+
const valuePrimitive = parsePrimitive(entry[1].value, entry[1].type);
|
|
284
|
+
if (valuePrimitive !== null) {
|
|
285
|
+
processedValue = valuePrimitive;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Create ScVals for key and value
|
|
289
|
+
const keyScValType = convertStellarTypeToScValType(entry[0].type);
|
|
290
|
+
const keyTypeHint = Array.isArray(keyScValType) ? keyScValType[0] : keyScValType;
|
|
291
|
+
const keyScVal = nativeToScVal(processedKey, { type: keyTypeHint });
|
|
292
|
+
|
|
293
|
+
const valueScValType = convertStellarTypeToScValType(entry[1].type);
|
|
294
|
+
const valueTypeHint = Array.isArray(valueScValType)
|
|
295
|
+
? valueScValType[0]
|
|
296
|
+
: valueScValType;
|
|
297
|
+
const valueScVal = nativeToScVal(processedValue, { type: valueTypeHint });
|
|
298
|
+
|
|
299
|
+
mapEntries.push(
|
|
300
|
+
new xdr.ScMapEntry({
|
|
301
|
+
key: keyScVal,
|
|
302
|
+
val: valueScVal,
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Sort map entries by XDR-encoded keys (required by Soroban)
|
|
309
|
+
const sortedMapEntries = mapEntries.sort((a, b) => compareScValsByXdr(a.key(), b.key()));
|
|
310
|
+
return xdr.ScVal.scvMap(sortedMapEntries);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return nativeToScVal(value);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
case 'Tuple': {
|
|
317
|
+
if (!paramSchema?.components || paramSchema.components.length === 0) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Tuple parameter "${paramSchema?.name ?? 'unknown'}" is missing component metadata`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const tupleComponents = paramSchema.components;
|
|
324
|
+
const tupleValues: xdr.ScVal[] = [];
|
|
325
|
+
|
|
326
|
+
tupleComponents.forEach((component, index) => {
|
|
327
|
+
const key = component.name ?? `item_${index}`;
|
|
328
|
+
let elementValue: unknown;
|
|
329
|
+
|
|
330
|
+
if (Array.isArray(value)) {
|
|
331
|
+
elementValue = value[index];
|
|
332
|
+
} else if (value && typeof value === 'object') {
|
|
333
|
+
elementValue = (value as Record<string, unknown>)[key];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (typeof elementValue === 'undefined') {
|
|
337
|
+
const expectedTypes = tupleComponents.map((c) => c.type).join(', ');
|
|
338
|
+
throw new Error(
|
|
339
|
+
`Missing tuple value for "${key}" in parameter "${paramSchema.name ?? 'unknown'}". Expected ${tupleComponents.length} values of types [${expectedTypes}] but received: ${JSON.stringify(value)}`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (typeof elementValue === 'string' && isLikelyEnumType(component.type)) {
|
|
344
|
+
elementValue = { tag: elementValue };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
tupleValues.push(valueToScVal(elementValue, component.type, component, parseInnerValue));
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
return xdr.ScVal.scvVec(tupleValues);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
case 'Option': {
|
|
354
|
+
// Handle Option<T> types
|
|
355
|
+
const innerType = parameters[0];
|
|
356
|
+
|
|
357
|
+
if (value === null || value === undefined) {
|
|
358
|
+
return nativeToScVal(null); // None variant
|
|
359
|
+
} else {
|
|
360
|
+
// Some variant - convert the inner value
|
|
361
|
+
let innerSchema: FunctionParameter | undefined;
|
|
362
|
+
if (enumMetadata) {
|
|
363
|
+
innerSchema = {
|
|
364
|
+
name: 'inner',
|
|
365
|
+
type: innerType,
|
|
366
|
+
...({ enumMetadata } as unknown as Record<string, unknown>),
|
|
367
|
+
} as unknown as FunctionParameter;
|
|
368
|
+
} else if (paramSchema?.components) {
|
|
369
|
+
innerSchema = {
|
|
370
|
+
name: 'inner',
|
|
371
|
+
type: innerType,
|
|
372
|
+
components: paramSchema.components,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return valueToScVal(value, innerType, innerSchema, parseInnerValue);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
case 'Result': {
|
|
380
|
+
// Handle Result<T,E> types
|
|
381
|
+
const okType = parameters[0];
|
|
382
|
+
const errType = parameters[1];
|
|
383
|
+
|
|
384
|
+
// Result types typically come as {ok: value} or {err: error}
|
|
385
|
+
if (typeof value === 'object' && value !== null) {
|
|
386
|
+
const resultObj = value as Record<string, unknown>;
|
|
387
|
+
if ('ok' in resultObj) {
|
|
388
|
+
const okScVal = valueToScVal(resultObj.ok, okType);
|
|
389
|
+
return nativeToScVal({ ok: okScVal });
|
|
390
|
+
} else if ('err' in resultObj) {
|
|
391
|
+
const errScVal = valueToScVal(resultObj.err, errType);
|
|
392
|
+
return nativeToScVal({ err: errScVal });
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return nativeToScVal(value);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
default: {
|
|
399
|
+
// Unknown generic type - fallback to basic conversion
|
|
400
|
+
const scValType = convertStellarTypeToScValType(parameterType);
|
|
401
|
+
const typeHint = Array.isArray(scValType) ? scValType[0] : scValType;
|
|
402
|
+
return nativeToScVal(value, { type: typeHint });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|