1+ // ABOUTME: Execute fund extraction from all Sentiment V1 contracts after upgrade
2+ // ABOUTME: Handles LToken liquidity and account collateral extraction with batching
3+
4+ const { ethers } = require ( 'ethers' ) ;
5+
6+ const DEPLOYED_ADDRESSES = {
7+ markets : {
8+ USDT : '0x4c8e1656E042A206EEf7e8fcff99BaC667E4623e' ,
9+ USDC : '0x0dDB1eA478F8eF0E22C7706D2903a41E94B1299B' ,
10+ FRAX : '0x2E9963ae673A885b6bfeDa2f80132CE28b784C40' ,
11+ ETH : '0xb190214D5EbAc7755899F2D96E519aa7a5776bEC' ,
12+ WBTC : '0xe520C46d5Dab5bB80aF7Dc8b821f47deB4607DB2' ,
13+ ARB : '0x21202227Bc15276E40d53889Bc83E59c3CccC121' ,
14+ OHM : '0x37E6a0EcB9e8E5D90104590049a0A197E1363b67'
15+ } ,
16+ core : {
17+ Registry : '0x17B07cfBAB33C0024040e7C299f8048F4a49679B'
18+ }
19+ } ;
20+
21+ const MULTISIG = "0x000000000000000000000000000000000000dEaD" ; // TODO: Update with actual multisig
22+
23+ async function extractFunds ( privateKey ) {
24+ const provider = new ethers . JsonRpcProvider ( process . env . ARBITRUM_RPC_URL ) ;
25+ const signer = new ethers . Wallet ( privateKey , provider ) ;
26+
27+ console . log ( '=== Starting Fund Extraction ===' ) ;
28+ console . log ( `Recipient: ${ MULTISIG } ` ) ;
29+ console . log ( `Signer: ${ signer . address } ` ) ;
30+
31+ // Phase 1: Extract LToken liquidity
32+ console . log ( '\n--- Extracting LToken Liquidity ---' ) ;
33+
34+ for ( const [ name , address ] of Object . entries ( DEPLOYED_ADDRESSES . markets ) ) {
35+ try {
36+ const lToken = new ethers . Contract ( address , [
37+ 'function recoverFunds() external returns (uint256)'
38+ ] , signer ) ;
39+
40+ const tx = await lToken . recoverFunds ( ) ;
41+ console . log ( `${ name } recovery tx: ${ tx . hash } ` ) ;
42+ await tx . wait ( ) ;
43+ } catch ( error ) {
44+ console . error ( `Failed to extract from ${ name } :` , error . message ) ;
45+ }
46+ }
47+
48+ // Phase 2: Get all accounts with assets
49+ console . log ( '\n--- Getting Accounts with Assets ---' ) ;
50+
51+ const registry = new ethers . Contract (
52+ DEPLOYED_ADDRESSES . core . Registry ,
53+ [ 'function accounts(uint256) view returns (address)' ] ,
54+ provider
55+ ) ;
56+
57+ // Load accounts from our scan data
58+ const fs = require ( 'fs' ) ;
59+ const scanFiles = fs . readdirSync ( './data' ) . filter ( f => f . includes ( 'positions_final' ) ) ;
60+ if ( scanFiles . length === 0 ) {
61+ throw new Error ( 'No position scan data found. Run smartPositionScan.js first.' ) ;
62+ }
63+
64+ const latestScan = scanFiles . sort ( ) . pop ( ) ;
65+ const scanData = JSON . parse ( fs . readFileSync ( `./data/${ latestScan } ` , 'utf8' ) ) ;
66+ const accountsWithAssets = scanData . positions . map ( p => p . account ) ;
67+
68+ console . log ( `Found ${ accountsWithAssets . length } accounts with assets` ) ;
69+
70+ // Phase 3: Extract from all accounts
71+ console . log ( '\n--- Extracting Account Collateral ---' ) ;
72+
73+ const BATCH_SIZE = 50 ; // Process in batches to avoid gas issues
74+ let processed = 0 ;
75+ let extracted = 0 ;
76+
77+ for ( let i = 0 ; i < accountsWithAssets . length ; i += BATCH_SIZE ) {
78+ const batch = accountsWithAssets . slice ( i , i + BATCH_SIZE ) ;
79+
80+ console . log ( `Processing batch ${ Math . floor ( i / BATCH_SIZE ) + 1 } /${ Math . ceil ( accountsWithAssets . length / BATCH_SIZE ) } ` ) ;
81+
82+ const promises = batch . map ( async ( accountAddress ) => {
83+ try {
84+ const account = new ethers . Contract ( accountAddress , [
85+ 'function recoverFunds() external'
86+ ] , signer ) ;
87+
88+ const tx = await account . recoverFunds ( {
89+ gasLimit : 200000 // Set reasonable gas limit
90+ } ) ;
91+ await tx . wait ( ) ;
92+ return { success : true , account : accountAddress , tx : tx . hash } ;
93+ } catch ( error ) {
94+ return { success : false , account : accountAddress , error : error . message } ;
95+ }
96+ } ) ;
97+
98+ const results = await Promise . allSettled ( promises ) ;
99+
100+ results . forEach ( ( result , idx ) => {
101+ processed ++ ;
102+ if ( result . status === 'fulfilled' && result . value . success ) {
103+ extracted ++ ;
104+ console . log ( `✓ ${ result . value . account } - ${ result . value . tx } ` ) ;
105+ } else {
106+ const error = result . status === 'rejected' ? result . reason : result . value . error ;
107+ console . log ( `✗ ${ batch [ idx ] } - ${ error } ` ) ;
108+ }
109+ } ) ;
110+
111+ console . log ( `Progress: ${ processed } /${ accountsWithAssets . length } processed, ${ extracted } extracted` ) ;
112+
113+ // Brief pause between batches
114+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
115+ }
116+
117+ console . log ( '\n=== Extraction Complete ===' ) ;
118+ console . log ( `Total accounts processed: ${ processed } ` ) ;
119+ console . log ( `Successfully extracted: ${ extracted } ` ) ;
120+ console . log ( `Failed: ${ processed - extracted } ` ) ;
121+ }
122+
123+ // CLI execution
124+ if ( require . main === module ) {
125+ const privateKey = process . argv [ 2 ] ;
126+
127+ if ( ! privateKey ) {
128+ console . log ( 'Usage: node extractFunds.js <private_key>' ) ;
129+ console . log ( 'Example: node extractFunds.js 0xabc...' ) ;
130+ console . log ( `Funds will be sent to hardcoded multisig: ${ MULTISIG } ` ) ;
131+ process . exit ( 1 ) ;
132+ }
133+
134+ extractFunds ( privateKey ) . catch ( console . error ) ;
135+ }
136+
137+ module . exports = { extractFunds } ;
0 commit comments