/** * Chain State Setup Script * * Prepares a realistic staking environment BEFORE tests run: * 1. Funds test wallets (Anvil #3, #4, #5) with ETH + KRK * 2. Creates active positions at different tax rates * 3. Triggers a recenter to update pool state * 4. Advances time to simulate position age * 5. Takes chain snapshot for test resets */ import { ethers } from 'ethers'; import { readFileSync } from 'fs'; import { join } from 'path'; const RPC_URL = process.env.STACK_RPC_URL ?? 'http://localhost:8545'; // Anvil test accounts (private keys) const DEPLOYER_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // Anvil #0 const MARCUS_KEY = '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6'; // Anvil #3 const SARAH_KEY = '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a'; // Anvil #4 const PRIYA_KEY = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'; // Anvil #5 interface ContractAddresses { Kraiken: string; Stake: string; LiquidityManager: string; } async function loadContracts(): Promise { const deploymentsPath = join(process.cwd(), 'onchain', 'deployments-local.json'); const deploymentsJson = readFileSync(deploymentsPath, 'utf-8'); const deployments = JSON.parse(deploymentsJson); return deployments.contracts; } async function sendRpc(method: string, params: unknown[]): Promise { const resp = await fetch(RPC_URL, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }), }); const data = await resp.json(); if (data.error) throw new Error(`RPC ${method} failed: ${data.error.message}`); return data.result; } async function main() { console.log('[SETUP] Starting chain state preparation...\n'); const provider = new ethers.JsonRpcProvider(RPC_URL); const deployer = new ethers.Wallet(DEPLOYER_KEY, provider); const marcus = new ethers.Wallet(MARCUS_KEY, provider); const sarah = new ethers.Wallet(SARAH_KEY, provider); const priya = new ethers.Wallet(PRIYA_KEY, provider); const addresses = await loadContracts(); console.log('[SETUP] Contract addresses loaded:'); console.log(` - Kraiken: ${addresses.Kraiken}`); console.log(` - Stake: ${addresses.Stake}`); console.log(` - LiquidityManager: ${addresses.LiquidityManager}\n`); // Contract ABIs (minimal required functions) const krkAbi = [ 'function transfer(address to, uint256 amount) returns (bool)', 'function balanceOf(address account) view returns (uint256)', 'function approve(address spender, uint256 amount) returns (bool)', 'function minStake() view returns (uint256)', ]; const stakeAbi = [ 'function snatch(uint256 assets, address receiver, uint32 taxRate, uint256[] calldata positionsToSnatch) returns (uint256)', 'function getPosition(uint256 positionId) view returns (tuple(uint256 share, address owner, uint32 creationTime, uint32 lastTaxTime, uint32 taxRate))', // minStake() is on Kraiken, not Stake 'function nextPositionId() view returns (uint256)', ]; const lmAbi = [ 'function recenter() external returns (bool)', ]; const krk = new ethers.Contract(addresses.Kraiken, krkAbi, deployer); const stake = new ethers.Contract(addresses.Stake, stakeAbi, deployer); const lm = new ethers.Contract(addresses.LiquidityManager, lmAbi, deployer); // Step 1: Fund test wallets with ETH console.log('[STEP 1] Funding test wallets with ETH...'); const ethAmount = ethers.parseEther('100'); // 100 ETH each for (const [name, wallet] of [ ['Marcus', marcus], ['Sarah', sarah], ['Priya', priya], ]) { await sendRpc('anvil_setBalance', [wallet.address, '0x' + ethAmount.toString(16)]); console.log(` ✓ ${name} (${wallet.address}): 100 ETH`); } // Step 2: Transfer KRK from deployer to test wallets console.log('\n[STEP 2] Distributing KRK tokens...'); const krkAmount = ethers.parseEther('1000'); // 1000 KRK each for (const [name, wallet] of [ ['Marcus', marcus], ['Sarah', sarah], ['Priya', priya], ]) { const tx = await krk.transfer(wallet.address, krkAmount); await tx.wait(); const balance = await krk.balanceOf(wallet.address); console.log(` ✓ ${name}: ${ethers.formatEther(balance)} KRK`); } // Step 3: Create staking positions console.log('\n[STEP 3] Creating active staking positions...'); const minStake = await krk.minStake(); console.log(` Minimum stake: ${ethers.formatEther(minStake)} KRK`); // Marcus stakes at LOW tax rate (index 2 = 5% yearly) console.log('\n Creating Marcus position (LOW tax)...'); const marcusAmount = ethers.parseEther('300'); const marcusTaxRate = 2; // 5% yearly (0.0137% daily) const marcusKrk = krk.connect(marcus) as typeof krk; const marcusStake = stake.connect(marcus) as typeof stake; let approveTx = await marcusKrk.approve(addresses.Stake, marcusAmount); await approveTx.wait(); // Explicit nonce to avoid stale nonce cache let nonce = await provider.getTransactionCount(marcus.address); let snatchTx = await marcusStake.snatch(marcusAmount, marcus.address, marcusTaxRate, [], { nonce }); let receipt = await snatchTx.wait(); console.log(` ✓ Marcus position created (300 KRK @ 5% tax)`); // Sarah stakes at MEDIUM tax rate (index 10 = 60% yearly) console.log('\n Creating Sarah position (MEDIUM tax)...'); const sarahAmount = ethers.parseEther('500'); const sarahTaxRate = 10; // 60% yearly (0.1644% daily) const sarahKrk = krk.connect(sarah) as typeof krk; const sarahStake = stake.connect(sarah) as typeof stake; approveTx = await sarahKrk.approve(addresses.Stake, sarahAmount); await approveTx.wait(); nonce = await provider.getTransactionCount(sarah.address); snatchTx = await sarahStake.snatch(sarahAmount, sarah.address, sarahTaxRate, [], { nonce }); receipt = await snatchTx.wait(); console.log(` ✓ Sarah position created (500 KRK @ 60% tax)`); // Step 4: Trigger recenter via deployer console.log('\n[STEP 4] Triggering recenter to update liquidity positions...'); try { const recenterTx = await lm.recenter(); await recenterTx.wait(); console.log(' ✓ Recenter successful'); } catch (error: any) { console.log(` ⚠ Recenter failed (may be expected): ${error.message}`); } // Step 5: Advance time by 1 day console.log('\n[STEP 5] Advancing chain time by 1 day...'); const oneDay = 86400; // seconds await sendRpc('anvil_increaseTime', [oneDay]); await sendRpc('anvil_mine', [1]); console.log(' ✓ Time advanced by 1 day'); // Step 6: Take chain snapshot console.log('\n[STEP 6] Taking chain snapshot for test resets...'); const snapshotId = await sendRpc('evm_snapshot', []); console.log(` ✓ Snapshot ID: ${snapshotId}`); // Verify final state console.log('\n[VERIFICATION] Final chain state:'); const nextPosId = await stake.nextPositionId(); console.log(` - Next position ID: ${nextPosId}`); for (const [name, wallet] of [ ['Marcus', marcus], ['Sarah', sarah], ['Priya', priya], ]) { const balance = await krk.balanceOf(wallet.address); console.log(` - ${name} KRK balance: ${ethers.formatEther(balance)} KRK`); } console.log('\n✅ Chain state setup complete!'); console.log(' Tests can now run against this prepared state.\n'); } main() .then(() => process.exit(0)) .catch((error) => { console.error('\n❌ Setup failed:', error); process.exit(1); });