@aztec/archiver 3.0.0-nightly.20251210 → 3.0.0-nightly.20251211
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/archiver/archiver.d.ts +9 -6
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +28 -10
- package/dest/archiver/archiver_store.d.ts +3 -3
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/config.d.ts +3 -2
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +8 -1
- package/dest/archiver/instrumentation.d.ts +3 -1
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +11 -0
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts +3 -0
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +1 -0
- package/dest/archiver/l1/bin/retrieve-calldata.js +147 -0
- package/dest/archiver/l1/calldata_retriever.d.ts +98 -0
- package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -0
- package/dest/archiver/l1/calldata_retriever.js +403 -0
- package/dest/archiver/l1/data_retrieval.d.ts +87 -0
- package/dest/archiver/l1/data_retrieval.d.ts.map +1 -0
- package/dest/archiver/{data_retrieval.js → l1/data_retrieval.js} +19 -89
- package/dest/archiver/l1/debug_tx.d.ts +19 -0
- package/dest/archiver/l1/debug_tx.d.ts.map +1 -0
- package/dest/archiver/l1/debug_tx.js +73 -0
- package/dest/archiver/l1/spire_proposer.d.ts +70 -0
- package/dest/archiver/l1/spire_proposer.d.ts.map +1 -0
- package/dest/archiver/l1/spire_proposer.js +157 -0
- package/dest/archiver/l1/trace_tx.d.ts +97 -0
- package/dest/archiver/l1/trace_tx.d.ts.map +1 -0
- package/dest/archiver/l1/trace_tx.js +91 -0
- package/dest/archiver/l1/types.d.ts +12 -0
- package/dest/archiver/l1/types.d.ts.map +1 -0
- package/dest/archiver/l1/types.js +3 -0
- package/dest/archiver/l1/validate_trace.d.ts +29 -0
- package/dest/archiver/l1/validate_trace.d.ts.map +1 -0
- package/dest/archiver/l1/validate_trace.js +150 -0
- package/dest/index.d.ts +2 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/test/mock_l2_block_source.js +1 -1
- package/package.json +15 -14
- package/src/archiver/archiver.ts +45 -20
- package/src/archiver/archiver_store.ts +2 -2
- package/src/archiver/config.ts +8 -7
- package/src/archiver/instrumentation.ts +14 -0
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +2 -2
- package/src/archiver/kv_archiver_store/message_store.ts +1 -1
- package/src/archiver/l1/README.md +98 -0
- package/src/archiver/l1/bin/retrieve-calldata.ts +182 -0
- package/src/archiver/l1/calldata_retriever.ts +531 -0
- package/src/archiver/{data_retrieval.ts → l1/data_retrieval.ts} +50 -137
- package/src/archiver/l1/debug_tx.ts +99 -0
- package/src/archiver/l1/spire_proposer.ts +160 -0
- package/src/archiver/l1/trace_tx.ts +128 -0
- package/src/archiver/l1/types.ts +13 -0
- package/src/archiver/l1/validate_trace.ts +211 -0
- package/src/index.ts +1 -1
- package/src/test/fixtures/debug_traceTransaction-multicall3.json +88 -0
- package/src/test/fixtures/debug_traceTransaction-multiplePropose.json +153 -0
- package/src/test/fixtures/debug_traceTransaction-proxied.json +122 -0
- package/src/test/fixtures/trace_transaction-multicall3.json +65 -0
- package/src/test/fixtures/trace_transaction-multiplePropose.json +319 -0
- package/src/test/fixtures/trace_transaction-proxied.json +128 -0
- package/src/test/fixtures/trace_transaction-randomRevert.json +216 -0
- package/src/test/mock_l2_block_source.ts +1 -1
- package/dest/archiver/data_retrieval.d.ts +0 -80
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type CheckpointBlobData } from '@aztec/blob-lib';
|
|
2
|
+
import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
|
|
3
|
+
import type { ViemClient, ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
4
|
+
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
5
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
|
+
import { type Logger } from '@aztec/foundation/log';
|
|
8
|
+
import { type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
9
|
+
import { CommitteeAttestation } from '@aztec/stdlib/block';
|
|
10
|
+
import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
11
|
+
import { Proof } from '@aztec/stdlib/proofs';
|
|
12
|
+
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
13
|
+
import { type GetContractReturnType, type Hex } from 'viem';
|
|
14
|
+
import type { ArchiverInstrumentation } from '../instrumentation.js';
|
|
15
|
+
import type { DataRetrieval } from '../structs/data_retrieval.js';
|
|
16
|
+
import type { InboxMessage } from '../structs/inbox_message.js';
|
|
17
|
+
import type { L1PublishedData } from '../structs/published.js';
|
|
18
|
+
export type RetrievedCheckpoint = {
|
|
19
|
+
checkpointNumber: CheckpointNumber;
|
|
20
|
+
archiveRoot: Fr;
|
|
21
|
+
header: CheckpointHeader;
|
|
22
|
+
checkpointBlobData: CheckpointBlobData;
|
|
23
|
+
l1: L1PublishedData;
|
|
24
|
+
chainId: Fr;
|
|
25
|
+
version: Fr;
|
|
26
|
+
attestations: CommitteeAttestation[];
|
|
27
|
+
};
|
|
28
|
+
export declare function retrievedToPublishedCheckpoint({ checkpointNumber, archiveRoot, header: checkpointHeader, checkpointBlobData, l1, chainId, version, attestations }: RetrievedCheckpoint): Promise<PublishedCheckpoint>;
|
|
29
|
+
/**
|
|
30
|
+
* Fetches new checkpoints.
|
|
31
|
+
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
32
|
+
* @param debugClient - The viem debug client to use for trace/debug RPC methods (optional).
|
|
33
|
+
* @param rollupAddress - The address of the rollup contract.
|
|
34
|
+
* @param searchStartBlock - The block number to use for starting the search.
|
|
35
|
+
* @param searchEndBlock - The highest block number that we should search up to.
|
|
36
|
+
* @param expectedNextL2BlockNum - The next L2 block number that we expect to find.
|
|
37
|
+
* @returns An array of block; as well as the next eth block to search from.
|
|
38
|
+
*/
|
|
39
|
+
export declare function retrieveCheckpointsFromRollup(rollup: GetContractReturnType<typeof RollupAbi, ViemPublicClient>, publicClient: ViemPublicClient, debugClient: ViemPublicDebugClient, blobSinkClient: BlobSinkClientInterface, searchStartBlock: bigint, searchEndBlock: bigint, contractAddresses: {
|
|
40
|
+
governanceProposerAddress: EthAddress;
|
|
41
|
+
slashFactoryAddress?: EthAddress;
|
|
42
|
+
slashingProposerAddress: EthAddress;
|
|
43
|
+
}, instrumentation: ArchiverInstrumentation, logger?: Logger): Promise<RetrievedCheckpoint[]>;
|
|
44
|
+
export declare function getL1BlockTime(publicClient: ViemPublicClient, blockNumber: bigint): Promise<bigint>;
|
|
45
|
+
export declare function getCheckpointBlobDataFromBlobs(blobSinkClient: BlobSinkClientInterface, blockHash: string, blobHashes: Buffer<ArrayBufferLike>[], checkpointNumber: CheckpointNumber, logger: Logger): Promise<CheckpointBlobData>;
|
|
46
|
+
/** Given an L1 to L2 message, retrieves its corresponding event from the Inbox within a specific block range. */
|
|
47
|
+
export declare function retrieveL1ToL2Message(inbox: GetContractReturnType<typeof InboxAbi, ViemClient>, leaf: Fr, fromBlock: bigint, toBlock: bigint): Promise<InboxMessage | undefined>;
|
|
48
|
+
/**
|
|
49
|
+
* Fetch L1 to L2 messages.
|
|
50
|
+
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
51
|
+
* @param inboxAddress - The address of the inbox contract to fetch messages from.
|
|
52
|
+
* @param blockUntilSynced - If true, blocks until the archiver has fully synced.
|
|
53
|
+
* @param searchStartBlock - The block number to use for starting the search.
|
|
54
|
+
* @param searchEndBlock - The highest block number that we should search up to.
|
|
55
|
+
* @returns An array of InboxLeaf and next eth block to search from.
|
|
56
|
+
*/
|
|
57
|
+
export declare function retrieveL1ToL2Messages(inbox: GetContractReturnType<typeof InboxAbi, ViemClient>, searchStartBlock: bigint, searchEndBlock: bigint): Promise<InboxMessage[]>;
|
|
58
|
+
/** Retrieves L2ProofVerified events from the rollup contract. */
|
|
59
|
+
export declare function retrieveL2ProofVerifiedEvents(publicClient: ViemPublicClient, rollupAddress: EthAddress, searchStartBlock: bigint, searchEndBlock?: bigint): Promise<{
|
|
60
|
+
l1BlockNumber: bigint;
|
|
61
|
+
checkpointNumber: CheckpointNumber;
|
|
62
|
+
proverId: Fr;
|
|
63
|
+
txHash: Hex;
|
|
64
|
+
}[]>;
|
|
65
|
+
/** Retrieve submitted proofs from the rollup contract */
|
|
66
|
+
export declare function retrieveL2ProofsFromRollup(publicClient: ViemPublicClient, rollupAddress: EthAddress, searchStartBlock: bigint, searchEndBlock?: bigint): Promise<DataRetrieval<{
|
|
67
|
+
proof: Proof;
|
|
68
|
+
proverId: Fr;
|
|
69
|
+
checkpointNumber: number;
|
|
70
|
+
txHash: `0x${string}`;
|
|
71
|
+
}>>;
|
|
72
|
+
export type SubmitEpochProof = {
|
|
73
|
+
archiveRoot: Fr;
|
|
74
|
+
proverId: Fr;
|
|
75
|
+
proof: Proof;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Gets epoch proof metadata (archive root and proof) from the calldata of an L1 transaction.
|
|
79
|
+
* Assumes that the block was published from an EOA.
|
|
80
|
+
* TODO: Add retries and error management.
|
|
81
|
+
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
82
|
+
* @param txHash - Hash of the tx that published it.
|
|
83
|
+
* @param expectedProverId - Expected prover ID.
|
|
84
|
+
* @returns Epoch proof metadata from the calldata, deserialized.
|
|
85
|
+
*/
|
|
86
|
+
export declare function getProofFromSubmitProofTx(publicClient: ViemPublicClient, txHash: `0x${string}`, expectedProverId: Fr): Promise<SubmitEpochProof>;
|
|
87
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YV9yZXRyaWV2YWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcmNoaXZlci9sMS9kYXRhX3JldHJpZXZhbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsS0FBSyxrQkFBa0IsRUFJeEIsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QixPQUFPLEtBQUssRUFBRSx1QkFBdUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXZFLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxnQkFBZ0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRWpHLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRW5FLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBQ2xFLE9BQU8sRUFBRSxLQUFLLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMvRCxPQUFPLEVBQVEsb0JBQW9CLEVBQWMsTUFBTSxxQkFBcUIsQ0FBQztBQUM3RSxPQUFPLEVBQWMsbUJBQW1CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0MsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFJeEQsT0FBTyxFQUVMLEtBQUsscUJBQXFCLEVBQzFCLEtBQUssR0FBRyxFQUlULE1BQU0sTUFBTSxDQUFDO0FBR2QsT0FBTyxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUNsRSxPQUFPLEtB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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data_retrieval.d.ts","sourceRoot":"","sources":["../../../src/archiver/l1/data_retrieval.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,16 +3,17 @@ import { asyncPool } from '@aztec/foundation/async-pool';
|
|
|
3
3
|
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
4
4
|
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
5
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
7
8
|
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
8
|
-
import { Body,
|
|
9
|
+
import { Body, L2BlockNew } from '@aztec/stdlib/block';
|
|
9
10
|
import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
10
11
|
import { Proof } from '@aztec/stdlib/proofs';
|
|
11
|
-
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
12
12
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
13
13
|
import { BlockHeader, GlobalVariables, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
|
|
14
|
-
import { decodeFunctionData, getAbiItem, hexToBytes
|
|
15
|
-
import { NoBlobBodiesFoundError } from '
|
|
14
|
+
import { decodeFunctionData, getAbiItem, hexToBytes } from 'viem';
|
|
15
|
+
import { NoBlobBodiesFoundError } from '../errors.js';
|
|
16
|
+
import { CalldataRetriever } from './calldata_retriever.js';
|
|
16
17
|
export async function retrievedToPublishedCheckpoint({ checkpointNumber, archiveRoot, header: checkpointHeader, checkpointBlobData, l1, chainId, version, attestations }) {
|
|
17
18
|
const { blocks: blocksBlobData } = checkpointBlobData;
|
|
18
19
|
// The lastArchiveRoot of a block is the new archive for the previous block.
|
|
@@ -78,12 +79,13 @@ export async function retrievedToPublishedCheckpoint({ checkpointNumber, archive
|
|
|
78
79
|
/**
|
|
79
80
|
* Fetches new checkpoints.
|
|
80
81
|
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
82
|
+
* @param debugClient - The viem debug client to use for trace/debug RPC methods (optional).
|
|
81
83
|
* @param rollupAddress - The address of the rollup contract.
|
|
82
84
|
* @param searchStartBlock - The block number to use for starting the search.
|
|
83
85
|
* @param searchEndBlock - The highest block number that we should search up to.
|
|
84
86
|
* @param expectedNextL2BlockNum - The next L2 block number that we expect to find.
|
|
85
87
|
* @returns An array of block; as well as the next eth block to search from.
|
|
86
|
-
*/ export async function retrieveCheckpointsFromRollup(rollup, publicClient, blobSinkClient, searchStartBlock, searchEndBlock, logger = createLogger('archiver')) {
|
|
88
|
+
*/ export async function retrieveCheckpointsFromRollup(rollup, publicClient, debugClient, blobSinkClient, searchStartBlock, searchEndBlock, contractAddresses, instrumentation, logger = createLogger('archiver')) {
|
|
87
89
|
const retrievedCheckpoints = [];
|
|
88
90
|
let rollupConstants;
|
|
89
91
|
do {
|
|
@@ -111,7 +113,7 @@ export async function retrievedToPublishedCheckpoint({ checkpointNumber, archive
|
|
|
111
113
|
targetCommitteeSize: Number(targetCommitteeSize)
|
|
112
114
|
};
|
|
113
115
|
}
|
|
114
|
-
const newCheckpoints = await processCheckpointProposedLogs(rollup, publicClient, blobSinkClient, checkpointProposedLogs, rollupConstants, logger);
|
|
116
|
+
const newCheckpoints = await processCheckpointProposedLogs(rollup, publicClient, debugClient, blobSinkClient, checkpointProposedLogs, rollupConstants, contractAddresses, instrumentation, logger);
|
|
115
117
|
retrievedCheckpoints.push(...newCheckpoints);
|
|
116
118
|
searchStartBlock = lastLog.blockNumber + 1n;
|
|
117
119
|
}while (searchStartBlock <= searchEndBlock)
|
|
@@ -122,10 +124,15 @@ export async function retrievedToPublishedCheckpoint({ checkpointNumber, archive
|
|
|
122
124
|
* Processes newly received CheckpointProposed logs.
|
|
123
125
|
* @param rollup - The rollup contract
|
|
124
126
|
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
127
|
+
* @param debugClient - The viem debug client to use for trace/debug RPC methods (optional).
|
|
125
128
|
* @param logs - CheckpointProposed logs.
|
|
126
129
|
* @returns - An array of checkpoints.
|
|
127
|
-
*/ async function processCheckpointProposedLogs(rollup, publicClient, blobSinkClient, logs, { chainId, version, targetCommitteeSize }, logger) {
|
|
130
|
+
*/ async function processCheckpointProposedLogs(rollup, publicClient, debugClient, blobSinkClient, logs, { chainId, version, targetCommitteeSize }, contractAddresses, instrumentation, logger) {
|
|
128
131
|
const retrievedCheckpoints = [];
|
|
132
|
+
const calldataRetriever = new CalldataRetriever(publicClient, debugClient, targetCommitteeSize, instrumentation, logger, {
|
|
133
|
+
...contractAddresses,
|
|
134
|
+
rollupAddress: EthAddress.fromString(rollup.address)
|
|
135
|
+
});
|
|
129
136
|
await asyncPool(10, logs, async (log)=>{
|
|
130
137
|
const checkpointNumber = CheckpointNumber.fromBigInt(log.args.checkpointNumber);
|
|
131
138
|
const archive = log.args.archive;
|
|
@@ -135,7 +142,8 @@ export async function retrievedToPublishedCheckpoint({ checkpointNumber, archive
|
|
|
135
142
|
const blobHashes = log.args.versionedBlobHashes.map((blobHash)=>Buffer.from(blobHash.slice(2), 'hex'));
|
|
136
143
|
// The value from the event and contract will match only if the checkpoint is in the chain.
|
|
137
144
|
if (archive === archiveFromChain) {
|
|
138
|
-
const checkpoint = await getCheckpointFromRollupTx(
|
|
145
|
+
const checkpoint = await calldataRetriever.getCheckpointFromRollupTx(log.transactionHash, blobHashes, checkpointNumber);
|
|
146
|
+
const checkpointBlobData = await getCheckpointBlobDataFromBlobs(blobSinkClient, checkpoint.blockHash, blobHashes, checkpointNumber, logger);
|
|
139
147
|
const l1 = {
|
|
140
148
|
blockNumber: log.blockNumber,
|
|
141
149
|
blockHash: log.blockHash,
|
|
@@ -143,6 +151,7 @@ export async function retrievedToPublishedCheckpoint({ checkpointNumber, archive
|
|
|
143
151
|
};
|
|
144
152
|
retrievedCheckpoints.push({
|
|
145
153
|
...checkpoint,
|
|
154
|
+
checkpointBlobData,
|
|
146
155
|
l1,
|
|
147
156
|
chainId,
|
|
148
157
|
version
|
|
@@ -169,79 +178,7 @@ export async function getL1BlockTime(publicClient, blockNumber) {
|
|
|
169
178
|
});
|
|
170
179
|
return block.timestamp;
|
|
171
180
|
}
|
|
172
|
-
|
|
173
|
-
* Extracts the first 'propose' method calldata from a multicall3 transaction's data.
|
|
174
|
-
* @param multicall3Data - The multicall3 transaction input data
|
|
175
|
-
* @param rollupAddress - The address of the rollup contract
|
|
176
|
-
* @returns The calldata for the first 'propose' method call to the rollup contract
|
|
177
|
-
*/ function extractRollupProposeCalldata(multicall3Data, rollupAddress) {
|
|
178
|
-
const { functionName: multicall3FunctionName, args: multicall3Args } = decodeFunctionData({
|
|
179
|
-
abi: multicall3Abi,
|
|
180
|
-
data: multicall3Data
|
|
181
|
-
});
|
|
182
|
-
if (multicall3FunctionName !== 'aggregate3') {
|
|
183
|
-
throw new Error(`Unexpected multicall3 method called ${multicall3FunctionName}`);
|
|
184
|
-
}
|
|
185
|
-
if (multicall3Args.length !== 1) {
|
|
186
|
-
throw new Error(`Unexpected number of arguments for multicall3`);
|
|
187
|
-
}
|
|
188
|
-
const [calls] = multicall3Args;
|
|
189
|
-
// Find all rollup calls
|
|
190
|
-
const rollupAddressLower = rollupAddress.toLowerCase();
|
|
191
|
-
for(let i = 0; i < calls.length; i++){
|
|
192
|
-
const addr = calls[i].target;
|
|
193
|
-
if (addr.toLowerCase() !== rollupAddressLower) {
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
const callData = calls[i].callData;
|
|
197
|
-
try {
|
|
198
|
-
const { functionName: rollupFunctionName } = decodeFunctionData({
|
|
199
|
-
abi: RollupAbi,
|
|
200
|
-
data: callData
|
|
201
|
-
});
|
|
202
|
-
if (rollupFunctionName === 'propose') {
|
|
203
|
-
return callData;
|
|
204
|
-
}
|
|
205
|
-
} catch {
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
throw new Error(`Rollup address not found in multicall3 args`);
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Gets checkpoint from the calldata of an L1 transaction.
|
|
213
|
-
* Assumes that the checkpoint was published from an EOA.
|
|
214
|
-
* TODO: Add retries and error management.
|
|
215
|
-
* @param publicClient - The viem public client to use for transaction retrieval.
|
|
216
|
-
* @param txHash - Hash of the tx that published it.
|
|
217
|
-
* @param checkpointNumber - Checkpoint number.
|
|
218
|
-
* @returns Checkpoint from the calldata, deserialized
|
|
219
|
-
*/ async function getCheckpointFromRollupTx(publicClient, blobSinkClient, txHash, blobHashes, checkpointNumber, rollupAddress, targetCommitteeSize, logger) {
|
|
220
|
-
logger.trace(`Fetching checkpoint ${checkpointNumber} from rollup tx ${txHash}`);
|
|
221
|
-
const { input: forwarderData, blockHash } = await publicClient.getTransaction({
|
|
222
|
-
hash: txHash
|
|
223
|
-
});
|
|
224
|
-
const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
|
|
225
|
-
const { functionName: rollupFunctionName, args: rollupArgs } = decodeFunctionData({
|
|
226
|
-
abi: RollupAbi,
|
|
227
|
-
data: rollupData
|
|
228
|
-
});
|
|
229
|
-
if (rollupFunctionName !== 'propose') {
|
|
230
|
-
throw new Error(`Unexpected rollup method called ${rollupFunctionName}`);
|
|
231
|
-
}
|
|
232
|
-
const [decodedArgs, packedAttestations, _signers, _blobInput] = rollupArgs;
|
|
233
|
-
const attestations = CommitteeAttestation.fromPacked(packedAttestations, targetCommitteeSize);
|
|
234
|
-
logger.trace(`Recovered propose calldata from tx ${txHash}`, {
|
|
235
|
-
checkpointNumber,
|
|
236
|
-
archive: decodedArgs.archive,
|
|
237
|
-
header: decodedArgs.header,
|
|
238
|
-
l1BlockHash: blockHash,
|
|
239
|
-
blobHashes,
|
|
240
|
-
attestations,
|
|
241
|
-
packedAttestations,
|
|
242
|
-
targetCommitteeSize
|
|
243
|
-
});
|
|
244
|
-
const header = CheckpointHeader.fromViem(decodedArgs.header);
|
|
181
|
+
export async function getCheckpointBlobDataFromBlobs(blobSinkClient, blockHash, blobHashes, checkpointNumber, logger) {
|
|
245
182
|
const blobBodies = await blobSinkClient.getBlobSidecar(blockHash, blobHashes);
|
|
246
183
|
if (blobBodies.length === 0) {
|
|
247
184
|
throw new NoBlobBodiesFoundError(checkpointNumber);
|
|
@@ -259,14 +196,7 @@ export async function getL1BlockTime(publicClient, blockNumber) {
|
|
|
259
196
|
// Throwing an error since this is most likely caused by a bug.
|
|
260
197
|
throw err;
|
|
261
198
|
}
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
checkpointNumber,
|
|
265
|
-
archiveRoot,
|
|
266
|
-
header,
|
|
267
|
-
checkpointBlobData,
|
|
268
|
-
attestations
|
|
269
|
-
};
|
|
199
|
+
return checkpointBlobData;
|
|
270
200
|
}
|
|
271
201
|
/** Given an L1 to L2 message, retrieves its corresponding event from the Inbox within a specific block range. */ export async function retrieveL1ToL2Message(inbox, leaf, fromBlock, toBlock) {
|
|
272
202
|
const logs = await inbox.getEvents.MessageSent({
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DebugCallTrace, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
2
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
4
|
+
import { type ZodFor } from '@aztec/foundation/schemas';
|
|
5
|
+
import type { Hex } from 'viem';
|
|
6
|
+
import type { CallInfo } from './types.js';
|
|
7
|
+
/** Zod schema for validating call trace from debug_traceTransaction */
|
|
8
|
+
export declare const callTraceSchema: ZodFor<DebugCallTrace>;
|
|
9
|
+
/**
|
|
10
|
+
* Traces a transaction and extracts all CALL operations to a specific contract and function selector.
|
|
11
|
+
*
|
|
12
|
+
* @param client - The Viem public client
|
|
13
|
+
* @param txHash - The transaction hash to trace
|
|
14
|
+
* @param targetAddress - The contract address to filter for
|
|
15
|
+
* @param functionSelector - The 4-byte function selector to filter for (with or without 0x prefix)
|
|
16
|
+
* @returns Array of CallInfo objects containing from, gasUsed, input, and value for matching calls
|
|
17
|
+
*/
|
|
18
|
+
export declare function getSuccessfulCallsFromDebug(client: ViemPublicDebugClient, txHash: Hex, targetAddress: EthAddress, functionSelector: string, logger?: Logger): Promise<CallInfo[]>;
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVidWdfdHguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcmNoaXZlci9sMS9kZWJ1Z190eC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFXLE1BQU0sMkJBQTJCLENBQUM7QUFHakUsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBR2hDLE9BQU8sS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUUzQyx1RUFBdUU7QUFDdkUsZUFBTyxNQUFNLGVBQWUsRUFBRSxNQUFNLENBQUMsY0FBYyxDQWFsRCxDQUFDO0FBRUY7Ozs7Ozs7O0dBUUc7QUFDSCx3QkFBc0IsMkJBQTJCLENBQy9DLE1BQU0sRUFBRSxxQkFBcUIsRUFDN0IsTUFBTSxFQUFFLEdBQUcsRUFDWCxhQUFhLEVBQUUsVUFBVSxFQUN6QixnQkFBZ0IsRUFBRSxNQUFNLEVBQ3hCLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FDZCxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0F3RHJCIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug_tx.d.ts","sourceRoot":"","sources":["../../../src/archiver/l1/debug_tx.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,2BAA2B,CAAC;AAGjE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAGhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,uEAAuE;AACvE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,CAalD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,GAAG,EACX,aAAa,EAAE,UAAU,EACzB,gBAAgB,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,QAAQ,EAAE,CAAC,CAwDrB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { schemas } from '@aztec/foundation/schemas';
|
|
3
|
+
import { withHexPrefix } from '@aztec/foundation/string';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
/** Zod schema for validating call trace from debug_traceTransaction */ export const callTraceSchema = z.lazy(()=>z.object({
|
|
6
|
+
from: schemas.HexStringWith0x,
|
|
7
|
+
to: schemas.HexStringWith0x.optional(),
|
|
8
|
+
type: z.string(),
|
|
9
|
+
input: schemas.HexStringWith0x.optional(),
|
|
10
|
+
output: schemas.HexStringWith0x.optional(),
|
|
11
|
+
gas: schemas.HexStringWith0x.optional(),
|
|
12
|
+
gasUsed: schemas.HexStringWith0x.optional(),
|
|
13
|
+
value: schemas.HexStringWith0x.optional(),
|
|
14
|
+
error: z.string().optional(),
|
|
15
|
+
calls: z.array(callTraceSchema).optional()
|
|
16
|
+
}));
|
|
17
|
+
/**
|
|
18
|
+
* Traces a transaction and extracts all CALL operations to a specific contract and function selector.
|
|
19
|
+
*
|
|
20
|
+
* @param client - The Viem public client
|
|
21
|
+
* @param txHash - The transaction hash to trace
|
|
22
|
+
* @param targetAddress - The contract address to filter for
|
|
23
|
+
* @param functionSelector - The 4-byte function selector to filter for (with or without 0x prefix)
|
|
24
|
+
* @returns Array of CallInfo objects containing from, gasUsed, input, and value for matching calls
|
|
25
|
+
*/ export async function getSuccessfulCallsFromDebug(client, txHash, targetAddress, functionSelector, logger) {
|
|
26
|
+
// Normalize inputs for comparison
|
|
27
|
+
const normalizedTarget = targetAddress.toString().toLowerCase();
|
|
28
|
+
const normalizedSelector = withHexPrefix(functionSelector.toLowerCase());
|
|
29
|
+
// Call debug_traceTransaction with callTracer
|
|
30
|
+
// Using 'any' here because debug_traceTransaction is not in viem's standard RPC types
|
|
31
|
+
const rawTrace = await client.request({
|
|
32
|
+
method: 'debug_traceTransaction',
|
|
33
|
+
params: [
|
|
34
|
+
txHash,
|
|
35
|
+
{
|
|
36
|
+
tracer: 'callTracer'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
if (rawTrace === null || rawTrace === undefined) {
|
|
41
|
+
throw new Error(`Failed to retrieve debug_traceTransaction for ${txHash}`);
|
|
42
|
+
}
|
|
43
|
+
logger?.trace(`Retrieved debug_traceTransaction for ${txHash}`, {
|
|
44
|
+
trace: rawTrace
|
|
45
|
+
});
|
|
46
|
+
// Validate the response with zod
|
|
47
|
+
const trace = callTraceSchema.parse(rawTrace);
|
|
48
|
+
const results = [];
|
|
49
|
+
/**
|
|
50
|
+
* Recursively traverse the call trace tree
|
|
51
|
+
*/ function traverseCalls(callTrace) {
|
|
52
|
+
// Skip calls that have errors, and all its descendants
|
|
53
|
+
if (callTrace.error) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Check if this is a CALL (not DELEGATECALL or STATICCALL) to the target address with matching selector
|
|
57
|
+
if (callTrace.type.toUpperCase() === 'CALL' && callTrace.to?.toLowerCase() === normalizedTarget && callTrace.input?.toLowerCase().startsWith(normalizedSelector)) {
|
|
58
|
+
results.push({
|
|
59
|
+
from: EthAddress.fromString(callTrace.from),
|
|
60
|
+
gasUsed: callTrace.gasUsed === undefined ? undefined : BigInt(callTrace.gasUsed),
|
|
61
|
+
input: callTrace.input,
|
|
62
|
+
value: callTrace.value ? BigInt(callTrace.value) : 0n
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Recursively process nested calls
|
|
66
|
+
for (const nestedCall of callTrace.calls ?? []){
|
|
67
|
+
traverseCalls(nestedCall);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Start traversal from the root trace
|
|
71
|
+
traverseCalls(trace);
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
2
|
+
import { type Hex, type Transaction } from 'viem';
|
|
3
|
+
export declare const SPIRE_PROPOSER_ADDRESS = "0x9ccc2f3ecde026230e11a5c8799ac7524f2bb294";
|
|
4
|
+
export declare const SPIRE_PROPOSER_EXPECTED_IMPLEMENTATION = "0x7d38d47e7c82195e6e607d3b0f1c20c615c7bf42";
|
|
5
|
+
export declare const EIP1967_IMPLEMENTATION_SLOT: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
6
|
+
export declare const SpireProposerAbi: readonly [{
|
|
7
|
+
readonly inputs: readonly [{
|
|
8
|
+
readonly components: readonly [{
|
|
9
|
+
readonly internalType: "address";
|
|
10
|
+
readonly name: "proposer";
|
|
11
|
+
readonly type: "address";
|
|
12
|
+
}, {
|
|
13
|
+
readonly internalType: "address";
|
|
14
|
+
readonly name: "target";
|
|
15
|
+
readonly type: "address";
|
|
16
|
+
}, {
|
|
17
|
+
readonly internalType: "bytes";
|
|
18
|
+
readonly name: "data";
|
|
19
|
+
readonly type: "bytes";
|
|
20
|
+
}, {
|
|
21
|
+
readonly internalType: "uint256";
|
|
22
|
+
readonly name: "value";
|
|
23
|
+
readonly type: "uint256";
|
|
24
|
+
}, {
|
|
25
|
+
readonly internalType: "uint256";
|
|
26
|
+
readonly name: "gasLimit";
|
|
27
|
+
readonly type: "uint256";
|
|
28
|
+
}];
|
|
29
|
+
readonly internalType: "struct IProposerMulticall.Call[]";
|
|
30
|
+
readonly name: "_calls";
|
|
31
|
+
readonly type: "tuple[]";
|
|
32
|
+
}];
|
|
33
|
+
readonly name: "multicall";
|
|
34
|
+
readonly outputs: readonly [];
|
|
35
|
+
readonly stateMutability: "nonpayable";
|
|
36
|
+
readonly type: "function";
|
|
37
|
+
}];
|
|
38
|
+
/**
|
|
39
|
+
* Verifies that a proxy contract points to the expected implementation using EIP-1967.
|
|
40
|
+
* @param publicClient - The viem public client
|
|
41
|
+
* @param proxyAddress - The proxy contract address
|
|
42
|
+
* @param expectedImplementation - The expected implementation address
|
|
43
|
+
* @param logger - Logger instance
|
|
44
|
+
* @returns True if the proxy points to the expected implementation
|
|
45
|
+
*/
|
|
46
|
+
export declare function verifyProxyImplementation(publicClient: {
|
|
47
|
+
getStorageAt: (params: {
|
|
48
|
+
address: Hex;
|
|
49
|
+
slot: Hex;
|
|
50
|
+
}) => Promise<Hex | undefined>;
|
|
51
|
+
}, proxyAddress: Hex, expectedImplementation: Hex, logger: Logger): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Attempts to decode transaction as a Spire Proposer Multicall.
|
|
54
|
+
* Spire Proposer is a proxy contract that wraps multiple calls.
|
|
55
|
+
* Returns the target address and calldata of the wrapped call if validation succeeds and there is a single call.
|
|
56
|
+
* @param tx - The transaction to decode
|
|
57
|
+
* @param publicClient - The viem public client for proxy verification
|
|
58
|
+
* @param logger - Logger instance
|
|
59
|
+
* @returns Object with 'to' and 'data' of the wrapped call, or undefined if validation fails
|
|
60
|
+
*/
|
|
61
|
+
export declare function getCallFromSpireProposer(tx: Transaction, publicClient: {
|
|
62
|
+
getStorageAt: (params: {
|
|
63
|
+
address: Hex;
|
|
64
|
+
slot: Hex;
|
|
65
|
+
}) => Promise<Hex | undefined>;
|
|
66
|
+
}, logger: Logger): Promise<{
|
|
67
|
+
to: Hex;
|
|
68
|
+
data: Hex;
|
|
69
|
+
} | undefined>;
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BpcmVfcHJvcG9zZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcmNoaXZlci9sMS9zcGlyZV9wcm9wb3Nlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVwRCxPQUFPLEVBQUUsS0FBSyxHQUFHLEVBQUUsS0FBSyxXQUFXLEVBQXdDLE1BQU0sTUFBTSxDQUFDO0FBR3hGLGVBQU8sTUFBTSxzQkFBc0IsK0NBQStDLENBQUM7QUFDbkYsZUFBTyxNQUFNLHNDQUFzQywrQ0FBK0MsQ0FBQztBQUluRyxlQUFPLE1BQU0sMkJBQTJCLHNFQUN1QyxDQUFDO0FBR2hGLGVBQU8sTUFBTSxnQkFBZ0I7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUFxQm5CLENBQUM7QUFFWDs7Ozs7OztHQU9HO0FBQ0gsd0JBQXNCLHlCQUF5QixDQUM3QyxZQUFZLEVBQUU7SUFBRSxZQUFZLEVBQUUsQ0FBQyxNQUFNLEVBQUU7UUFBRSxPQUFPLEVBQUUsR0FBRyxDQUFDO1FBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQTtLQUFFLEtBQUssT0FBTyxDQUFDLEdBQUcsR0FBRyxTQUFTLENBQUMsQ0FBQTtDQUFFLEVBQ2pHLFlBQVksRUFBRSxHQUFHLEVBQ2pCLHNCQUFzQixFQUFFLEdBQUcsRUFDM0IsTUFBTSxFQUFFLE1BQU0sR0FDYixPQUFPLENBQUMsT0FBTyxDQUFDLENBaUNsQjtBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsd0JBQXNCLHdCQUF3QixDQUM1QyxFQUFFLEVBQUUsV0FBVyxFQUNmLFlBQVksRUFBRTtJQUFFLFlBQVksRUFBRSxDQUFDLE1BQU0sRUFBRTtRQUFFLE9BQU8sRUFBRSxHQUFHLENBQUM7UUFBQyxJQUFJLEVBQUUsR0FBRyxDQUFBO0tBQUUsS0FBSyxPQUFPLENBQUMsR0FBRyxHQUFHLFNBQVMsQ0FBQyxDQUFBO0NBQUUsRUFDakcsTUFBTSxFQUFFLE1BQU0sR0FDYixPQUFPLENBQUM7SUFBRSxFQUFFLEVBQUUsR0FBRyxDQUFDO0lBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQTtDQUFFLEdBQUcsU0FBUyxDQUFDLENBNEQ3QyJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spire_proposer.d.ts","sourceRoot":"","sources":["../../../src/archiver/l1/spire_proposer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAwC,MAAM,MAAM,CAAC;AAGxF,eAAO,MAAM,sBAAsB,+CAA+C,CAAC;AACnF,eAAO,MAAM,sCAAsC,+CAA+C,CAAC;AAInG,eAAO,MAAM,2BAA2B,sEACuC,CAAC;AAGhF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBnB,CAAC;AAEX;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE;IAAE,YAAY,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,GAAG,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,KAAK,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAA;CAAE,EACjG,YAAY,EAAE,GAAG,EACjB,sBAAsB,EAAE,GAAG,EAC3B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAiClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,WAAW,EACf,YAAY,EAAE;IAAE,YAAY,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,GAAG,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,KAAK,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAA;CAAE,EACjG,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,GAAG,SAAS,CAAC,CA4D7C"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { decodeFunctionData, getAddress, trim } from 'viem';
|
|
3
|
+
// Spire Proposer Multicall constants
|
|
4
|
+
export const SPIRE_PROPOSER_ADDRESS = '0x9ccc2f3ecde026230e11a5c8799ac7524f2bb294';
|
|
5
|
+
export const SPIRE_PROPOSER_EXPECTED_IMPLEMENTATION = '0x7d38d47e7c82195e6e607d3b0f1c20c615c7bf42';
|
|
6
|
+
// EIP-1967 storage slot for implementation address
|
|
7
|
+
// keccak256("eip1967.proxy.implementation") - 1 = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
|
|
8
|
+
export const EIP1967_IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
|
|
9
|
+
// Spire Proposer Multicall ABI
|
|
10
|
+
export const SpireProposerAbi = [
|
|
11
|
+
{
|
|
12
|
+
inputs: [
|
|
13
|
+
{
|
|
14
|
+
components: [
|
|
15
|
+
{
|
|
16
|
+
internalType: 'address',
|
|
17
|
+
name: 'proposer',
|
|
18
|
+
type: 'address'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
internalType: 'address',
|
|
22
|
+
name: 'target',
|
|
23
|
+
type: 'address'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
internalType: 'bytes',
|
|
27
|
+
name: 'data',
|
|
28
|
+
type: 'bytes'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
internalType: 'uint256',
|
|
32
|
+
name: 'value',
|
|
33
|
+
type: 'uint256'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
internalType: 'uint256',
|
|
37
|
+
name: 'gasLimit',
|
|
38
|
+
type: 'uint256'
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
internalType: 'struct IProposerMulticall.Call[]',
|
|
42
|
+
name: '_calls',
|
|
43
|
+
type: 'tuple[]'
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
name: 'multicall',
|
|
47
|
+
outputs: [],
|
|
48
|
+
stateMutability: 'nonpayable',
|
|
49
|
+
type: 'function'
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Verifies that a proxy contract points to the expected implementation using EIP-1967.
|
|
54
|
+
* @param publicClient - The viem public client
|
|
55
|
+
* @param proxyAddress - The proxy contract address
|
|
56
|
+
* @param expectedImplementation - The expected implementation address
|
|
57
|
+
* @param logger - Logger instance
|
|
58
|
+
* @returns True if the proxy points to the expected implementation
|
|
59
|
+
*/ export async function verifyProxyImplementation(publicClient, proxyAddress, expectedImplementation, logger) {
|
|
60
|
+
try {
|
|
61
|
+
// Read the EIP-1967 implementation slot
|
|
62
|
+
const implementationData = await publicClient.getStorageAt({
|
|
63
|
+
address: proxyAddress,
|
|
64
|
+
slot: EIP1967_IMPLEMENTATION_SLOT
|
|
65
|
+
});
|
|
66
|
+
if (!implementationData) {
|
|
67
|
+
logger.warn(`No implementation found in EIP-1967 slot for proxy ${proxyAddress}`);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// The implementation address is stored in the last 20 bytes of the slot
|
|
71
|
+
// We need to extract and normalize it for comparison
|
|
72
|
+
const implementationAddress = getAddress(trim(implementationData));
|
|
73
|
+
const expectedAddress = getAddress(expectedImplementation);
|
|
74
|
+
const matches = implementationAddress.toLowerCase() === expectedAddress.toLowerCase();
|
|
75
|
+
if (!matches) {
|
|
76
|
+
logger.warn(`Proxy implementation mismatch: expected ${expectedAddress}, got ${implementationAddress}`, {
|
|
77
|
+
proxyAddress,
|
|
78
|
+
expectedImplementation,
|
|
79
|
+
actualImplementation: implementationAddress
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return matches;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
logger.warn(`Failed to verify proxy implementation for ${proxyAddress}: ${err}`);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Attempts to decode transaction as a Spire Proposer Multicall.
|
|
90
|
+
* Spire Proposer is a proxy contract that wraps multiple calls.
|
|
91
|
+
* Returns the target address and calldata of the wrapped call if validation succeeds and there is a single call.
|
|
92
|
+
* @param tx - The transaction to decode
|
|
93
|
+
* @param publicClient - The viem public client for proxy verification
|
|
94
|
+
* @param logger - Logger instance
|
|
95
|
+
* @returns Object with 'to' and 'data' of the wrapped call, or undefined if validation fails
|
|
96
|
+
*/ export async function getCallFromSpireProposer(tx, publicClient, logger) {
|
|
97
|
+
const txHash = tx.hash;
|
|
98
|
+
try {
|
|
99
|
+
// Check if transaction is to the Spire Proposer address
|
|
100
|
+
if (!tx.to || !EthAddress.areEqual(tx.to, SPIRE_PROPOSER_ADDRESS)) {
|
|
101
|
+
logger.debug(`Transaction is not to Spire Proposer address (to: ${tx.to})`, {
|
|
102
|
+
txHash
|
|
103
|
+
});
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
// Verify the proxy points to the expected implementation
|
|
107
|
+
const isValidProxy = await verifyProxyImplementation(publicClient, tx.to, SPIRE_PROPOSER_EXPECTED_IMPLEMENTATION, logger);
|
|
108
|
+
if (!isValidProxy) {
|
|
109
|
+
logger.warn(`Spire Proposer proxy implementation verification failed`, {
|
|
110
|
+
txHash,
|
|
111
|
+
to: tx.to
|
|
112
|
+
});
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
// Try to decode as Spire Proposer multicall
|
|
116
|
+
const { functionName: spireFunctionName, args: spireArgs } = decodeFunctionData({
|
|
117
|
+
abi: SpireProposerAbi,
|
|
118
|
+
data: tx.input
|
|
119
|
+
});
|
|
120
|
+
// If not multicall, return undefined
|
|
121
|
+
if (spireFunctionName !== 'multicall') {
|
|
122
|
+
logger.warn(`Transaction to Spire Proposer is not multicall (got ${spireFunctionName})`, {
|
|
123
|
+
txHash
|
|
124
|
+
});
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
if (spireArgs.length !== 1) {
|
|
128
|
+
logger.warn(`Unexpected number of arguments for Spire Proposer multicall (got ${spireArgs.length})`, {
|
|
129
|
+
txHash
|
|
130
|
+
});
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const [calls] = spireArgs;
|
|
134
|
+
// Validate exactly ONE call (see ./README.md for rationale)
|
|
135
|
+
if (calls.length !== 1) {
|
|
136
|
+
logger.warn(`Spire Proposer multicall must contain exactly one call (got ${calls.length})`, {
|
|
137
|
+
txHash
|
|
138
|
+
});
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
const call = calls[0];
|
|
142
|
+
// Successfully extracted the single wrapped call
|
|
143
|
+
logger.trace(`Decoded Spire Proposer with single call to ${call.target}`, {
|
|
144
|
+
txHash
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
to: call.target,
|
|
148
|
+
data: call.data
|
|
149
|
+
};
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// Any decoding error triggers fallback to trace
|
|
152
|
+
logger.warn(`Failed to decode Spire Proposer: ${err}`, {
|
|
153
|
+
txHash
|
|
154
|
+
});
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|