UNPKG

sui-svelte-wallet-kit

Version:

Svelte 5 wallet kit for Sui: connect wallets, manage accounts, SuiNS, balance, sign transactions/messages

165 lines (164 loc) 6.24 kB
/** * Multisig Validation * Validates MultisigConfig and signer definitions */ import { MultisigError, MultisigErrorCode } from './MultisigTypes.js'; /** * Check if config is preconfigured mode */ export function isPreconfiguredMode(config) { return config.mode === 'preconfigured'; } /** * Check if config is dynamic mode */ export function isDynamicMode(config) { return config.mode === 'dynamic'; } /** * Validate a single signer definition */ export function validateSigner(signer, index) { const errors = []; const prefix = `Signer[${index}]`; if (!signer || typeof signer !== 'object') { errors.push(`${prefix}: Invalid signer definition`); return errors; } // Validate weight if provided if (signer.weight !== undefined) { if (typeof signer.weight !== 'number' || signer.weight < 1) { errors.push(`${prefix}: Weight must be a positive number (got ${signer.weight})`); } } switch (signer.type) { case 'passkey': // Passkey signer only needs type, weight and name are optional break; case 'zklogin': // ZkLogin signer only needs type, weight and name are optional break; case 'wallet': if (!signer.address || typeof signer.address !== 'string') { errors.push(`${prefix}: Wallet signer requires a valid address`); } else if (!signer.address.startsWith('0x')) { errors.push(`${prefix}: Wallet address must start with '0x'`); } break; case 'publicKey': if (!signer.publicKey || typeof signer.publicKey !== 'string') { errors.push(`${prefix}: PublicKey signer requires a valid publicKey (base64 string)`); } if (!signer.keyType) { errors.push(`${prefix}: PublicKey signer requires keyType (ed25519, secp256k1, secp256r1, passkey, or zklogin)`); } else if (!['ed25519', 'secp256k1', 'secp256r1', 'passkey', 'zklogin'].includes(signer.keyType)) { errors.push(`${prefix}: Invalid keyType '${signer.keyType}'. Must be ed25519, secp256k1, secp256r1, passkey, or zklogin`); } break; default: errors.push(`${prefix}: Unknown signer type '${signer.type}'`); } return errors; } /** * Calculate total weight from signers */ export function calculateTotalWeight(signers) { return signers.reduce((sum, s) => sum + (s.weight ?? 1), 0); } /** * Validate preconfigured mode config */ export function validatePreconfiguredConfig(config) { const errors = []; // Validate signers array if (!config.signers || !Array.isArray(config.signers)) { errors.push('MultisigPreConfig requires a signers array'); return { valid: false, errors }; } if (config.signers.length === 0) { errors.push('MultisigPreConfig requires at least one signer'); return { valid: false, errors }; } // Validate each signer config.signers.forEach((signer, index) => { errors.push(...validateSigner(signer, index)); }); // Validate threshold const totalWeight = calculateTotalWeight(config.signers); if (typeof config.threshold !== 'number') { errors.push('Threshold must be a number'); } else if (config.threshold < 1) { errors.push(`Threshold must be at least 1 (got ${config.threshold})`); } else if (config.threshold > totalWeight) { errors.push(`Threshold (${config.threshold}) cannot exceed total signer weight (${totalWeight})`); } // Validate network if provided if (config.network !== undefined) { if (!['mainnet', 'testnet', 'devnet'].includes(config.network)) { errors.push(`Invalid network '${config.network}'. Must be mainnet, testnet, or devnet`); } } return { valid: errors.length === 0, errors }; } /** * Validate dynamic mode config */ export function validateDynamicConfig(config) { const errors = []; // Validate defaultThreshold if provided if (config.defaultThreshold !== undefined) { if (typeof config.defaultThreshold !== 'number' || config.defaultThreshold < 1) { errors.push(`defaultThreshold must be a positive number (got ${config.defaultThreshold})`); } } // Validate network if provided if (config.network !== undefined) { if (!['mainnet', 'testnet', 'devnet'].includes(config.network)) { errors.push(`Invalid network '${config.network}'. Must be mainnet, testnet, or devnet`); } } // storageKey is optional, no validation needed return { valid: errors.length === 0, errors }; } /** * Validate MultisigConfig * Throws MultisigError if invalid */ export function validateMultisigConfig(config) { if (!config || typeof config !== 'object') { throw new MultisigError(MultisigErrorCode.INVALID_CONFIG, 'MultisigConfig must be a valid object'); } if (!config.mode) { throw new MultisigError(MultisigErrorCode.INVALID_CONFIG, "MultisigConfig requires a 'mode' property ('preconfigured' or 'dynamic')"); } let result; if (isPreconfiguredMode(config)) { result = validatePreconfiguredConfig(config); } else if (isDynamicMode(config)) { result = validateDynamicConfig(config); } else { throw new MultisigError(MultisigErrorCode.INVALID_CONFIG, `Invalid mode '${config.mode}'. Must be 'preconfigured' or 'dynamic'`); } if (!result.valid) { throw new MultisigError(MultisigErrorCode.INVALID_CONFIG, `Invalid MultisigConfig: ${result.errors.join('; ')}`); } return result; } /** * Validate threshold against total weight */ export function validateThreshold(threshold, totalWeight) { if (typeof threshold !== 'number' || threshold < 1) { throw new MultisigError(MultisigErrorCode.INVALID_THRESHOLD, `Threshold must be at least 1 (got ${threshold})`); } if (threshold > totalWeight) { throw new MultisigError(MultisigErrorCode.INVALID_THRESHOLD, `Threshold (${threshold}) cannot exceed total signer weight (${totalWeight})`); } }