Skip to main content
Glama
utils.ts9.6 kB
import { hash, num, shortString, validateAndParseAddress } from 'starknet'; import { uint256, constants } from 'starknet'; import { getProvider } from './clients.js'; // Map of network names to chain IDs for cleaner code const CHAIN_IDS = { mainnet: constants.StarknetChainId.SN_MAIN, sepolia: constants.StarknetChainId.SN_SEPOLIA }; /** * Utility functions for Starknet */ export const utils = { /** * Convert a number to a felt (field element) hex string * @param value The number to convert * @returns Hex string representation */ toFelt(value: string | number | bigint): string { return num.toHex(value); }, /** * Convert a felt (field element) to a number * @param felt The felt to convert * @returns BigInt value */ fromFelt(felt: string): bigint { return num.toBigInt(felt); }, /** * Convert a string to a felt (field element) * @param value The string to convert * @returns Felt representation of the string */ stringToFelt(value: string): string { return shortString.encodeShortString(value); }, /** * Convert a felt (field element) to a string * @param felt The felt to convert * @returns Decoded string */ feltToString(felt: string): string { try { return shortString.decodeShortString(felt); } catch (error) { console.error('Error decoding felt to string:', error); return String(felt); // Fallback to simple string conversion } }, /** * Convert a BigInt to a hex string representation of a felt * This is useful for contract addresses returned from contracts * @param value BigInt value to convert * @returns Hex string */ bigintToHex(value: bigint): string { return '0x' + value.toString(16); }, /** * Format a BigInt value to a Starknet-compatible hex address * Useful for contract addresses returned from function calls * @param value BigInt value or string to format as address * @returns Formatted address string */ formatAddress(value: bigint | string): string { try { if (typeof value === 'string') { return value.startsWith('0x') ? value : `0x${value}`; } return '0x' + value.toString(16).padStart(64, '0'); } catch (error) { throw new Error(`Invalid address value: ${value}`); } }, /** * Compute the starknet keccak hash of a string * @param value The string to hash * @returns Hash as string */ hashMessage(value: string): string { return hash.starknetKeccak(value).toString(); }, /** * Check if an address is valid * @param address The address to check * @returns True if valid */ isValidAddress(address: string): boolean { try { validateAndParseAddress(address); return true; } catch (error) { return false; } }, /** * Resolve a Starknet ID name to an address or return the address if already valid * @param nameOrAddress Starknet name (starknet.id) or address * @param network Network name (mainnet, sepolia) * @returns The resolved address * @throws Error if name cannot be resolved or address is invalid */ async resolveNameOrAddress(nameOrAddress: string, network = 'mainnet'): Promise<string> { // If it's already a valid address, return it if (this.isValidAddress(nameOrAddress)) { return nameOrAddress; } try { // Import StarknetIdNavigator dynamically to avoid circular dependencies const { StarknetIdNavigator, utils: starknetIdUtils } = await import('starknetid.js'); // Get provider const provider = getProvider(network); // Get chain ID from map, defaulting to mainnet if not found const chainId = CHAIN_IDS[network as keyof typeof CHAIN_IDS] || CHAIN_IDS.mainnet; // Create StarknetIdNavigator instance const navigator = new StarknetIdNavigator(provider, chainId); // Check if it's a valid Starknet domain if (starknetIdUtils.isStarkDomain(nameOrAddress)) { // Make sure it has .stark suffix const name = nameOrAddress.endsWith('.stark') ? nameOrAddress : `${nameOrAddress}.stark`; const address = await navigator.getAddressFromStarkName(name); if (!address || address === '0x0') { throw new Error(`Could not resolve Starknet ID: ${name}`); } return address; } throw new Error(`Invalid address or unresolvable Starknet ID: ${nameOrAddress}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Error resolving name or address: ${errorMessage}`); } }, /** * Calculate a contract address * @param salt A random salt * @param classHash The class hash * @param constructorCalldata The constructor calldata * @param deployerAddress The deployer address * @returns Calculated contract address */ calculateContractAddress( salt: string, classHash: string, constructorCalldata: string[] = [], deployerAddress?: string ): string { try { return hash.calculateContractAddressFromHash( salt, classHash, constructorCalldata, deployerAddress || '0x0' ); } catch (error) { throw new Error(`Error calculating contract address: ${(error as Error).message}`); } }, /** * Split a uint256 into low and high parts * @param value The uint256 value * @returns Object with low and high parts as hex strings */ splitUint256(value: bigint): { low: string; high: string } { try { // Use StarknetJS's uint256.bnToUint256 for proper conversion const result = uint256.bnToUint256(value); // Convert the result to the expected string format return { low: result.low.toString(), high: result.high.toString() }; } catch (error) { // Fallback to manual calculation if StarknetJS function fails const low = '0x' + (value & BigInt('0xffffffffffffffffffffffffffffffff')).toString(16); const high = '0x' + ((value >> BigInt(128)) & BigInt('0xffffffffffffffffffffffffffffffff')).toString(16); return { low, high }; } }, /** * Combine low and high parts into a uint256 * @param low The low part * @param high The high part * @returns Combined BigInt value */ combineUint256(low: string, high: string): bigint { try { return uint256.uint256ToBN({ low, high }); } catch (error) { // Fallback to manual calculation if StarknetJS function fails const lowBigInt = BigInt(low); const highBigInt = BigInt(high); return lowBigInt + (highBigInt << BigInt(128)); } }, /** * Convert a BigInt to a Starknet uint256 representation * Important when writing to contracts that expect uint256 * @param value The BigInt value to convert * @returns Object with low and high parts */ bigintToUint256(value: bigint): { low: string; high: string } { const result = uint256.bnToUint256(value); // Convert the result to the expected string format return { low: result.low.toString(), high: result.high.toString() }; }, /** * Convert a Starknet uint256 to a BigInt * Useful when reading uint256 values from contracts * @param uint The uint256 object or components * @returns BigInt value */ uint256ToBigint(uint: { low: string; high: string } | string[]): bigint { if (Array.isArray(uint)) { return uint256.uint256ToBN({ low: uint[0], high: uint[1] }); } return uint256.uint256ToBN(uint); }, /** * Format a numeric value returned from a contract * This will determine the appropriate format based on the expected type * @param value The value returned from the contract * @param type The expected type ('felt', 'uint256', 'address', 'string') * @returns Formatted value according to the specified type */ formatContractValue( value: any, type: 'felt' | 'uint256' | 'address' | 'string' = 'felt' ): string | bigint | { low: string; high: string } { try { if (type === 'uint256') { // If already uint256 format with low/high, return as is if (typeof value === 'object' && 'low' in value && 'high' in value) { return value; } // If array with two elements (low, high), convert to uint256 object if (Array.isArray(value) && value.length === 2) { return { low: value[0].toString(), high: value[1].toString() }; } // If bigint or can be converted to bigint, convert to uint256 return this.bigintToUint256(BigInt(value.toString())); } else if (type === 'address') { // Format as proper Starknet address return this.formatAddress(value.toString()); } else if (type === 'string') { // Try to convert to string if it's encoded as a felt try { if (typeof value === 'bigint' || (!isNaN(Number(value)) && /^[0-9]+$/.test(value.toString())) || (typeof value === 'string' && value.startsWith('0x'))) { return this.feltToString(this.toFelt(value)); } return value.toString(); } catch { return value.toString(); } } else { // Default felt handling return this.toFelt(value); } } catch (error) { console.error(`Error formatting contract value (${type}):`, error); return String(value); // Fallback to simple string conversion } } };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mcpdotdirect/starknet-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server