@aztec/ethereum 1.0.0 → 1.1.0
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/config.d.ts +27 -5
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +90 -9
- package/dest/contracts/empire_base.d.ts +3 -4
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/empire_base.js +4 -8
- package/dest/contracts/governance_proposer.d.ts +2 -2
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +4 -9
- package/dest/contracts/multicall.d.ts +2 -1
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +79 -48
- package/dest/contracts/rollup.d.ts +13 -1
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +64 -0
- package/dest/contracts/slashing_proposer.d.ts +2 -2
- package/dest/contracts/slashing_proposer.d.ts.map +1 -1
- package/dest/contracts/slashing_proposer.js +5 -10
- package/dest/deploy_l1_contracts.d.ts +837 -534
- package/dest/deploy_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_l1_contracts.js +35 -19
- package/dest/l1_tx_utils.d.ts +7 -2
- package/dest/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils.js +41 -13
- package/dest/l1_tx_utils_with_blobs.d.ts +2 -1
- package/dest/l1_tx_utils_with_blobs.d.ts.map +1 -1
- package/dest/l1_tx_utils_with_blobs.js +5 -3
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +5 -3
- package/dest/test/start_anvil.d.ts +2 -0
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +3 -0
- package/package.json +6 -4
- package/src/config.ts +102 -8
- package/src/contracts/empire_base.ts +10 -16
- package/src/contracts/governance_proposer.ts +10 -10
- package/src/contracts/multicall.ts +73 -46
- package/src/contracts/rollup.ts +82 -1
- package/src/contracts/slashing_proposer.ts +11 -11
- package/src/deploy_l1_contracts.ts +45 -18
- package/src/l1_tx_utils.ts +56 -19
- package/src/l1_tx_utils_with_blobs.ts +14 -3
- package/src/queries.ts +3 -0
- package/src/test/start_anvil.ts +5 -3
|
@@ -5,8 +5,10 @@ import { type Anvil } from '@viem/anvil';
|
|
|
5
5
|
export declare function startAnvil(opts?: {
|
|
6
6
|
port?: number;
|
|
7
7
|
l1BlockTime?: number;
|
|
8
|
+
captureMethodCalls?: boolean;
|
|
8
9
|
}): Promise<{
|
|
9
10
|
anvil: Anvil;
|
|
11
|
+
methodCalls?: string[];
|
|
10
12
|
rpcUrl: string;
|
|
11
13
|
stop: () => Promise<void>;
|
|
12
14
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start_anvil.d.ts","sourceRoot":"","sources":["../../src/test/start_anvil.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,aAAa,CAAC;AAGtD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,GAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"start_anvil.d.ts","sourceRoot":"","sources":["../../src/test/start_anvil.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,aAAa,CAAC;AAGtD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,GAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACzB,GACL,OAAO,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC9F"}
|
package/dest/test/start_anvil.js
CHANGED
|
@@ -6,6 +6,7 @@ import { dirname, resolve } from 'path';
|
|
|
6
6
|
* Ensures there's a running Anvil instance and returns the RPC URL.
|
|
7
7
|
*/ export async function startAnvil(opts = {}) {
|
|
8
8
|
const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
|
|
9
|
+
const methodCalls = opts.captureMethodCalls ? [] : undefined;
|
|
9
10
|
let port;
|
|
10
11
|
// Start anvil.
|
|
11
12
|
// We go via a wrapper script to ensure if the parent dies, anvil dies.
|
|
@@ -19,6 +20,7 @@ import { dirname, resolve } from 'path';
|
|
|
19
20
|
});
|
|
20
21
|
// Listen to the anvil output to get the port.
|
|
21
22
|
const removeHandler = anvil.on('message', (message)=>{
|
|
23
|
+
methodCalls?.push(...message.match(/eth_[^\s]+/g) || []);
|
|
22
24
|
if (port === undefined && message.includes('Listening on')) {
|
|
23
25
|
port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)[2]);
|
|
24
26
|
}
|
|
@@ -38,6 +40,7 @@ import { dirname, resolve } from 'path';
|
|
|
38
40
|
// Object.defineProperty(anvil, 'port', { value: port, writable: false });
|
|
39
41
|
return {
|
|
40
42
|
anvil,
|
|
43
|
+
methodCalls,
|
|
41
44
|
stop: ()=>anvil.stop(),
|
|
42
45
|
rpcUrl: `http://127.0.0.1:${port}`
|
|
43
46
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/ethereum",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -32,11 +32,12 @@
|
|
|
32
32
|
"../package.common.json"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@aztec/blob-lib": "1.
|
|
36
|
-
"@aztec/foundation": "1.
|
|
37
|
-
"@aztec/l1-artifacts": "1.
|
|
35
|
+
"@aztec/blob-lib": "1.1.0",
|
|
36
|
+
"@aztec/foundation": "1.1.0",
|
|
37
|
+
"@aztec/l1-artifacts": "1.1.0",
|
|
38
38
|
"@viem/anvil": "^0.0.10",
|
|
39
39
|
"dotenv": "^16.0.3",
|
|
40
|
+
"lodash.pickby": "^4.5.0",
|
|
40
41
|
"tslib": "^2.4.0",
|
|
41
42
|
"viem": "2.23.7",
|
|
42
43
|
"zod": "^3.23.8"
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@jest/globals": "^30.0.0",
|
|
46
47
|
"@types/jest": "^30.0.0",
|
|
48
|
+
"@types/lodash.pickby": "^4",
|
|
47
49
|
"@types/node": "^22.15.17",
|
|
48
50
|
"@viem/anvil": "^0.0.10",
|
|
49
51
|
"get-port": "^7.1.0",
|
package/src/config.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ConfigMappingsType,
|
|
3
|
+
type NetworkNames,
|
|
3
4
|
bigintConfigHelper,
|
|
4
5
|
booleanConfigHelper,
|
|
5
6
|
getConfigFromMappings,
|
|
6
7
|
numberConfigHelper,
|
|
7
8
|
} from '@aztec/foundation/config';
|
|
9
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
8
10
|
|
|
9
11
|
import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from './l1_tx_utils.js';
|
|
10
12
|
|
|
@@ -42,6 +44,8 @@ export type L1ContractsConfig = {
|
|
|
42
44
|
manaTarget: bigint;
|
|
43
45
|
/** The proving cost per mana */
|
|
44
46
|
provingCostPerMana: bigint;
|
|
47
|
+
/** The number of seconds to wait for an exit */
|
|
48
|
+
exitDelaySeconds: number;
|
|
45
49
|
} & L1TxUtilsConfig;
|
|
46
50
|
|
|
47
51
|
export const DefaultL1ContractsConfig = {
|
|
@@ -52,19 +56,73 @@ export const DefaultL1ContractsConfig = {
|
|
|
52
56
|
aztecProofSubmissionEpochs: 1, // you have a full epoch to submit a proof after the epoch to prove ends
|
|
53
57
|
depositAmount: BigInt(100e18),
|
|
54
58
|
minimumStake: BigInt(50e18),
|
|
55
|
-
slashingQuorum:
|
|
56
|
-
slashingRoundSize:
|
|
57
|
-
governanceProposerQuorum:
|
|
58
|
-
governanceProposerRoundSize:
|
|
59
|
+
slashingQuorum: 101,
|
|
60
|
+
slashingRoundSize: 200,
|
|
61
|
+
governanceProposerQuorum: 151,
|
|
62
|
+
governanceProposerRoundSize: 300,
|
|
59
63
|
manaTarget: BigInt(1e10),
|
|
60
64
|
provingCostPerMana: BigInt(100),
|
|
65
|
+
exitDelaySeconds: 2 * 24 * 60 * 60,
|
|
61
66
|
} satisfies L1ContractsConfig;
|
|
62
67
|
|
|
68
|
+
const LocalGovernanceConfiguration = {
|
|
69
|
+
proposeConfig: {
|
|
70
|
+
lockDelay: 60n * 60n * 24n * 30n,
|
|
71
|
+
lockAmount: 1n * 10n ** 24n,
|
|
72
|
+
},
|
|
73
|
+
votingDelay: 60n,
|
|
74
|
+
votingDuration: 60n * 60n,
|
|
75
|
+
executionDelay: 60n,
|
|
76
|
+
gracePeriod: 60n * 60n * 24n * 7n,
|
|
77
|
+
quorum: 1n * 10n ** 17n,
|
|
78
|
+
voteDifferential: 4n * 10n ** 16n,
|
|
79
|
+
minimumVotes: 400n * 10n ** 18n,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const TestnetGovernanceConfiguration = {
|
|
83
|
+
proposeConfig: {
|
|
84
|
+
lockDelay: 60n * 60n * 24n,
|
|
85
|
+
lockAmount: DefaultL1ContractsConfig.depositAmount * 100n,
|
|
86
|
+
},
|
|
87
|
+
votingDelay: 60n,
|
|
88
|
+
votingDuration: 60n * 60n,
|
|
89
|
+
executionDelay: 60n * 60n * 24n,
|
|
90
|
+
gracePeriod: 60n * 60n * 24n * 7n,
|
|
91
|
+
quorum: 3n * 10n ** 17n,
|
|
92
|
+
voteDifferential: 4n * 10n ** 16n,
|
|
93
|
+
minimumVotes: DefaultL1ContractsConfig.minimumStake * 200n,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const getGovernanceConfiguration = (networkName: NetworkNames) => {
|
|
97
|
+
if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
98
|
+
return TestnetGovernanceConfiguration;
|
|
99
|
+
}
|
|
100
|
+
return LocalGovernanceConfiguration;
|
|
101
|
+
};
|
|
102
|
+
|
|
63
103
|
// Making a default config here as we are only using it thought the deployment
|
|
64
104
|
// and do not expect to be using different setups, so having environment variables
|
|
65
105
|
// for it seems overkill
|
|
66
|
-
|
|
106
|
+
const LocalRewardConfig = {
|
|
107
|
+
sequencerBps: 5000,
|
|
108
|
+
rewardDistributor: EthAddress.ZERO.toString(),
|
|
109
|
+
booster: EthAddress.ZERO.toString(),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const TestnetRewardConfig = {
|
|
67
113
|
sequencerBps: 5000,
|
|
114
|
+
rewardDistributor: EthAddress.ZERO.toString(),
|
|
115
|
+
booster: EthAddress.ZERO.toString(),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const getRewardConfig = (networkName: NetworkNames) => {
|
|
119
|
+
if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
120
|
+
return TestnetRewardConfig;
|
|
121
|
+
}
|
|
122
|
+
return LocalRewardConfig;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const LocalRewardBoostConfig = {
|
|
68
126
|
increment: 200000,
|
|
69
127
|
maxScore: 5000000,
|
|
70
128
|
a: 5000,
|
|
@@ -72,10 +130,41 @@ export const DefaultRewardConfig = {
|
|
|
72
130
|
minimum: 100000,
|
|
73
131
|
};
|
|
74
132
|
|
|
133
|
+
const TestnetRewardBoostConfig = {
|
|
134
|
+
increment: 125000,
|
|
135
|
+
maxScore: 15000000,
|
|
136
|
+
a: 1000,
|
|
137
|
+
k: 1000000,
|
|
138
|
+
minimum: 100000,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const getRewardBoostConfig = (networkName: NetworkNames) => {
|
|
142
|
+
if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
143
|
+
return TestnetRewardBoostConfig;
|
|
144
|
+
}
|
|
145
|
+
return LocalRewardBoostConfig;
|
|
146
|
+
};
|
|
147
|
+
|
|
75
148
|
// Similar to the above, no need for environment variables for this.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
149
|
+
const LocalEntryQueueConfig = {
|
|
150
|
+
bootstrapValidatorSetSize: 0,
|
|
151
|
+
bootstrapFlushSize: 0,
|
|
152
|
+
normalFlushSizeMin: 48,
|
|
153
|
+
normalFlushSizeQuotient: 2,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const TestnetEntryQueueConfig = {
|
|
157
|
+
bootstrapValidatorSetSize: 750,
|
|
158
|
+
bootstrapFlushSize: 75,
|
|
159
|
+
normalFlushSizeMin: 1,
|
|
160
|
+
normalFlushSizeQuotient: 2475,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const getEntryQueueConfig = (networkName: NetworkNames) => {
|
|
164
|
+
if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
165
|
+
return TestnetEntryQueueConfig;
|
|
166
|
+
}
|
|
167
|
+
return LocalEntryQueueConfig;
|
|
79
168
|
};
|
|
80
169
|
|
|
81
170
|
export const l1ContractsConfigMappings: ConfigMappingsType<L1ContractsConfig> = {
|
|
@@ -144,6 +233,11 @@ export const l1ContractsConfigMappings: ConfigMappingsType<L1ContractsConfig> =
|
|
|
144
233
|
description: 'The proving cost per mana',
|
|
145
234
|
...bigintConfigHelper(DefaultL1ContractsConfig.provingCostPerMana),
|
|
146
235
|
},
|
|
236
|
+
exitDelaySeconds: {
|
|
237
|
+
env: 'AZTEC_EXIT_DELAY_SECONDS',
|
|
238
|
+
description: 'The delay before a validator can exit the set',
|
|
239
|
+
...numberConfigHelper(DefaultL1ContractsConfig.exitDelaySeconds),
|
|
240
|
+
},
|
|
147
241
|
...l1TxUtilsConfigMappings,
|
|
148
242
|
};
|
|
149
243
|
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
2
2
|
import { EmpireBaseAbi } from '@aztec/l1-artifacts/EmpireBaseAbi';
|
|
3
3
|
|
|
4
|
-
import { type Hex,
|
|
4
|
+
import { type Hex, encodeFunctionData, hashTypedData } from 'viem';
|
|
5
5
|
|
|
6
6
|
import type { L1TxRequest } from '../l1_tx_utils.js';
|
|
7
|
-
import type { ExtendedViemWalletClient } from '../types.js';
|
|
8
7
|
|
|
9
8
|
export interface IEmpireBase {
|
|
10
9
|
getRoundInfo(rollupAddress: Hex, round: bigint): Promise<{ lastVote: bigint; leader: Hex; executed: boolean }>;
|
|
11
10
|
computeRound(slot: bigint): Promise<bigint>;
|
|
12
11
|
createVoteRequest(payload: Hex): L1TxRequest;
|
|
13
|
-
createVoteRequestWithSignature(
|
|
12
|
+
createVoteRequestWithSignature(
|
|
13
|
+
payload: Hex,
|
|
14
|
+
chainId: number,
|
|
15
|
+
signerAddress: Hex,
|
|
16
|
+
signer: (msg: Hex) => Promise<Hex>,
|
|
17
|
+
): Promise<L1TxRequest>;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export function encodeVote(payload: Hex): Hex {
|
|
@@ -39,7 +43,7 @@ export function encodeVoteWithSignature(payload: Hex, signature: Signature) {
|
|
|
39
43
|
* @returns The EIP-712 signature
|
|
40
44
|
*/
|
|
41
45
|
export async function signVoteWithSig(
|
|
42
|
-
|
|
46
|
+
signer: (msg: Hex) => Promise<Hex>,
|
|
43
47
|
proposal: Hex,
|
|
44
48
|
nonce: bigint,
|
|
45
49
|
verifyingContract: Hex,
|
|
@@ -64,16 +68,6 @@ export async function signVoteWithSig(
|
|
|
64
68
|
nonce,
|
|
65
69
|
};
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const signatureHex = await walletClient.signTypedData({
|
|
72
|
-
account: walletClient.account.address,
|
|
73
|
-
domain,
|
|
74
|
-
types,
|
|
75
|
-
primaryType: 'Vote',
|
|
76
|
-
message,
|
|
77
|
-
});
|
|
78
|
-
return Signature.fromString(signatureHex);
|
|
71
|
+
const msg = hashTypedData({ domain, types, primaryType: 'Vote', message });
|
|
72
|
+
return Signature.fromString(await signer(msg));
|
|
79
73
|
}
|
|
@@ -5,7 +5,7 @@ import { GovernanceProposerAbi } from '@aztec/l1-artifacts/GovernanceProposerAbi
|
|
|
5
5
|
import { type GetContractReturnType, type Hex, type TransactionReceipt, encodeFunctionData, getContract } from 'viem';
|
|
6
6
|
|
|
7
7
|
import type { GasPrice, L1TxRequest, L1TxUtils } from '../l1_tx_utils.js';
|
|
8
|
-
import type {
|
|
8
|
+
import type { ViemClient } from '../types.js';
|
|
9
9
|
import { type IEmpireBase, encodeVote, encodeVoteWithSignature, signVoteWithSig } from './empire_base.js';
|
|
10
10
|
import { extractProposalIdFromLogs } from './governance.js';
|
|
11
11
|
|
|
@@ -52,12 +52,7 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
52
52
|
rollupAddress: Hex,
|
|
53
53
|
round: bigint,
|
|
54
54
|
): Promise<{ lastVote: bigint; leader: Hex; executed: boolean }> {
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
lastVote: roundInfo[0],
|
|
58
|
-
leader: roundInfo[1],
|
|
59
|
-
executed: roundInfo[2],
|
|
60
|
-
};
|
|
55
|
+
return await this.proposer.read.getRoundData([rollupAddress, round]);
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
public getProposalVotes(rollupAddress: Hex, round: bigint, proposal: Hex): Promise<bigint> {
|
|
@@ -71,9 +66,14 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
71
66
|
};
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
public async createVoteRequestWithSignature(
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
public async createVoteRequestWithSignature(
|
|
70
|
+
payload: Hex,
|
|
71
|
+
chainId: number,
|
|
72
|
+
signerAddress: Hex,
|
|
73
|
+
signer: (msg: Hex) => Promise<Hex>,
|
|
74
|
+
): Promise<L1TxRequest> {
|
|
75
|
+
const nonce = await this.getNonce(signerAddress);
|
|
76
|
+
const signature = await signVoteWithSig(signer, payload, nonce, this.address.toString(), chainId);
|
|
77
77
|
return {
|
|
78
78
|
to: this.address.toString(),
|
|
79
79
|
data: encodeVoteWithSignature(payload, signature),
|
|
@@ -5,6 +5,7 @@ import { type EncodeFunctionDataParameters, type Hex, encodeFunctionData, multic
|
|
|
5
5
|
|
|
6
6
|
import type { L1BlobInputs, L1GasConfig, L1TxRequest, L1TxUtils } from '../l1_tx_utils.js';
|
|
7
7
|
import type { ExtendedViemWalletClient } from '../types.js';
|
|
8
|
+
import { FormattedViemError, formatViemError } from '../utils.js';
|
|
8
9
|
import { RollupContract } from './rollup.js';
|
|
9
10
|
|
|
10
11
|
export const MULTI_CALL_3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11' as const;
|
|
@@ -32,61 +33,87 @@ export class Multicall3 {
|
|
|
32
33
|
|
|
33
34
|
const encodedForwarderData = encodeFunctionData(forwarderFunctionData);
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
try {
|
|
37
|
+
const { receipt, gasPrice } = await l1TxUtils.sendAndMonitorTransaction(
|
|
38
|
+
{
|
|
39
|
+
to: MULTI_CALL_3_ADDRESS,
|
|
40
|
+
data: encodedForwarderData,
|
|
41
|
+
},
|
|
42
|
+
gasConfig,
|
|
43
|
+
blobConfig,
|
|
44
|
+
);
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
if (receipt.status === 'success') {
|
|
47
|
+
const stats = await l1TxUtils.getTransactionStats(receipt.transactionHash);
|
|
48
|
+
return { receipt, gasPrice, stats };
|
|
49
|
+
} else {
|
|
50
|
+
logger.error('Forwarder transaction failed', undefined, { receipt });
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const args = {
|
|
53
|
+
...forwarderFunctionData,
|
|
54
|
+
address: MULTI_CALL_3_ADDRESS,
|
|
55
|
+
};
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
let errorMsg: string | undefined;
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
blobs: blobConfig.blobs,
|
|
68
|
-
kzg: blobConfig.kzg,
|
|
69
|
-
maxFeePerBlobGas,
|
|
70
|
-
},
|
|
71
|
-
[
|
|
59
|
+
if (blobConfig) {
|
|
60
|
+
const maxFeePerBlobGas = blobConfig.maxFeePerBlobGas ?? gasPrice.maxFeePerBlobGas;
|
|
61
|
+
if (maxFeePerBlobGas === undefined) {
|
|
62
|
+
errorMsg = 'maxFeePerBlobGas is required to get the error message';
|
|
63
|
+
} else {
|
|
64
|
+
logger.debug('Trying to get error from reverted tx with blob config');
|
|
65
|
+
errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(
|
|
66
|
+
encodedForwarderData,
|
|
67
|
+
args,
|
|
72
68
|
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true),
|
|
77
|
-
value: toPaddedHex(0n, true),
|
|
78
|
-
},
|
|
79
|
-
],
|
|
69
|
+
blobs: blobConfig.blobs,
|
|
70
|
+
kzg: blobConfig.kzg,
|
|
71
|
+
maxFeePerBlobGas,
|
|
80
72
|
},
|
|
81
|
-
|
|
82
|
-
|
|
73
|
+
[
|
|
74
|
+
{
|
|
75
|
+
address: rollupAddress,
|
|
76
|
+
stateDiff: [
|
|
77
|
+
{
|
|
78
|
+
slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true),
|
|
79
|
+
value: toPaddedHex(0n, true),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
logger.debug('Trying to get error from reverted tx without blob config');
|
|
88
|
+
errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(encodedForwarderData, args, undefined, []);
|
|
83
89
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(encodedForwarderData, args, undefined, []);
|
|
90
|
+
|
|
91
|
+
return { receipt, gasPrice, errorMsg };
|
|
87
92
|
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
for (const request of requests) {
|
|
95
|
+
logger.debug('Simulating request', { request });
|
|
96
|
+
const result = await l1TxUtils
|
|
97
|
+
.simulate(request, undefined, [
|
|
98
|
+
{
|
|
99
|
+
address: rollupAddress,
|
|
100
|
+
stateDiff: [
|
|
101
|
+
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
])
|
|
105
|
+
.catch(err => formatViemError(err, request.abi));
|
|
106
|
+
if (result instanceof FormattedViemError) {
|
|
107
|
+
logger.error('Found error in simulation', result, {
|
|
108
|
+
to: request.to ?? 'null',
|
|
109
|
+
data: request.data,
|
|
110
|
+
});
|
|
88
111
|
|
|
89
|
-
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
logger.warn('Failed to get error from reverted tx', { err });
|
|
116
|
+
throw err;
|
|
90
117
|
}
|
|
91
118
|
}
|
|
92
119
|
}
|
package/src/contracts/rollup.ts
CHANGED
|
@@ -22,6 +22,11 @@ export type ViemCommitteeAttestation = {
|
|
|
22
22
|
signature: ViemSignature;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
export type ViemCommitteeAttestations = {
|
|
26
|
+
signatureIndices: `0x${string}`;
|
|
27
|
+
signaturesOrAddresses: `0x${string}`;
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
export type L1RollupContractAddresses = Pick<
|
|
26
31
|
L1ContractAddresses,
|
|
27
32
|
| 'rollupAddress'
|
|
@@ -174,6 +179,11 @@ export class RollupContract {
|
|
|
174
179
|
return this.rollup.read.getDepositAmount();
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
@memoize
|
|
183
|
+
getExitDelay() {
|
|
184
|
+
return this.rollup.read.getExitDelay();
|
|
185
|
+
}
|
|
186
|
+
|
|
177
187
|
@memoize
|
|
178
188
|
getManaTarget() {
|
|
179
189
|
return this.rollup.read.getManaTarget();
|
|
@@ -376,7 +386,7 @@ export class RollupContract {
|
|
|
376
386
|
public async validateHeader(
|
|
377
387
|
args: readonly [
|
|
378
388
|
ViemHeader,
|
|
379
|
-
|
|
389
|
+
ViemCommitteeAttestations,
|
|
380
390
|
`0x${string}`,
|
|
381
391
|
`0x${string}`,
|
|
382
392
|
{
|
|
@@ -399,6 +409,77 @@ export class RollupContract {
|
|
|
399
409
|
}
|
|
400
410
|
}
|
|
401
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Packs an array of committee attestations into the format expected by the Solidity contract
|
|
414
|
+
*
|
|
415
|
+
* @param attestations - Array of committee attestations with addresses and signatures
|
|
416
|
+
* @returns Packed attestations with bitmap and tightly packed signature/address data
|
|
417
|
+
*/
|
|
418
|
+
static packAttestations(attestations: ViemCommitteeAttestation[]): ViemCommitteeAttestations {
|
|
419
|
+
const length = attestations.length;
|
|
420
|
+
|
|
421
|
+
// Calculate bitmap size (1 bit per attestation, rounded up to nearest byte)
|
|
422
|
+
const bitmapSize = Math.ceil(length / 8);
|
|
423
|
+
const signatureIndices = new Uint8Array(bitmapSize);
|
|
424
|
+
|
|
425
|
+
// Calculate total data size needed
|
|
426
|
+
let totalDataSize = 0;
|
|
427
|
+
for (let i = 0; i < length; i++) {
|
|
428
|
+
const signature = attestations[i].signature;
|
|
429
|
+
// Check if signature is empty (v = 0)
|
|
430
|
+
const isEmpty = signature.v === 0;
|
|
431
|
+
|
|
432
|
+
if (!isEmpty) {
|
|
433
|
+
totalDataSize += 65; // v (1) + r (32) + s (32)
|
|
434
|
+
} else {
|
|
435
|
+
totalDataSize += 20; // address only
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const signaturesOrAddresses = new Uint8Array(totalDataSize);
|
|
440
|
+
let dataIndex = 0;
|
|
441
|
+
|
|
442
|
+
// Pack the data
|
|
443
|
+
for (let i = 0; i < length; i++) {
|
|
444
|
+
const attestation = attestations[i];
|
|
445
|
+
const signature = attestation.signature;
|
|
446
|
+
|
|
447
|
+
// Check if signature is empty
|
|
448
|
+
const isEmpty = signature.v === 0;
|
|
449
|
+
|
|
450
|
+
if (!isEmpty) {
|
|
451
|
+
// Set bit in bitmap (bit 7-0 in each byte, left to right)
|
|
452
|
+
const byteIndex = Math.floor(i / 8);
|
|
453
|
+
const bitIndex = 7 - (i % 8);
|
|
454
|
+
signatureIndices[byteIndex] |= 1 << bitIndex;
|
|
455
|
+
|
|
456
|
+
// Pack signature: v + r + s
|
|
457
|
+
signaturesOrAddresses[dataIndex] = signature.v;
|
|
458
|
+
dataIndex++;
|
|
459
|
+
|
|
460
|
+
// Pack r (32 bytes)
|
|
461
|
+
const rBytes = Buffer.from(signature.r.slice(2), 'hex');
|
|
462
|
+
signaturesOrAddresses.set(rBytes, dataIndex);
|
|
463
|
+
dataIndex += 32;
|
|
464
|
+
|
|
465
|
+
// Pack s (32 bytes)
|
|
466
|
+
const sBytes = Buffer.from(signature.s.slice(2), 'hex');
|
|
467
|
+
signaturesOrAddresses.set(sBytes, dataIndex);
|
|
468
|
+
dataIndex += 32;
|
|
469
|
+
} else {
|
|
470
|
+
// Pack address only (20 bytes)
|
|
471
|
+
const addrBytes = Buffer.from(attestation.addr.slice(2), 'hex');
|
|
472
|
+
signaturesOrAddresses.set(addrBytes, dataIndex);
|
|
473
|
+
dataIndex += 20;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
signatureIndices: `0x${Buffer.from(signatureIndices).toString('hex')}`,
|
|
479
|
+
signaturesOrAddresses: `0x${Buffer.from(signaturesOrAddresses).toString('hex')}`,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
402
483
|
/**
|
|
403
484
|
* @notice Calls `canProposeAtTime` with the time of the next Ethereum block and the sender address
|
|
404
485
|
*
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from 'viem';
|
|
13
13
|
|
|
14
14
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils.js';
|
|
15
|
-
import type {
|
|
15
|
+
import type { ViemClient } from '../types.js';
|
|
16
16
|
import { FormattedViemError } from '../utils.js';
|
|
17
17
|
import { type IEmpireBase, encodeVote, encodeVoteWithSignature, signVoteWithSig } from './empire_base.js';
|
|
18
18
|
|
|
@@ -57,12 +57,7 @@ export class SlashingProposerContract extends EventEmitter implements IEmpireBas
|
|
|
57
57
|
rollupAddress: Hex,
|
|
58
58
|
round: bigint,
|
|
59
59
|
): Promise<{ lastVote: bigint; leader: Hex; executed: boolean }> {
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
lastVote: roundInfo[0],
|
|
63
|
-
leader: roundInfo[1],
|
|
64
|
-
executed: roundInfo[2],
|
|
65
|
-
};
|
|
60
|
+
return await this.proposer.read.getRoundData([rollupAddress, round]);
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
public getProposalVotes(rollupAddress: Hex, round: bigint, proposal: Hex): Promise<bigint> {
|
|
@@ -76,9 +71,14 @@ export class SlashingProposerContract extends EventEmitter implements IEmpireBas
|
|
|
76
71
|
};
|
|
77
72
|
}
|
|
78
73
|
|
|
79
|
-
public async createVoteRequestWithSignature(
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
public async createVoteRequestWithSignature(
|
|
75
|
+
payload: Hex,
|
|
76
|
+
chainId: number,
|
|
77
|
+
signerAddress: Hex,
|
|
78
|
+
signer: (msg: Hex) => Promise<Hex>,
|
|
79
|
+
): Promise<L1TxRequest> {
|
|
80
|
+
const nonce = await this.getNonce(signerAddress);
|
|
81
|
+
const signature = await signVoteWithSig(signer, payload, nonce, this.address.toString(), chainId);
|
|
82
82
|
return {
|
|
83
83
|
to: this.address.toString(),
|
|
84
84
|
data: encodeVoteWithSignature(payload, signature),
|
|
@@ -152,7 +152,7 @@ export class SlashingProposerContract extends EventEmitter implements IEmpireBas
|
|
|
152
152
|
{
|
|
153
153
|
// Gas estimation is way off for this, likely because we are creating the contract/selector to call
|
|
154
154
|
// for the actual slashing dynamically.
|
|
155
|
-
gasLimitBufferPercentage:
|
|
155
|
+
gasLimitBufferPercentage: 50, // +50% gas
|
|
156
156
|
},
|
|
157
157
|
)
|
|
158
158
|
.catch(err => {
|