@wuwei-labs/srsly
Version:
TypeScript SDK for SRSLY
147 lines • 5.33 kB
JavaScript
/**
* @purpose Universal signer handling for @solana/kit and @solana/web3.js compatibility
*
* Accepts signers from both libraries and extracts address strings for use with Codama.
*/
import { createNoopSigner, address } from '@solana/kit';
/**
* Extract address string from any supported signer format
*
* @param signer - Signer in any supported format
* @returns Base58-encoded address string
* @throws Error if signer format is invalid
*
* @example
* ```typescript
* // String address
* const addr1 = getSignerAddress("9WzDXw...");
*
* // web3.js Keypair
* import { Keypair } from '@solana/web3.js';
* const keypair = Keypair.generate();
* const addr2 = getSignerAddress(keypair);
*
* // @solana/kit signer
* import { createNoopSigner, address } from '@solana/kit';
* const kitSigner = createNoopSigner(address("9WzDXw..."));
* const addr3 = getSignerAddress(kitSigner);
* ```
*/
export function getSignerAddress(signer) {
// Handle string address
if (typeof signer === 'string') {
validateAddress(signer);
return address(signer);
}
// Handle object signers
if (signer && typeof signer === 'object') {
// web3.js Keypair/Wallet format (has publicKey.toBase58())
if ('publicKey' in signer &&
signer.publicKey &&
typeof signer.publicKey.toBase58 === 'function') {
const addr = signer.publicKey.toBase58();
validateAddress(addr);
return address(addr);
}
// web3.js PublicKey format (has direct toBase58())
if ('toBase58' in signer && typeof signer.toBase58 === 'function') {
const addr = signer.toBase58();
validateAddress(addr);
return address(addr);
}
// @solana/kit signer format
if ('address' in signer) {
const addr = String(signer.address);
validateAddress(addr);
return address(addr);
}
}
throw new Error('Invalid signer format. Expected:\n' +
' - string address\n' +
' - web3.js Keypair (with publicKey.toBase58())\n' +
' - web3.js PublicKey (with toBase58())\n' +
' - @solana/kit TransactionSigner (with address property)');
}
/**
* Validate that a string is a valid Solana address
*
* Basic validation - checks length only. For full validation,
* use @solana/kit's address() function or web3.js PublicKey constructor.
*
* @param address - Address string to validate
* @throws Error if address format is invalid
*/
export function validateAddress(address) {
if (typeof address !== 'string') {
throw new Error(`Address must be a string, got ${typeof address}`);
}
// Solana addresses are base58-encoded 32-byte values
// Base58 encoding of 32 bytes = 32-44 characters (typically 43-44)
if (address.length < 32 || address.length > 44) {
throw new Error(`Invalid Solana address length: ${address.length}. ` +
`Expected 32-44 characters (base58-encoded 32 bytes). ` +
`Got: "${address}"`);
}
// Check for invalid base58 characters
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]+$/;
if (!base58Regex.test(address)) {
throw new Error(`Invalid Solana address characters. ` +
`Address must be base58-encoded (no 0, O, I, or l characters). ` +
`Got: "${address}"`);
}
}
/**
* Type guard to check if a value is a web3.js signer
*/
export function isWeb3jsSigner(signer) {
return (typeof signer === 'object' &&
signer !== null &&
'publicKey' in signer &&
typeof signer.publicKey?.toBase58 === 'function');
}
/**
* Type guard to check if a value is a @solana/kit signer
*/
export function isKitSigner(signer) {
return typeof signer === 'object' && signer !== null && 'address' in signer;
}
/**
* Type guard to check if a value is a string address
*/
export function isAddressString(signer) {
return typeof signer === 'string';
}
/**
* Convert UniversalSigner to TransactionSigner
*
* Intelligently handles different signer types:
* - If already a TransactionSigner with signing capability → return as-is
* - Otherwise → create noop signer from address (for browser/wallet cases)
*
* This enables the SDK to work in both:
* - Browser/wallet contexts (where you only have addresses, not private keys)
* - CLI/backend contexts (where you have full keypair signers)
*
* @param signer - Signer in any supported format
* @returns TransactionSigner (either real or noop)
*
* @example
* ```typescript
* // CLI/backend - has private key
* const wallet = await createKeyPairSignerFromBytes(secretKey);
* const txSigner = toTransactionSigner(wallet); // Returns wallet as-is
*
* // Browser - only has address
* const walletAddress = "9WzDXw...";
* const txSigner = toTransactionSigner(walletAddress); // Creates noop signer
* ```
*/
export function toTransactionSigner(signer) {
// If already a TransactionSigner with signing capability, use it directly
if (typeof signer === 'object' && signer !== null && 'signMessages' in signer) {
return signer;
}
// Otherwise, extract address and create noop signer (for browser/wallet cases)
return createNoopSigner(getSignerAddress(signer));
}
//# sourceMappingURL=signer.js.map