UNPKG

@lit-protocol/e2e

Version:

Lit Protocol E2E testing package for running comprehensive integration tests

363 lines 15.2 kB
import { createAuthManager, storagePlugins, ViemAccountAuthenticator, } from '@lit-protocol/auth'; import { createLitClient, utils as litUtils } from '@lit-protocol/lit-client'; import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; import { persistGeneratedAccount } from './helper/generated-accounts'; import { NetworkNameSchema, resolveNetwork, } from './helper/network'; import { z } from 'zod'; import { fundAccount } from './helper/fundAccount'; import { getOrCreatePkp } from './helper/pkp-utils'; // import { createPkpAuthContext } from './helper/auth-contexts'; const SupportedNetworkSchema = z.enum([ 'naga-dev', 'naga-test', 'naga-local', 'naga-staging', 'naga-proto', 'naga', ]); const LogLevelSchema = z.enum(['silent', 'info', 'debug']); // Configurations const LOCAL_NETWORK_FUNDING_AMOUNT = '1'; const LIVE_NETWORK_LEDGER_DEPOSIT_AMOUNT = process.env['LIVE_NETWORK_LEDGER_DEPOSIT_AMOUNT'] ?? '5'; // Mainnet-style networks have separate knobs so `naga-proto` can remain cheap while // `naga` can be configured independently. const NAGA_MAINNET_NETWORK_FUNDING_AMOUNT = process.env['NAGA_MAINNET_NETWORK_FUNDING_AMOUNT'] ?? '0.01'; const NAGA_PROTO_NETWORK_FUNDING_AMOUNT = process.env['NAGA_PROTO_NETWORK_FUNDING_AMOUNT'] ?? '0.01'; const NAGA_MAINNET_LEDGER_DEPOSIT_AMOUNT = // Default stays low to avoid stranding real mainnet funds; override as needed. process.env['NAGA_MAINNET_LEDGER_DEPOSIT_AMOUNT'] ?? '0.01'; const NAGA_PROTO_LEDGER_DEPOSIT_AMOUNT = process.env['NAGA_PROTO_LEDGER_DEPOSIT_AMOUNT'] ?? '0.01'; const LIVE_NETWORK_FUNDING_AMOUNT = process.env['LIVE_NETWORK_FUNDING_AMOUNT'] ?? '5'; const EVE_VALIDATION_IPFS_CID = 'QmcxWmo3jefFsPUnskJXYBwsJYtiFuMAH1nDQEs99AwzDe'; async function initInternal(mode, network, logLevel) { /** * ==================================== * Prepare accounts for testing * ==================================== */ const networkForPersistence = (network ?? process.env['NETWORK']); const alicePrivateKeyEnv = process.env['E2E_ALICE_PRIVATE_KEY']; const alicePrivateKey = alicePrivateKeyEnv ?? generatePrivateKey(); if (!alicePrivateKeyEnv) { persistGeneratedAccount({ label: 'init:alice', privateKey: alicePrivateKey, network: networkForPersistence, }); } const aliceViemAccount = privateKeyToAccount(alicePrivateKey); const aliceViemAccountAuthData = await ViemAccountAuthenticator.authenticate(aliceViemAccount); /** * ==================================== * Environment settings * ==================================== */ const networkInput = network ?? process.env['NETWORK']; const _logLevel = logLevel ?? process.env['LOG_LEVEL']; if (_logLevel) { process.env['LOG_LEVEL'] = _logLevel; } if (!networkInput) { throw new Error(`❌ Network not specified. Please set the NETWORK environment variable or pass a network parameter. Available networks: ${NetworkNameSchema.options.join(', ')}`); } /** * ==================================== * Network configuration and setup * ❗️ If it's on local chain, we will fund it with the first Anvil account. * ❗️ If it's on live chain, we will fund it with the master account. (set in the .env file) * ==================================== */ const parsedNetwork = NetworkNameSchema.parse(networkInput); const rpcOverrideEnvVar = parsedNetwork === 'naga' || parsedNetwork === 'naga-proto' ? 'LIT_MAINNET_RPC_URL' : 'LIT_YELLOWSTONE_PRIVATE_RPC_URL'; const rpcOverride = process.env[rpcOverrideEnvVar]; const resolvedNetworkBase = await resolveNetwork({ network: parsedNetwork, rpcUrlOverride: rpcOverride, }); let { name: resolvedNetworkName, type: networkType } = resolvedNetworkBase; let networkModule = resolvedNetworkBase.networkModule; if (resolvedNetworkName === 'naga-local') { const localContextPath = process.env['NAGA_LOCAL_CONTEXT_PATH']; if (localContextPath) { const supportsLocalContext = (module) => !!module && typeof module.withLocalContext === 'function'; if (supportsLocalContext(networkModule)) { const localContextName = process.env['NETWORK']; console.log('✅ Loading naga-local signatures from NAGA_LOCAL_CONTEXT_PATH:', localContextPath); networkModule = await networkModule.withLocalContext({ networkContextPath: localContextPath, networkName: localContextName, }); } else { console.warn('⚠️ NAGA_LOCAL_CONTEXT_PATH is set but nagaLocal.withLocalContext is unavailable in the current networks build.'); } } } const resolvedNetwork = { ...resolvedNetworkBase, networkModule, }; console.log('✅ Using network:', resolvedNetworkName); console.log('✅ Using log level:', _logLevel); if (rpcOverride) { console.log(`✅ Using RPC override (${rpcOverrideEnvVar}):`, rpcOverride); } const isLocal = networkType === 'local'; const isNagaMainnet = resolvedNetworkName === 'naga'; const isNagaProto = resolvedNetworkName === 'naga-proto'; const liveMasterKeyOverrides = { naga: 'LIVE_MASTER_ACCOUNT_NAGA', 'naga-dev': 'LIVE_MASTER_ACCOUNT_NAGA_DEV', 'naga-test': 'LIVE_MASTER_ACCOUNT_NAGA_TEST', 'naga-staging': 'LIVE_MASTER_ACCOUNT_NAGA_STAGING', }; const overrideEnvVar = liveMasterKeyOverrides[resolvedNetworkName]; const masterAccountEnvVar = isLocal ? 'LOCAL_MASTER_ACCOUNT' : overrideEnvVar && process.env[overrideEnvVar] ? overrideEnvVar : 'LIVE_MASTER_ACCOUNT'; const masterPrivateKey = process.env[masterAccountEnvVar]; if (!masterPrivateKey) { throw new Error(`❌ ${masterAccountEnvVar} is not set (expected a 0x-prefixed private key; required for NETWORK=${resolvedNetworkName}).`); } const masterAccount = privateKeyToAccount(masterPrivateKey); // Keep existing API shape: `localMasterAccount` is the sponsor account used by this run // (LOCAL on local networks, LIVE on live networks). const localMasterAccount = masterAccount; const fundingAmount = isLocal ? LOCAL_NETWORK_FUNDING_AMOUNT : isNagaMainnet ? NAGA_MAINNET_NETWORK_FUNDING_AMOUNT : isNagaProto ? NAGA_PROTO_NETWORK_FUNDING_AMOUNT : LIVE_NETWORK_FUNDING_AMOUNT; const ledgerDepositAmount = isNagaMainnet ? NAGA_MAINNET_LEDGER_DEPOSIT_AMOUNT : isNagaProto ? NAGA_PROTO_LEDGER_DEPOSIT_AMOUNT : LIVE_NETWORK_LEDGER_DEPOSIT_AMOUNT; // Fund accounts sequentially to avoid nonce conflicts with same sponsor await fundAccount(aliceViemAccount, masterAccount, networkModule, { ifLessThan: fundingAmount, thenFund: fundingAmount, }); let bobViemAccount; let bobViemAccountAuthData; let eveViemAccount; if (mode === 'full') { const bobPrivateKeyEnv = process.env['E2E_BOB_PRIVATE_KEY']; const bobPrivateKey = bobPrivateKeyEnv ?? generatePrivateKey(); if (!bobPrivateKeyEnv) { persistGeneratedAccount({ label: 'init:bob', privateKey: bobPrivateKey, network: networkForPersistence, }); } bobViemAccount = privateKeyToAccount(bobPrivateKey); bobViemAccountAuthData = await ViemAccountAuthenticator.authenticate(bobViemAccount); const evePrivateKeyEnv = process.env['E2E_EVE_PRIVATE_KEY']; const evePrivateKey = evePrivateKeyEnv ?? generatePrivateKey(); if (!evePrivateKeyEnv) { persistGeneratedAccount({ label: 'init:eve', privateKey: evePrivateKey, network: networkForPersistence, }); } eveViemAccount = privateKeyToAccount(evePrivateKey); await fundAccount(bobViemAccount, masterAccount, networkModule, { ifLessThan: fundingAmount, thenFund: fundingAmount, }); await fundAccount(eveViemAccount, masterAccount, networkModule, { ifLessThan: fundingAmount, thenFund: fundingAmount, }); } /** * ==================================== * Initialise the LitClient * ==================================== */ const litClient = await createLitClient({ network: networkModule }); /** * ==================================== * (Master) Initialise Payment Manager * ==================================== */ const masterPaymentManager = await litClient.getPaymentManager({ account: masterAccount, }); const masterPaymentBalance = await masterPaymentManager.getBalance({ userAddress: masterAccount.address, }); console.log('✅ Master Payment Balance:', masterPaymentBalance); async function masterDepositForUser(userAddress) { await masterPaymentManager.depositForUser({ userAddress: userAddress, amountInLitkey: ledgerDepositAmount, }); console.log(`✅ New ${userAddress} Ledger Balance:`, await masterPaymentManager.getBalance({ userAddress: userAddress })); } /** * ==================================== * Initialise the AuthManager * ==================================== */ const authManager = createAuthManager({ storage: storagePlugins.localStorageNode({ appName: 'my-local-testing-app', networkName: resolvedNetworkName, storagePath: './.e2e/lit-auth-local', }), }); const createAliceEoaAuthContext = () => authManager.createEoaAuthContext({ config: { account: aliceViemAccount, }, authConfig: { statement: 'I authorize the Lit Protocol to execute this Lit Action.', domain: 'example.com', resources: [ ['lit-action-execution', '*'], ['pkp-signing', '*'], ['access-control-condition-decryption', '*'], ], capabilityAuthSigs: [], expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(), }, litClient: litClient, }); const aliceViemAccountPkp = await getOrCreatePkp(litClient, aliceViemAccountAuthData, aliceViemAccount); if (mode === 'fast') { await masterDepositForUser(aliceViemAccount.address); await masterDepositForUser(aliceViemAccountPkp.ethAddress); const aliceEoaAuthContext = await createAliceEoaAuthContext(); console.log('✅ Initialised components (fast)'); const baseResult = { litClient, authManager, localMasterAccount, aliceViemAccount, aliceViemAccountAuthData, aliceViemAccountPkp, aliceEoaAuthContext, masterDepositForUser, resolvedNetwork, }; return baseResult; } if (!bobViemAccount || !bobViemAccountAuthData || !eveViemAccount) { throw new Error('❌ Failed to prepare accounts for full init'); } /** * ==================================== * Get or create PKPs for Alice and Bob * ==================================== */ const bobViemAccountPkp = await getOrCreatePkp(litClient, bobViemAccountAuthData, bobViemAccount); // Use custom auth to create a PKP for Eve const uniqueDappName = 'e2e-test-dapp'; const authMethodConfig = litUtils.generateUniqueAuthMethodType({ uniqueDappName: uniqueDappName, }); const eveCustomAuthData = litUtils.generateAuthData({ uniqueDappName: uniqueDappName, uniqueAuthMethodType: authMethodConfig.bigint, userId: 'eve', }); const { pkpData } = await litClient.mintWithCustomAuth({ account: eveViemAccount, authData: eveCustomAuthData, scope: 'sign-anything', validationIpfsCid: EVE_VALIDATION_IPFS_CID, }); const eveViemAccountPkp = { ...pkpData.data, tokenId: pkpData.data.tokenId, }; // Making sure all signers have sufficient ledger balance before calling the signSessionKey endpoint await masterDepositForUser(aliceViemAccount.address); await masterDepositForUser(bobViemAccount.address); await masterDepositForUser(aliceViemAccountPkp.ethAddress); await masterDepositForUser(bobViemAccountPkp.ethAddress); await masterDepositForUser(eveViemAccount.address); await masterDepositForUser(eveViemAccountPkp.ethAddress); /** * ==================================== * Create the auth context * ==================================== */ const aliceEoaAuthContext = await createAliceEoaAuthContext(); console.log('✅ Initialised components'); /** * ==================================== * Create PKP auth context * ==================================== */ const alicePkpAuthContext = await authManager.createPkpAuthContext({ authData: aliceViemAccountAuthData, pkpPublicKey: aliceViemAccountPkp.pubkey, authConfig: { resources: [ ['pkp-signing', '*'], ['lit-action-execution', '*'], ['access-control-condition-decryption', '*'], ], // 30m expiration expiration: new Date(Date.now() + 1000 * 60 * 30).toISOString(), }, litClient: litClient, }); const alicePkpViemAccount = await litClient.getPkpViemAccount({ pkpPublicKey: aliceViemAccountPkp.pubkey, authContext: alicePkpAuthContext, chainConfig: networkModule.getChainConfig(), }); await fundAccount(alicePkpViemAccount, masterAccount, networkModule, { ifLessThan: fundingAmount, thenFund: fundingAmount, }); /** * ==================================== * Depositing to Lit Ledger for different accounts * ==================================== */ await masterDepositForUser(alicePkpViemAccount.address); const baseResult = { litClient, authManager, localMasterAccount, aliceViemAccount, aliceViemAccountAuthData, aliceViemAccountPkp, aliceEoaAuthContext, masterDepositForUser, resolvedNetwork, }; const fullResult = { ...baseResult, bobViemAccount, bobViemAccountAuthData, bobViemAccountPkp, alicePkpAuthContext, eveViemAccount, eveCustomAuthData, eveViemAccountPkp, eveValidationIpfsCid: EVE_VALIDATION_IPFS_CID, }; return fullResult; } export const init = async (network, logLevel) => { return initInternal('full', network, logLevel); }; export const initFast = async (network, logLevel) => { return initInternal('fast', network, logLevel); }; //# sourceMappingURL=init.js.map