Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
"lint:fix": "npx nx run-many --target=lint --all -- --fix",
"format:check": "npx nx format:check --all",
"test:e2e": "npx jest --clearCache --config ./jest.e2e.config.ts && LOG_LEVEL=${LOG_LEVEL:-silent} dotenvx run --env-file=.env -- jest --runInBand --detectOpenHandles --forceExit --config ./jest.e2e.config.ts --testTimeout=50000000 --runTestsByPath packages/e2e/src/e2e.spec.ts",
"test:e2e:lite-mainnet": "RUN_LITE_MAINNET_E2E=1 NETWORK=naga LOG_LEVEL=${LOG_LEVEL:-silent} dotenvx run --env-file=.env -- jest --runInBand --detectOpenHandles --forceExit --config ./jest.e2e.config.ts --testTimeout=50000000 --runTestsByPath packages/e2e/src/lite/mainnet-lite.spec.ts",
"test:target": "npx jest --clearCache --config ./jest.e2e.config.ts && LOG_LEVEL=${LOG_LEVEL:-silent} dotenvx run --env-file=.env -- jest --runInBand --detectOpenHandles --forceExit --config ./jest.e2e.config.ts --testTimeout=50000000",
"test:e2e:ci": "npx jest --clearCache --config ./jest.e2e.config.ts && LOG_LEVEL=${LOG_LEVEL:-silent} npx jest --runInBand --detectOpenHandles --forceExit --config ./jest.e2e.config.ts --testTimeout=50000000 --runTestsByPath",
"pretest:e2e": "pnpm run generate:lit-actions",
"pretest:e2e:ci": "pnpm run generate:lit-actions",
"e2e:money-back": "dotenvx run --env-file=.env -- tsx tools/money-back.ts",
"pretest:custom": "pnpm run generate:lit-actions",
"test:health": "LOG_LEVEL=${LOG_LEVEL:-silent} dotenvx run --env-file=.env -- tsx packages/e2e/src/health/index.ts",
"ci:health": "LOG_LEVEL=${LOG_LEVEL:-silent} tsx packages/e2e/src/health/index.ts"
Expand Down
8 changes: 8 additions & 0 deletions packages/constants/src/lib/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,11 @@ export const LIT_RPC = {
* More info: https://app.conduit.xyz/published/view/chronicle-yellowstone-testnet-9qgmzfcohk
*/
CHRONICLE_YELLOWSTONE: 'https://yellowstone-rpc.litprotocol.com',

/**
* Lit Chain mainnet RPC endpoint.
*/
LIT_CHAIN: 'https://lit-chain-rpc.litprotocol.com/',
} as const;

export type LIT_RPC_TYPE = ConstantKeys<typeof LIT_RPC>;
Expand All @@ -1067,6 +1072,7 @@ export const LIT_EVM_CHAINS = LIT_CHAINS;
* Represents the Lit Network constants.
*/
export const LIT_NETWORK = {
Naga: 'naga',
NagaDev: 'naga-dev',
NagaTest: 'naga-test',
Custom: 'custom',
Expand All @@ -1089,6 +1095,7 @@ export type LIT_NETWORK_VALUES = ConstantValues<typeof LIT_NETWORK>;
* A mapping of network names to their corresponding RPC URLs.
*/
export const RPC_URL_BY_NETWORK: Record<LIT_NETWORK_VALUES, LIT_RPC_VALUES> = {
[LIT_NETWORK.Naga]: LIT_RPC.LIT_CHAIN,
[LIT_NETWORK.NagaDev]: LIT_RPC.CHRONICLE_YELLOWSTONE,
[LIT_NETWORK.NagaTest]: LIT_RPC.CHRONICLE_YELLOWSTONE,
[LIT_NETWORK.Custom]: LIT_RPC.LOCAL_ANVIL,
Expand Down Expand Up @@ -1227,6 +1234,7 @@ export const LOCAL_STORAGE_KEYS = {
* loaded from the chain during initialization
*/
export const LIT_NETWORKS: Record<LIT_NETWORK_VALUES, string[]> = {
[LIT_NETWORK.Naga]: [],
[LIT_NETWORK.NagaDev]: [],
[LIT_NETWORK.NagaTest]: [],
[LIT_NETWORK.Custom]: [],
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts/src/utils/abi-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import type { NetworkCache } from '../types/contracts';
import { toFunctionSignature } from 'viem/utils';
import { Interface } from 'ethers';
import { utils } from 'ethers';

/**
* Represents a single contract method with its metadata
Expand Down Expand Up @@ -51,7 +51,7 @@ export function extractAbiMethods(
ABI.forEach((abiItem) => {
if (abiItem.type === 'function' && methodNames.includes(abiItem.name)) {
try {
const iface = new Interface(ABI);
const iface = new utils.Interface(ABI);

// Special case for safeTransferFrom - use the basic version to avoid ambiguity
let functionFragment;
Expand Down
12 changes: 11 additions & 1 deletion packages/e2e/src/helper/createTestAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { AuthContext } from '../types';
import { TestEnv } from './createTestEnv';
import { fundAccount } from './fundAccount';
import { persistGeneratedAccount } from './generated-accounts';
import { getOrCreatePkp } from './pkp-utils';

type CreateTestAccountOpts = {
Expand Down Expand Up @@ -49,8 +50,17 @@ export async function createTestAccount(
): Promise<CreateTestAccountResult> {
console.log(`--- ${`[${opts.label}]`} Creating test account ---`);
// 1. store result
const privateKey = generatePrivateKey();
persistGeneratedAccount({
label: `createTestAccount:${opts.label}`,
privateKey,
network:
typeof testEnv.networkModule.getNetworkName === 'function'
? testEnv.networkModule.getNetworkName()
: process.env['NETWORK'],
});
let person: CreateTestAccountResult = {
account: privateKeyToAccount(generatePrivateKey()),
account: privateKeyToAccount(privateKey),
pkp: undefined,
eoaAuthContext: undefined,
pkpAuthContext: undefined,
Expand Down
28 changes: 25 additions & 3 deletions packages/e2e/src/helper/createTestEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,23 @@ export const CONFIG = {
nativeFundingAmount: '0.01',
ledgerDepositAmount: '0.01',
sponsorshipLimits: {
totalMaxPriceInWei: '10000000000000000',
userMaxPrice: 10000000000000000n,
// The mainnet payment delegation flow uses this as the per-request budget and
// must be large enough to cover the minimum estimated price for a PKP sign.
totalMaxPriceInWei: '60000000000000000000',
userMaxPrice: 60000000000000000000n,
},
},
};

const NAGA_MAINNET_NETWORK_FUNDING_AMOUNT =
process.env['NAGA_MAINNET_NETWORK_FUNDING_AMOUNT'] ?? '20';
const NAGA_PROTO_NETWORK_FUNDING_AMOUNT =
process.env['NAGA_PROTO_NETWORK_FUNDING_AMOUNT'] ?? '0.01';
const NAGA_MAINNET_LEDGER_DEPOSIT_AMOUNT =
process.env['NAGA_MAINNET_LEDGER_DEPOSIT_AMOUNT'] ?? '60';
const NAGA_PROTO_LEDGER_DEPOSIT_AMOUNT =
process.env['NAGA_PROTO_LEDGER_DEPOSIT_AMOUNT'] ?? '0.01';

export type TestEnvs = {
address: `0x${string}`;
networkModule: LitNetworkModule;
Expand Down Expand Up @@ -125,7 +136,18 @@ export const createTestEnv = async (envVars: EnvVars): Promise<TestEnv> => {
networkModule = applyRpcOverride(
envVars.network === 'naga-proto' ? nagaProto : naga
);
config = CONFIG.MAINNET;
config =
envVars.network === 'naga'
? {
...CONFIG.MAINNET,
nativeFundingAmount: NAGA_MAINNET_NETWORK_FUNDING_AMOUNT,
ledgerDepositAmount: NAGA_MAINNET_LEDGER_DEPOSIT_AMOUNT,
}
: {
...CONFIG.MAINNET,
nativeFundingAmount: NAGA_PROTO_NETWORK_FUNDING_AMOUNT,
ledgerDepositAmount: NAGA_PROTO_LEDGER_DEPOSIT_AMOUNT,
};
break;
}
default: {
Expand Down
81 changes: 81 additions & 0 deletions packages/e2e/src/helper/generated-accounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import fs from 'node:fs';
import path from 'node:path';
import { randomBytes } from 'node:crypto';
import { privateKeyToAccount } from 'viem/accounts';

export type GeneratedAccountRecord = {
runId: string;
createdAt: string;
network?: string;
label: string;
address: `0x${string}`;
privateKey: `0x${string}`;
};

const DEFAULT_E2E_DIR = path.resolve(process.cwd(), '.e2e');
const DEFAULT_ACCOUNTS_FILE = path.join(DEFAULT_E2E_DIR, 'generated-accounts.jsonl');

export const E2E_RUN_ID: string =
process.env['E2E_RUN_ID'] ??
`${Date.now()}-${process.pid}-${randomBytes(4).toString('hex')}`;

export function getGeneratedAccountsFilePath(): string {
return process.env['E2E_GENERATED_ACCOUNTS_FILE'] ?? DEFAULT_ACCOUNTS_FILE;
}

export function persistGeneratedAccount(params: {
label: string;
privateKey: `0x${string}`;
network?: string;
}): GeneratedAccountRecord {
const filePath = getGeneratedAccountsFilePath();
fs.mkdirSync(path.dirname(filePath), { recursive: true });

const account = privateKeyToAccount(params.privateKey);
const record: GeneratedAccountRecord = {
runId: E2E_RUN_ID,
createdAt: new Date().toISOString(),
network: params.network,
label: params.label,
address: account.address,
privateKey: params.privateKey,
};

fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`, {
encoding: 'utf8',
});

// Best-effort: restrict permissions on *nix.
try {
fs.chmodSync(filePath, 0o600);
} catch {
// Ignore (e.g. Windows or filesystem constraints).
}

return record;
}

export function readGeneratedAccounts(params?: {
filePath?: string;
runId?: string;
}): GeneratedAccountRecord[] {
const filePath = params?.filePath ?? getGeneratedAccountsFilePath();
if (!fs.existsSync(filePath)) return [];

const runId = params?.runId;
const lines = fs.readFileSync(filePath, 'utf8').split('\n');
const records: GeneratedAccountRecord[] = [];

for (const line of lines) {
if (!line.trim()) continue;
try {
const parsed = JSON.parse(line) as GeneratedAccountRecord;
if (runId && parsed.runId !== runId) continue;
records.push(parsed);
} catch {
// Ignore malformed lines (best-effort persistence).
}
}

return records;
}
97 changes: 79 additions & 18 deletions packages/e2e/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { createLitClient, utils as litUtils } from '@lit-protocol/lit-client';
import type { NagaLocalModule } from '@lit-protocol/networks';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { persistGeneratedAccount } from './helper/generated-accounts';
import {
NetworkName,
NetworkNameSchema,
Expand Down Expand Up @@ -43,8 +44,17 @@ type LogLevel = z.infer<typeof LogLevelSchema>;
const LIVE_NETWORK_FUNDING_AMOUNT = '0.01';
const LOCAL_NETWORK_FUNDING_AMOUNT = '1';
const LIVE_NETWORK_LEDGER_DEPOSIT_AMOUNT = '1';
const MAINNET_NETWORK_FUNDING_AMOUNT = '0.01';
const MAINNET_LEDGER_DEPOSIT_AMOUNT = '0.01';
// 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 EVE_VALIDATION_IPFS_CID =
'QmcxWmo3jefFsPUnskJXYBwsJYtiFuMAH1nDQEs99AwzDe';
Expand Down Expand Up @@ -92,13 +102,22 @@ async function initInternal(
* Prepare accounts for testing
* ====================================
*/
const localMasterAccount = privateKeyToAccount(
process.env['LOCAL_MASTER_ACCOUNT'] as `0x${string}`
);
const liveMasterAccount = privateKeyToAccount(
process.env['LIVE_MASTER_ACCOUNT'] as `0x${string}`
);
const aliceViemAccount = privateKeyToAccount(generatePrivateKey());
const networkForPersistence = (network ?? process.env['NETWORK']) as
| string
| undefined;

const alicePrivateKeyEnv = process.env['E2E_ALICE_PRIVATE_KEY'] as
| `0x${string}`
| undefined;
const alicePrivateKey = alicePrivateKeyEnv ?? generatePrivateKey();
if (!alicePrivateKeyEnv) {
persistGeneratedAccount({
label: 'init:alice',
privateKey: alicePrivateKey,
network: networkForPersistence,
});
}
const aliceViemAccount = privateKeyToAccount(alicePrivateKey);
const aliceViemAccountAuthData = await ViemAccountAuthenticator.authenticate(
aliceViemAccount
);
Expand Down Expand Up @@ -187,16 +206,36 @@ async function initInternal(
}

const isLocal = networkType === 'local';
const isMainnet =
resolvedNetworkName === 'naga' || resolvedNetworkName === 'naga-proto';
const masterAccount = isLocal ? localMasterAccount : liveMasterAccount;
const isNagaMainnet = resolvedNetworkName === 'naga';
const isNagaProto = resolvedNetworkName === 'naga-proto';
const masterAccountEnvVar = isLocal
? 'LOCAL_MASTER_ACCOUNT'
: 'LIVE_MASTER_ACCOUNT';
const masterPrivateKey = process.env[masterAccountEnvVar] as
| `0x${string}`
| undefined;

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
: isMainnet
? MAINNET_NETWORK_FUNDING_AMOUNT
: isNagaMainnet
? NAGA_MAINNET_NETWORK_FUNDING_AMOUNT
: isNagaProto
? NAGA_PROTO_NETWORK_FUNDING_AMOUNT
: LIVE_NETWORK_FUNDING_AMOUNT;
const ledgerDepositAmount = isMainnet
? MAINNET_LEDGER_DEPOSIT_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
Expand All @@ -210,12 +249,34 @@ async function initInternal(
let eveViemAccount: ViemAccount | undefined;

if (mode === 'full') {
bobViemAccount = privateKeyToAccount(generatePrivateKey());
const bobPrivateKeyEnv = process.env['E2E_BOB_PRIVATE_KEY'] as
| `0x${string}`
| undefined;
const bobPrivateKey = bobPrivateKeyEnv ?? generatePrivateKey();
if (!bobPrivateKeyEnv) {
persistGeneratedAccount({
label: 'init:bob',
privateKey: bobPrivateKey,
network: networkForPersistence,
});
}
bobViemAccount = privateKeyToAccount(bobPrivateKey);
bobViemAccountAuthData = await ViemAccountAuthenticator.authenticate(
bobViemAccount
);

eveViemAccount = privateKeyToAccount(generatePrivateKey());
const evePrivateKeyEnv = process.env['E2E_EVE_PRIVATE_KEY'] as
| `0x${string}`
| undefined;
const evePrivateKey = evePrivateKeyEnv ?? generatePrivateKey();
if (!evePrivateKeyEnv) {
persistGeneratedAccount({
label: 'init:eve',
privateKey: evePrivateKey,
network: networkForPersistence,
});
}
eveViemAccount = privateKeyToAccount(evePrivateKey);

await fundAccount(bobViemAccount, masterAccount, networkModule, {
ifLessThan: fundingAmount,
Expand Down
Loading
Loading