Skip to content

Commit fe68528

Browse files
committed
feat: add EOA consolidation sending all transactions from client
1 parent c29469d commit fe68528

File tree

1 file changed

+126
-1
lines changed
  • programs/use-cases/consolidation

1 file changed

+126
-1
lines changed

programs/use-cases/consolidation/write.ts

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@ import {
66
finalityCheckpoints,
77
checkSourceValidators,
88
checkTargetValidators,
9+
callReadMethod,
10+
callWriteMethodWithReceipt,
11+
logInfo,
912
} from 'utils';
1013
import { consolidation } from './main.js';
11-
import { Address, Hex, zeroAddress } from 'viem';
14+
import { Address, Hex, hexToBigInt, zeroAddress } from 'viem';
1215
import { checkPubkeys } from 'utils/pubkeys-checks.js';
1316
import {
1417
revokeDelegate,
1518
requestConsolidation,
1619
} from 'features/consolidation.js';
20+
import { getDashboardContract, getVaultHubContract } from 'contracts';
21+
import { getAccount, getPublicClient, getWalletWithAccount } from 'providers';
22+
23+
const CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS =
24+
'0x0000BBdDc7CE488642fb579F8B00f3a590007251';
1725

1826
const consolidationWrite = consolidation
1927
.command('write')
@@ -87,3 +95,120 @@ consolidationWrite
8795
.command('eoa-revoke-delegate')
8896
.description('Revoke delegate for the EOA using EIP-7702')
8997
.action(async () => revokeDelegate());
98+
99+
consolidationWrite
100+
.command('eoa-transactions')
101+
.description(
102+
'Make separate consolidation requests for each source pubkey, increase rewards adjustment',
103+
)
104+
.argument(
105+
'<source_pubkeys>',
106+
'2D array of source validator pubkeys: each inner list will be consolidated into a single target validator',
107+
stringTo2dArray,
108+
)
109+
.argument(
110+
'<target_pubkeys>',
111+
'List of target validator public keys to consolidate into. One target pubkey per group of source pubkeys',
112+
stringToHexArray,
113+
)
114+
.argument('<staking_vault>', 'staking vault address', stringToAddress)
115+
.action(
116+
async (
117+
sourcePubkeys: Hex[][],
118+
targetPubkeys: Hex[],
119+
stakingVault: Address,
120+
) => {
121+
const publicClient = getPublicClient();
122+
const walletClient = getWalletWithAccount();
123+
const account = getAccount();
124+
125+
// 0. Input validation
126+
const sourcePubkeysFlat = sourcePubkeys.flat();
127+
checkPubkeys(sourcePubkeysFlat);
128+
checkPubkeys(targetPubkeys);
129+
if (sourcePubkeys.length !== targetPubkeys.length) {
130+
throw new Error(
131+
'sourcePubkeys and targetPubkeys must have the same length',
132+
);
133+
}
134+
if (stakingVault === zeroAddress) {
135+
throw new Error('stakingVault must be non-zero address');
136+
}
137+
138+
// 1. Check source validators
139+
const finalityCheckpointsInfo = await finalityCheckpoints();
140+
const finalizedEpoch = Number(
141+
finalityCheckpointsInfo.data.finalized.epoch,
142+
);
143+
const sourceValidatorsInfo = await fetchValidatorsInfo(sourcePubkeysFlat);
144+
await checkSourceValidators(sourceValidatorsInfo, finalizedEpoch);
145+
146+
// 2. Check target validators
147+
const targetValidatorsInfo = await fetchValidatorsInfo(targetPubkeys);
148+
await checkTargetValidators(targetValidatorsInfo);
149+
150+
// 3. Onchain checks
151+
const vaultHub = await getVaultHubContract();
152+
const vaultConnection = await callReadMethod(
153+
vaultHub,
154+
'vaultConnection',
155+
[stakingVault],
156+
);
157+
if (
158+
vaultConnection.vaultIndex === 0n ||
159+
vaultConnection.pendingDisconnect
160+
) {
161+
throw new Error('Vault is not connected or is pending disconnect');
162+
}
163+
164+
// 4. Get fee per request
165+
const { data } = await publicClient.call({
166+
to: CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
167+
data: '0x',
168+
blockTag: 'latest',
169+
});
170+
if (!data) throw new Error('Fee read returned empty data');
171+
const feePerRequest = hexToBigInt(data);
172+
173+
// 5. Request consolidation
174+
for (const [pubkeyIndex, targetPubkey] of targetPubkeys.entries()) {
175+
const sourcePubkeysGroup = sourcePubkeys[pubkeyIndex] ?? [];
176+
for (const sourcePubkey of sourcePubkeysGroup) {
177+
if (sourcePubkey == null || targetPubkey == null) {
178+
throw new Error('sourcePubkey and targetPubkey must be defined');
179+
}
180+
181+
const calldata =
182+
`0x${sourcePubkey.slice(2)}${targetPubkey.slice(2)}` as Hex;
183+
const txHash = await walletClient.sendTransaction({
184+
to: CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
185+
data: calldata,
186+
value: feePerRequest,
187+
account: account,
188+
chain: walletClient.chain,
189+
});
190+
191+
logInfo('consolidation request tx hash:', txHash);
192+
193+
const txReceipt = await publicClient.waitForTransactionReceipt({
194+
hash: txHash,
195+
});
196+
logInfo('consolidation request tx receipt:', txReceipt);
197+
}
198+
}
199+
200+
// 6. Increase rewards adjustment
201+
const dashboardContract = getDashboardContract(vaultConnection.owner);
202+
const totalBalance = sourceValidatorsInfo.data.reduce(
203+
(sum, validator) => sum + Number(validator.balance),
204+
0,
205+
);
206+
if (totalBalance > 0) {
207+
await callWriteMethodWithReceipt({
208+
contract: dashboardContract,
209+
methodName: 'increaseRewardsAdjustment',
210+
payload: [BigInt(totalBalance)],
211+
});
212+
}
213+
},
214+
);

0 commit comments

Comments
 (0)