@aztec/ethereum 0.69.0-devnet → 0.69.1-devnet
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/deploy_l1_contracts.js +3 -3
- package/dest/l1_tx_utils.d.ts +24 -9
- package/dest/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils.js +154 -56
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +15 -8
- package/dest/test/tx_delayer.js +4 -4
- package/dest/utils.d.ts +2 -0
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +95 -2
- package/package.json +3 -4
- package/src/deploy_l1_contracts.ts +2 -2
- package/src/l1_tx_utils.ts +187 -65
- package/src/test/start_anvil.ts +17 -8
- package/src/test/tx_delayer.ts +3 -3
- package/src/utils.ts +109 -0
package/dest/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodeEventLog, } from 'viem';
|
|
1
|
+
import { BaseError, ContractFunctionRevertedError, decodeEventLog, } from 'viem';
|
|
2
2
|
export function extractEvent(logs, address, abi, eventName, filter, logger) {
|
|
3
3
|
const event = tryExtractEvent(logs, address, abi, eventName, filter, logger);
|
|
4
4
|
if (!event) {
|
|
@@ -24,4 +24,97 @@ function tryExtractEvent(logs, address, abi, eventName, filter, logger) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
export function prettyLogViemErrorMsg(err) {
|
|
28
|
+
if (err instanceof BaseError) {
|
|
29
|
+
const revertError = err.walk(err => err instanceof ContractFunctionRevertedError);
|
|
30
|
+
if (revertError instanceof ContractFunctionRevertedError) {
|
|
31
|
+
const errorName = revertError.data?.errorName ?? '';
|
|
32
|
+
const args = revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : '';
|
|
33
|
+
return `${errorName}${args}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return err?.message ?? err;
|
|
37
|
+
}
|
|
38
|
+
export function formatViemError(error) {
|
|
39
|
+
const truncateHex = (hex, length = 100) => {
|
|
40
|
+
if (!hex || typeof hex !== 'string') {
|
|
41
|
+
return hex;
|
|
42
|
+
}
|
|
43
|
+
if (!hex.startsWith('0x')) {
|
|
44
|
+
return hex;
|
|
45
|
+
}
|
|
46
|
+
if (hex.length <= length * 2) {
|
|
47
|
+
return hex;
|
|
48
|
+
}
|
|
49
|
+
return `${hex.slice(0, length)}...${hex.slice(-length)}`;
|
|
50
|
+
};
|
|
51
|
+
const formatRequestBody = (body) => {
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(body);
|
|
54
|
+
// Recursively process all parameters that might contain hex strings
|
|
55
|
+
const processParams = (obj) => {
|
|
56
|
+
if (Array.isArray(obj)) {
|
|
57
|
+
return obj.map(item => processParams(item));
|
|
58
|
+
}
|
|
59
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
60
|
+
const result = {};
|
|
61
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
62
|
+
result[key] = processParams(value);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
if (typeof obj === 'string') {
|
|
67
|
+
if (obj.startsWith('0x')) {
|
|
68
|
+
return truncateHex(obj);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return obj;
|
|
72
|
+
};
|
|
73
|
+
// Process the entire request body
|
|
74
|
+
const processed = processParams(parsed);
|
|
75
|
+
return JSON.stringify(processed, null, 2);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return body;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const truncateHexStringsInText = (text) => {
|
|
82
|
+
const hexRegex = /\b0x[a-fA-F0-9]{10,}/g;
|
|
83
|
+
return text.replace(hexRegex, hex => truncateHex(hex));
|
|
84
|
+
};
|
|
85
|
+
const extractAndFormatRequestBody = (message) => {
|
|
86
|
+
// First handle Request body JSON
|
|
87
|
+
const requestBodyRegex = /Request body: ({[\s\S]*?})\n/g;
|
|
88
|
+
let result = message.replace(requestBodyRegex, (match, body) => {
|
|
89
|
+
return `Request body: ${formatRequestBody(body)}\n`;
|
|
90
|
+
});
|
|
91
|
+
// Then handle Arguments section
|
|
92
|
+
const argsRegex = /((?:Request |Estimate Gas )?Arguments:[\s\S]*?(?=\n\n|$))/g;
|
|
93
|
+
result = result.replace(argsRegex, section => {
|
|
94
|
+
const lines = section.split('\n');
|
|
95
|
+
const processedLines = lines.map(line => {
|
|
96
|
+
// Check if line contains a colon followed by content
|
|
97
|
+
const colonIndex = line.indexOf(':');
|
|
98
|
+
if (colonIndex !== -1) {
|
|
99
|
+
const [prefix, content] = [line.slice(0, colonIndex + 1), line.slice(colonIndex + 1).trim()];
|
|
100
|
+
// If content contains a hex string, truncate it
|
|
101
|
+
if (content.includes('0x')) {
|
|
102
|
+
const hexMatches = content.match(/0x[a-fA-F0-9]+/g) || [];
|
|
103
|
+
let processedContent = content;
|
|
104
|
+
hexMatches.forEach(hex => {
|
|
105
|
+
processedContent = processedContent.replace(hex, truncateHex(hex));
|
|
106
|
+
});
|
|
107
|
+
return `${prefix} ${processedContent}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return line;
|
|
111
|
+
});
|
|
112
|
+
return processedLines.join('\n');
|
|
113
|
+
});
|
|
114
|
+
// Finally, catch any remaining hex strings in the message
|
|
115
|
+
result = truncateHexStringsInText(result);
|
|
116
|
+
return result;
|
|
117
|
+
};
|
|
118
|
+
return JSON.stringify({ error: extractAndFormatRequestBody(error?.message || String(error)) }, null, 2).replace(/\\n/g, '\n');
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxFQUVMLFNBQVMsRUFFVCw2QkFBNkIsRUFJN0IsY0FBYyxHQUNmLE1BQU0sTUFBTSxDQUFDO0FBU2QsTUFBTSxVQUFVLFlBQVksQ0FLMUIsSUFBVyxFQUNYLE9BQVksRUFDWixHQUFTLEVBQ1QsU0FBcUIsRUFDckIsTUFBcUMsRUFDckMsTUFBZTtJQUVmLE1BQU0sS0FBSyxHQUFHLGVBQWUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzdFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNYLE1BQU0sSUFBSSxLQUFLLENBQUMsaUNBQWlDLFNBQVMsaUJBQWlCLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDeEYsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVELFNBQVMsZUFBZSxDQUt0QixJQUFXLEVBQ1gsT0FBWSxFQUNaLEdBQVMsRUFDVCxTQUFxQixFQUNyQixNQUFxQyxFQUNyQyxNQUFlO0lBRWYsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN2QixJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLEtBQUssT0FBTyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7WUFDeEQsSUFBSSxDQUFDO2dCQUNILE1BQU0sWUFBWSxHQUFHLGNBQWMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUM7Z0JBQ3JELElBQUksWUFBWSxDQUFDLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztvQkFDekMsTUFBTSxhQUFhLEdBQUcsWUFBMEIsQ0FBQztvQkFDakQsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQzt3QkFDckMsT0FBTyxhQUFhLENBQUM7b0JBQ3ZCLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNiLE1BQU0sRUFBRSxJQUFJLENBQUMsMkNBQTJDLE9BQU8sS0FBSyxHQUFHLEVBQUUsQ0FBQyxDQUFDO1lBQzdFLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUM7QUFFRCxNQUFNLFVBQVUscUJBQXFCLENBQUMsR0FBUTtJQUM1QyxJQUFJLEdBQUcsWUFBWSxTQUFTLEVBQUUsQ0FBQztRQUM3QixNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxZQUFZLDZCQUE2QixDQUFDLENBQUM7UUFDbEYsSUFBSSxXQUFXLFlBQVksNkJBQTZCLEVBQUUsQ0FBQztZQUN6RCxNQUFNLFNBQVMsR0FBRyxXQUFXLENBQUMsSUFBSSxFQUFFLFNBQVMsSUFBSSxFQUFFLENBQUM7WUFDcEQsTUFBTSxJQUFJLEdBQ1IsV0FBVyxDQUFDLFlBQVksSUFBSSxXQUFXLENBQUMsWUFBWSxFQUFFLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNsSCxPQUFPLEdBQUcsU0FBUyxHQUFHLElBQUksRUFBRSxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxHQUFHLEVBQUUsT0FBTyxJQUFJLEdBQUcsQ0FBQztBQUM3QixDQUFDO0FBRUQsTUFBTSxVQUFVLGVBQWUsQ0FBQyxLQUFVO0lBQ3hDLE1BQU0sV0FBVyxHQUFHLENBQUMsR0FBVyxFQUFFLE1BQU0sR0FBRyxHQUFHLEVBQUUsRUFBRTtRQUNoRCxJQUFJLENBQUMsR0FBRyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3BDLE9BQU8sR0FBRyxDQUFDO1FBQ2IsQ0FBQztRQUNELElBQUksQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDMUIsT0FBTyxHQUFHLENBQUM7UUFDYixDQUFDO1FBQ0QsSUFBSSxHQUFHLENBQUMsTUFBTSxJQUFJLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM3QixPQUFPLEdBQUcsQ0FBQztRQUNiLENBQUM7UUFDRCxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7SUFDM0QsQ0FBQyxDQUFDO0lBRUYsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFO1FBQ3pDLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFaEMsb0VBQW9FO1lBQ3BFLE1BQU0sYUFBYSxHQUFHLENBQUMsR0FBUSxFQUFPLEVBQUU7Z0JBQ3RDLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO29CQUN2QixPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztnQkFDOUMsQ0FBQztnQkFDRCxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUM7b0JBQzVDLE1BQU0sTUFBTSxHQUFRLEVBQUUsQ0FBQztvQkFDdkIsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQzt3QkFDL0MsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztvQkFDckMsQ0FBQztvQkFDRCxPQUFPLE1BQU0sQ0FBQztnQkFDaEIsQ0FBQztnQkFDRCxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO29CQUM1QixJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQzt3QkFDekIsT0FBTyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUM7b0JBQzFCLENBQUM7Z0JBQ0gsQ0FBQztnQkFDRCxPQUFPLEdBQUcsQ0FBQztZQUNiLENBQUMsQ0FBQztZQUVGLGtDQUFrQztZQUNsQyxNQUFNLFNBQVMsR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDeEMsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDNUMsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUMsQ0FBQztJQUVGLE1BQU0sd0JBQXdCLEdBQUcsQ0FBQyxJQUFZLEVBQVUsRUFBRTtRQUN4RCxNQUFNLFFBQVEsR0FBRyx1QkFBdUIsQ0FBQztRQUN6QyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDekQsQ0FBQyxDQUFDO0lBRUYsTUFBTSwyQkFBMkIsR0FBRyxDQUFDLE9BQWUsRUFBVSxFQUFFO1FBQzlELGlDQUFpQztRQUNqQyxNQUFNLGdCQUFnQixHQUFHLCtCQUErQixDQUFDO1FBQ3pELElBQUksTUFBTSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDN0QsT0FBTyxpQkFBaUIsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztRQUN0RCxDQUFDLENBQUMsQ0FBQztRQUVILGdDQUFnQztRQUNoQyxNQUFNLFNBQVMsR0FBRyw0REFBNEQsQ0FBQztRQUMvRSxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLEVBQUU7WUFDM0MsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNsQyxNQUFNLGNBQWMsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUN0QyxxREFBcUQ7Z0JBQ3JELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ3JDLElBQUksVUFBVSxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7b0JBQ3RCLE1BQU0sQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxVQUFVLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDN0YsZ0RBQWdEO29CQUNoRCxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQzt3QkFDM0IsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQzt3QkFDMUQsSUFBSSxnQkFBZ0IsR0FBRyxPQUFPLENBQUM7d0JBQy9CLFVBQVUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUU7NEJBQ3ZCLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7d0JBQ3JFLENBQUMsQ0FBQyxDQUFDO3dCQUNILE9BQU8sR0FBRyxNQUFNLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztvQkFDekMsQ0FBQztnQkFDSCxDQUFDO2dCQUNELE9BQU8sSUFBSSxDQUFDO1lBQ2QsQ0FBQyxDQUFDLENBQUM7WUFDSCxPQUFPLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbkMsQ0FBQyxDQUFDLENBQUM7UUFFSCwwREFBMEQ7UUFDMUQsTUFBTSxHQUFHLHdCQUF3QixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTFDLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUMsQ0FBQztJQUVGLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSwyQkFBMkIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FDN0csTUFBTSxFQUNOLElBQUksQ0FDTCxDQUFDO0FBQ0osQ0FBQyJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/ethereum",
|
|
3
|
-
"version": "0.69.
|
|
3
|
+
"version": "0.69.1-devnet",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -29,11 +29,10 @@
|
|
|
29
29
|
"../package.common.json"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@aztec/foundation": "0.69.
|
|
33
|
-
"@aztec/l1-artifacts": "0.69.
|
|
32
|
+
"@aztec/foundation": "0.69.1-devnet",
|
|
33
|
+
"@aztec/l1-artifacts": "0.69.1-devnet",
|
|
34
34
|
"@viem/anvil": "^0.0.10",
|
|
35
35
|
"dotenv": "^16.0.3",
|
|
36
|
-
"get-port": "^7.1.0",
|
|
37
36
|
"tslib": "^2.4.0",
|
|
38
37
|
"viem": "^2.7.15",
|
|
39
38
|
"zod": "^3.23.8"
|
|
@@ -404,7 +404,7 @@ export const deployL1Contracts = async (
|
|
|
404
404
|
logger.verbose(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
|
|
405
405
|
|
|
406
406
|
const slashFactoryAddress = await deployer.deploy(l1Artifacts.slashFactory, [rollupAddress.toString()]);
|
|
407
|
-
logger.
|
|
407
|
+
logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
|
|
408
408
|
|
|
409
409
|
await deployer.waitForDeployments();
|
|
410
410
|
logger.verbose(`All core contracts have been deployed`);
|
|
@@ -762,7 +762,7 @@ export async function deployL1Contract(
|
|
|
762
762
|
} else {
|
|
763
763
|
// Regular deployment path
|
|
764
764
|
const deployData = encodeDeployData({ abi, bytecode, args });
|
|
765
|
-
const receipt = await l1TxUtils.sendAndMonitorTransaction({
|
|
765
|
+
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
766
766
|
to: null,
|
|
767
767
|
data: deployData,
|
|
768
768
|
});
|
package/src/l1_tx_utils.ts
CHANGED
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
formatGwei,
|
|
23
23
|
} from 'viem';
|
|
24
24
|
|
|
25
|
+
import { formatViemError } from './utils.js';
|
|
26
|
+
|
|
25
27
|
// 1_000_000_000 Gwei = 1 ETH
|
|
26
28
|
// 1_000_000_000 Wei = 1 Gwei
|
|
27
29
|
// 1_000_000_000_000_000_000 Wei = 1 ETH
|
|
@@ -30,7 +32,11 @@ const WEI_CONST = 1_000_000_000n;
|
|
|
30
32
|
|
|
31
33
|
// setting a minimum bump percentage to 10% due to geth's implementation
|
|
32
34
|
// https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298
|
|
33
|
-
const MIN_REPLACEMENT_BUMP_PERCENTAGE =
|
|
35
|
+
const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10;
|
|
36
|
+
|
|
37
|
+
// setting a minimum bump percentage to 100% due to geth's implementation
|
|
38
|
+
// https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/blobpool/config.go#L34
|
|
39
|
+
const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100;
|
|
34
40
|
|
|
35
41
|
// Avg ethereum block time is ~12s
|
|
36
42
|
const BLOCK_TIME_MS = 12_000;
|
|
@@ -39,7 +45,7 @@ export interface L1TxUtilsConfig {
|
|
|
39
45
|
/**
|
|
40
46
|
* How much to increase calculated gas limit.
|
|
41
47
|
*/
|
|
42
|
-
gasLimitBufferPercentage?:
|
|
48
|
+
gasLimitBufferPercentage?: number;
|
|
43
49
|
/**
|
|
44
50
|
* Maximum gas price in gwei
|
|
45
51
|
*/
|
|
@@ -48,14 +54,22 @@ export interface L1TxUtilsConfig {
|
|
|
48
54
|
* Minimum gas price in gwei
|
|
49
55
|
*/
|
|
50
56
|
minGwei?: bigint;
|
|
57
|
+
/**
|
|
58
|
+
* Maximum blob fee per gas in gwei
|
|
59
|
+
*/
|
|
60
|
+
maxBlobGwei?: bigint;
|
|
51
61
|
/**
|
|
52
62
|
* Priority fee bump percentage
|
|
53
63
|
*/
|
|
54
|
-
priorityFeeBumpPercentage?:
|
|
64
|
+
priorityFeeBumpPercentage?: number;
|
|
55
65
|
/**
|
|
56
66
|
* How much to increase priority fee by each attempt (percentage)
|
|
57
67
|
*/
|
|
58
|
-
priorityFeeRetryBumpPercentage?:
|
|
68
|
+
priorityFeeRetryBumpPercentage?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage config
|
|
71
|
+
*/
|
|
72
|
+
fixedPriorityFeePerGas?: number;
|
|
59
73
|
/**
|
|
60
74
|
* Maximum number of speed-up attempts
|
|
61
75
|
*/
|
|
@@ -83,7 +97,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
|
83
97
|
gasLimitBufferPercentage: {
|
|
84
98
|
description: 'How much to increase gas price by each attempt (percentage)',
|
|
85
99
|
env: 'L1_GAS_LIMIT_BUFFER_PERCENTAGE',
|
|
86
|
-
...
|
|
100
|
+
...numberConfigHelper(10),
|
|
87
101
|
},
|
|
88
102
|
minGwei: {
|
|
89
103
|
description: 'Minimum gas price in gwei',
|
|
@@ -95,15 +109,25 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
|
95
109
|
env: 'L1_GAS_PRICE_MAX',
|
|
96
110
|
...bigintConfigHelper(100n),
|
|
97
111
|
},
|
|
112
|
+
maxBlobGwei: {
|
|
113
|
+
description: 'Maximum blob fee per gas in gwei',
|
|
114
|
+
env: 'L1_BLOB_FEE_PER_GAS_MAX',
|
|
115
|
+
...bigintConfigHelper(1_500n),
|
|
116
|
+
},
|
|
98
117
|
priorityFeeBumpPercentage: {
|
|
99
118
|
description: 'How much to increase priority fee by each attempt (percentage)',
|
|
100
119
|
env: 'L1_PRIORITY_FEE_BUMP_PERCENTAGE',
|
|
101
|
-
...
|
|
120
|
+
...numberConfigHelper(20),
|
|
102
121
|
},
|
|
103
122
|
priorityFeeRetryBumpPercentage: {
|
|
104
123
|
description: 'How much to increase priority fee by each retry attempt (percentage)',
|
|
105
124
|
env: 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE',
|
|
106
|
-
...
|
|
125
|
+
...numberConfigHelper(50),
|
|
126
|
+
},
|
|
127
|
+
fixedPriorityFeePerGas: {
|
|
128
|
+
description: 'Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage',
|
|
129
|
+
env: 'L1_FIXED_PRIORITY_FEE_PER_GAS',
|
|
130
|
+
...numberConfigHelper(0),
|
|
107
131
|
},
|
|
108
132
|
maxAttempts: {
|
|
109
133
|
description: 'Maximum number of speed-up attempts',
|
|
@@ -118,7 +142,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
|
118
142
|
stallTimeMs: {
|
|
119
143
|
description: 'How long before considering tx stalled',
|
|
120
144
|
env: 'L1_TX_MONITOR_STALL_TIME_MS',
|
|
121
|
-
...numberConfigHelper(
|
|
145
|
+
...numberConfigHelper(45_000),
|
|
122
146
|
},
|
|
123
147
|
txTimeoutMs: {
|
|
124
148
|
description: 'How long to wait for a tx to be mined before giving up. Set to 0 to disable.',
|
|
@@ -143,12 +167,13 @@ export interface L1TxRequest {
|
|
|
143
167
|
export interface L1BlobInputs {
|
|
144
168
|
blobs: Uint8Array[];
|
|
145
169
|
kzg: any;
|
|
146
|
-
maxFeePerBlobGas
|
|
170
|
+
maxFeePerBlobGas?: bigint;
|
|
147
171
|
}
|
|
148
172
|
|
|
149
|
-
interface GasPrice {
|
|
173
|
+
export interface GasPrice {
|
|
150
174
|
maxFeePerGas: bigint;
|
|
151
175
|
maxPriorityFeePerGas: bigint;
|
|
176
|
+
maxFeePerBlobGas?: bigint;
|
|
152
177
|
}
|
|
153
178
|
|
|
154
179
|
export class L1TxUtils {
|
|
@@ -174,37 +199,57 @@ export class L1TxUtils {
|
|
|
174
199
|
*/
|
|
175
200
|
public async sendTransaction(
|
|
176
201
|
request: L1TxRequest,
|
|
177
|
-
_gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint },
|
|
178
|
-
|
|
202
|
+
_gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint; txTimeoutAt?: Date },
|
|
203
|
+
blobInputs?: L1BlobInputs,
|
|
179
204
|
): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
205
|
+
try {
|
|
206
|
+
const gasConfig = { ...this.config, ..._gasConfig };
|
|
207
|
+
const account = this.walletClient.account;
|
|
208
|
+
let gasLimit: bigint;
|
|
209
|
+
|
|
210
|
+
if (gasConfig.fixedGas) {
|
|
211
|
+
gasLimit = gasConfig.fixedGas;
|
|
212
|
+
} else {
|
|
213
|
+
gasLimit = await this.estimateGas(account, request);
|
|
214
|
+
}
|
|
189
215
|
|
|
190
|
-
|
|
216
|
+
const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs);
|
|
191
217
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
...blobInputs,
|
|
196
|
-
gas: gasLimit,
|
|
197
|
-
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
198
|
-
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
this.logger?.verbose(`Sent L1 transaction ${txHash}`, {
|
|
202
|
-
gasLimit,
|
|
203
|
-
maxFeePerGas: formatGwei(gasPrice.maxFeePerGas),
|
|
204
|
-
maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas),
|
|
205
|
-
});
|
|
218
|
+
if (gasConfig.txTimeoutAt && Date.now() > gasConfig.txTimeoutAt.getTime()) {
|
|
219
|
+
throw new Error('Transaction timed out before sending');
|
|
220
|
+
}
|
|
206
221
|
|
|
207
|
-
|
|
222
|
+
let txHash: Hex;
|
|
223
|
+
if (blobInputs) {
|
|
224
|
+
txHash = await this.walletClient.sendTransaction({
|
|
225
|
+
...request,
|
|
226
|
+
...blobInputs,
|
|
227
|
+
gas: gasLimit,
|
|
228
|
+
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
229
|
+
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
230
|
+
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
txHash = await this.walletClient.sendTransaction({
|
|
234
|
+
...request,
|
|
235
|
+
gas: gasLimit,
|
|
236
|
+
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
237
|
+
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
this.logger?.verbose(`Sent L1 transaction ${txHash}`, {
|
|
241
|
+
gasLimit,
|
|
242
|
+
maxFeePerGas: formatGwei(gasPrice.maxFeePerGas),
|
|
243
|
+
maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas),
|
|
244
|
+
...(gasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(gasPrice.maxFeePerBlobGas) }),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
return { txHash, gasLimit, gasPrice };
|
|
248
|
+
} catch (err: any) {
|
|
249
|
+
const formattedErr = formatViemError(err);
|
|
250
|
+
this.logger?.error(`Failed to send transaction`, formattedErr);
|
|
251
|
+
throw formattedErr;
|
|
252
|
+
}
|
|
208
253
|
}
|
|
209
254
|
|
|
210
255
|
/**
|
|
@@ -218,7 +263,7 @@ export class L1TxUtils {
|
|
|
218
263
|
request: L1TxRequest,
|
|
219
264
|
initialTxHash: Hex,
|
|
220
265
|
params: { gasLimit: bigint },
|
|
221
|
-
_gasConfig?: Partial<L1TxUtilsConfig
|
|
266
|
+
_gasConfig?: Partial<L1TxUtilsConfig> & { txTimeoutAt?: Date },
|
|
222
267
|
_blobInputs?: L1BlobInputs,
|
|
223
268
|
): Promise<TransactionReceipt> {
|
|
224
269
|
const gasConfig = { ...this.config, ..._gasConfig };
|
|
@@ -246,7 +291,12 @@ export class L1TxUtils {
|
|
|
246
291
|
let attempts = 0;
|
|
247
292
|
let lastAttemptSent = Date.now();
|
|
248
293
|
const initialTxTime = lastAttemptSent;
|
|
294
|
+
|
|
249
295
|
let txTimedOut = false;
|
|
296
|
+
const isTimedOut = () =>
|
|
297
|
+
(gasConfig.txTimeoutAt && Date.now() > gasConfig.txTimeoutAt.getTime()) ||
|
|
298
|
+
(gasConfig.txTimeoutMs !== undefined && Date.now() - initialTxTime > gasConfig.txTimeoutMs) ||
|
|
299
|
+
false;
|
|
250
300
|
|
|
251
301
|
while (!txTimedOut) {
|
|
252
302
|
try {
|
|
@@ -284,11 +334,9 @@ export class L1TxUtils {
|
|
|
284
334
|
this.logger?.debug(`L1 transaction ${currentTxHash} pending. Time passed: ${timePassed}ms.`);
|
|
285
335
|
|
|
286
336
|
// Check timeout before continuing
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
337
|
+
txTimedOut = isTimedOut();
|
|
338
|
+
if (txTimedOut) {
|
|
339
|
+
break;
|
|
292
340
|
}
|
|
293
341
|
|
|
294
342
|
await sleep(gasConfig.checkIntervalMs!);
|
|
@@ -299,15 +347,25 @@ export class L1TxUtils {
|
|
|
299
347
|
attempts++;
|
|
300
348
|
const newGasPrice = await this.getGasPrice(
|
|
301
349
|
gasConfig,
|
|
350
|
+
!!blobInputs,
|
|
302
351
|
attempts,
|
|
303
352
|
tx.maxFeePerGas && tx.maxPriorityFeePerGas
|
|
304
|
-
? {
|
|
353
|
+
? {
|
|
354
|
+
maxFeePerGas: tx.maxFeePerGas,
|
|
355
|
+
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
|
|
356
|
+
maxFeePerBlobGas: tx.maxFeePerBlobGas,
|
|
357
|
+
}
|
|
305
358
|
: undefined,
|
|
306
359
|
);
|
|
307
360
|
|
|
308
361
|
this.logger?.debug(
|
|
309
362
|
`L1 transaction ${currentTxHash} appears stuck. Attempting speed-up ${attempts}/${gasConfig.maxAttempts} ` +
|
|
310
363
|
`with new priority fee ${formatGwei(newGasPrice.maxPriorityFeePerGas)} gwei`,
|
|
364
|
+
{
|
|
365
|
+
maxFeePerGas: formatGwei(newGasPrice.maxFeePerGas),
|
|
366
|
+
maxPriorityFeePerGas: formatGwei(newGasPrice.maxPriorityFeePerGas),
|
|
367
|
+
...(newGasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(newGasPrice.maxFeePerBlobGas) }),
|
|
368
|
+
},
|
|
311
369
|
);
|
|
312
370
|
|
|
313
371
|
currentTxHash = await this.walletClient.sendTransaction({
|
|
@@ -324,16 +382,15 @@ export class L1TxUtils {
|
|
|
324
382
|
}
|
|
325
383
|
await sleep(gasConfig.checkIntervalMs!);
|
|
326
384
|
} catch (err: any) {
|
|
327
|
-
|
|
385
|
+
const formattedErr = formatViemError(err);
|
|
386
|
+
this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, formattedErr);
|
|
328
387
|
if (err.message?.includes('reverted')) {
|
|
329
|
-
throw
|
|
388
|
+
throw formattedErr;
|
|
330
389
|
}
|
|
331
390
|
await sleep(gasConfig.checkIntervalMs!);
|
|
332
391
|
}
|
|
333
392
|
// Check if tx has timed out.
|
|
334
|
-
|
|
335
|
-
txTimedOut = Date.now() - initialTxTime > gasConfig.txTimeoutMs!;
|
|
336
|
-
}
|
|
393
|
+
txTimedOut = isTimedOut();
|
|
337
394
|
}
|
|
338
395
|
throw new Error(`L1 transaction ${currentTxHash} timed out`);
|
|
339
396
|
}
|
|
@@ -346,11 +403,12 @@ export class L1TxUtils {
|
|
|
346
403
|
*/
|
|
347
404
|
public async sendAndMonitorTransaction(
|
|
348
405
|
request: L1TxRequest,
|
|
349
|
-
gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint },
|
|
406
|
+
gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint; txTimeoutAt?: Date },
|
|
350
407
|
blobInputs?: L1BlobInputs,
|
|
351
|
-
): Promise<TransactionReceipt> {
|
|
352
|
-
const { txHash, gasLimit } = await this.sendTransaction(request, gasConfig, blobInputs);
|
|
353
|
-
|
|
408
|
+
): Promise<{ receipt: TransactionReceipt; gasPrice: GasPrice }> {
|
|
409
|
+
const { txHash, gasLimit, gasPrice } = await this.sendTransaction(request, gasConfig, blobInputs);
|
|
410
|
+
const receipt = await this.monitorTransaction(request, txHash, { gasLimit }, gasConfig, blobInputs);
|
|
411
|
+
return { receipt, gasPrice };
|
|
354
412
|
}
|
|
355
413
|
|
|
356
414
|
/**
|
|
@@ -358,6 +416,7 @@ export class L1TxUtils {
|
|
|
358
416
|
*/
|
|
359
417
|
private async getGasPrice(
|
|
360
418
|
_gasConfig?: L1TxUtilsConfig,
|
|
419
|
+
isBlobTx: boolean = false,
|
|
361
420
|
attempt: number = 0,
|
|
362
421
|
previousGasPrice?: typeof attempt extends 0 ? never : GasPrice,
|
|
363
422
|
): Promise<GasPrice> {
|
|
@@ -365,26 +424,54 @@ export class L1TxUtils {
|
|
|
365
424
|
const block = await this.publicClient.getBlock({ blockTag: 'latest' });
|
|
366
425
|
const baseFee = block.baseFeePerGas ?? 0n;
|
|
367
426
|
|
|
368
|
-
// Get
|
|
369
|
-
let
|
|
427
|
+
// Get blob base fee if available
|
|
428
|
+
let blobBaseFee = 0n;
|
|
429
|
+
try {
|
|
430
|
+
const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' });
|
|
431
|
+
blobBaseFee = BigInt(blobBaseFeeHex);
|
|
432
|
+
this.logger?.debug('Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
|
|
433
|
+
} catch {
|
|
434
|
+
this.logger?.warn('Failed to get blob base fee', attempt);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let priorityFee: bigint;
|
|
438
|
+
if (gasConfig.fixedPriorityFeePerGas) {
|
|
439
|
+
this.logger?.debug('Using fixed priority fee per gas', {
|
|
440
|
+
fixedPriorityFeePerGas: gasConfig.fixedPriorityFeePerGas,
|
|
441
|
+
});
|
|
442
|
+
// try to maintain precision up to 1000000 wei
|
|
443
|
+
priorityFee = BigInt(gasConfig.fixedPriorityFeePerGas * 1_000_000) * (WEI_CONST / 1_000_000n);
|
|
444
|
+
} else {
|
|
445
|
+
// Get initial priority fee from the network
|
|
446
|
+
priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas();
|
|
447
|
+
}
|
|
370
448
|
let maxFeePerGas = baseFee;
|
|
371
449
|
|
|
450
|
+
let maxFeePerBlobGas = blobBaseFee;
|
|
451
|
+
|
|
372
452
|
// Bump base fee so it's valid for next blocks if it stalls
|
|
373
453
|
const numBlocks = Math.ceil(gasConfig.stallTimeMs! / BLOCK_TIME_MS);
|
|
374
454
|
for (let i = 0; i < numBlocks; i++) {
|
|
375
455
|
// each block can go up 12.5% from previous baseFee
|
|
376
456
|
maxFeePerGas = (maxFeePerGas * (1_000n + 125n)) / 1_000n;
|
|
457
|
+
// same for blob gas fee
|
|
458
|
+
maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n)) / 1_000n;
|
|
377
459
|
}
|
|
378
460
|
|
|
379
461
|
if (attempt > 0) {
|
|
380
462
|
const configBump =
|
|
381
463
|
gasConfig.priorityFeeRetryBumpPercentage ?? defaultL1TxUtilsConfig.priorityFeeRetryBumpPercentage!;
|
|
382
|
-
|
|
383
|
-
|
|
464
|
+
|
|
465
|
+
// if this is a blob tx, we have to use the blob bump percentage
|
|
466
|
+
const minBumpPercentage = isBlobTx ? MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE : MIN_REPLACEMENT_BUMP_PERCENTAGE;
|
|
467
|
+
|
|
468
|
+
const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage;
|
|
384
469
|
|
|
385
470
|
// Calculate minimum required fees based on previous attempt
|
|
386
|
-
|
|
387
|
-
const
|
|
471
|
+
// multiply by 100 & divide by 100 to maintain some precision
|
|
472
|
+
const minPriorityFee =
|
|
473
|
+
(previousGasPrice!.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
|
|
474
|
+
const minMaxFee = (previousGasPrice!.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
|
|
388
475
|
|
|
389
476
|
// Add priority fee to maxFeePerGas
|
|
390
477
|
maxFeePerGas += priorityFee;
|
|
@@ -393,8 +480,11 @@ export class L1TxUtils {
|
|
|
393
480
|
priorityFee = priorityFee > minPriorityFee ? priorityFee : minPriorityFee;
|
|
394
481
|
maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee;
|
|
395
482
|
} else {
|
|
396
|
-
// first attempt, just bump priority fee
|
|
397
|
-
|
|
483
|
+
// first attempt, just bump priority fee, unless it's a fixed config
|
|
484
|
+
// multiply by 100 & divide by 100 to maintain some precision
|
|
485
|
+
if (!gasConfig.fixedPriorityFeePerGas) {
|
|
486
|
+
priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n;
|
|
487
|
+
}
|
|
398
488
|
maxFeePerGas += priorityFee;
|
|
399
489
|
}
|
|
400
490
|
|
|
@@ -402,17 +492,41 @@ export class L1TxUtils {
|
|
|
402
492
|
const maxGweiInWei = gasConfig.maxGwei! * WEI_CONST;
|
|
403
493
|
maxFeePerGas = maxFeePerGas > maxGweiInWei ? maxGweiInWei : maxFeePerGas;
|
|
404
494
|
|
|
495
|
+
// Ensure we don't exceed maxBlobGwei
|
|
496
|
+
if (maxFeePerBlobGas) {
|
|
497
|
+
const maxBlobGweiInWei = gasConfig.maxBlobGwei! * WEI_CONST;
|
|
498
|
+
maxFeePerBlobGas = maxFeePerBlobGas > maxBlobGweiInWei ? maxBlobGweiInWei : maxFeePerBlobGas;
|
|
499
|
+
}
|
|
500
|
+
|
|
405
501
|
// Ensure priority fee doesn't exceed max fee
|
|
406
502
|
const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee;
|
|
407
503
|
|
|
504
|
+
if (attempt > 0 && previousGasPrice?.maxFeePerBlobGas) {
|
|
505
|
+
const bumpPercentage =
|
|
506
|
+
gasConfig.priorityFeeRetryBumpPercentage! > MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE
|
|
507
|
+
? gasConfig.priorityFeeRetryBumpPercentage!
|
|
508
|
+
: MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE;
|
|
509
|
+
|
|
510
|
+
// calculate min blob fee based on previous attempt
|
|
511
|
+
const minBlobFee = (previousGasPrice.maxFeePerBlobGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
|
|
512
|
+
|
|
513
|
+
// use max between current network values and min required values
|
|
514
|
+
maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
|
|
515
|
+
}
|
|
516
|
+
|
|
408
517
|
this.logger?.debug(`Computed gas price`, {
|
|
409
518
|
attempt,
|
|
410
519
|
baseFee: formatGwei(baseFee),
|
|
411
520
|
maxFeePerGas: formatGwei(maxFeePerGas),
|
|
412
521
|
maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
|
|
522
|
+
...(maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(maxFeePerBlobGas) }),
|
|
413
523
|
});
|
|
414
524
|
|
|
415
|
-
return {
|
|
525
|
+
return {
|
|
526
|
+
maxFeePerGas,
|
|
527
|
+
maxPriorityFeePerGas,
|
|
528
|
+
...(maxFeePerBlobGas && { maxFeePerBlobGas: maxFeePerBlobGas }),
|
|
529
|
+
};
|
|
416
530
|
}
|
|
417
531
|
|
|
418
532
|
/**
|
|
@@ -430,14 +544,22 @@ export class L1TxUtils {
|
|
|
430
544
|
// Strangely, the only way to get gas and send blobs is prepareTransactionRequest().
|
|
431
545
|
// See: https://github.com/wevm/viem/issues/2075
|
|
432
546
|
if (_blobInputs) {
|
|
433
|
-
|
|
434
|
-
|
|
547
|
+
const gasPrice = await this.getGasPrice(gasConfig, true, 0);
|
|
548
|
+
initialEstimate = (
|
|
549
|
+
await this.walletClient.prepareTransactionRequest({
|
|
550
|
+
account,
|
|
551
|
+
...request,
|
|
552
|
+
..._blobInputs,
|
|
553
|
+
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
|
|
554
|
+
})
|
|
555
|
+
)?.gas;
|
|
435
556
|
} else {
|
|
436
557
|
initialEstimate = await this.publicClient.estimateGas({ account, ...request });
|
|
437
558
|
}
|
|
438
559
|
|
|
439
560
|
// Add buffer based on either fixed amount or percentage
|
|
440
|
-
const withBuffer =
|
|
561
|
+
const withBuffer =
|
|
562
|
+
initialEstimate + (initialEstimate * BigInt((gasConfig.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
|
|
441
563
|
|
|
442
564
|
return withBuffer;
|
|
443
565
|
}
|
package/src/test/start_anvil.ts
CHANGED
|
@@ -2,38 +2,47 @@ import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
|
2
2
|
import { fileURLToPath } from '@aztec/foundation/url';
|
|
3
3
|
|
|
4
4
|
import { type Anvil, createAnvil } from '@viem/anvil';
|
|
5
|
-
import getPort from 'get-port';
|
|
6
5
|
import { dirname, resolve } from 'path';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Ensures there's a running Anvil instance and returns the RPC URL.
|
|
10
9
|
*/
|
|
11
10
|
export async function startAnvil(l1BlockTime?: number): Promise<{ anvil: Anvil; rpcUrl: string }> {
|
|
12
|
-
let ethereumHostPort: number | undefined;
|
|
13
|
-
|
|
14
11
|
const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
|
|
15
12
|
|
|
13
|
+
let port: number | undefined;
|
|
14
|
+
|
|
16
15
|
// Start anvil.
|
|
17
16
|
// We go via a wrapper script to ensure if the parent dies, anvil dies.
|
|
18
17
|
const anvil = await retry(
|
|
19
18
|
async () => {
|
|
20
|
-
ethereumHostPort = await getPort();
|
|
21
19
|
const anvil = createAnvil({
|
|
22
20
|
anvilBinary,
|
|
23
|
-
port:
|
|
21
|
+
port: 0,
|
|
24
22
|
blockTime: l1BlockTime,
|
|
23
|
+
stopTimeout: 1000,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Listen to the anvil output to get the port.
|
|
27
|
+
const removeHandler = anvil.on('message', (message: string) => {
|
|
28
|
+
if (port === undefined && message.includes('Listening on')) {
|
|
29
|
+
port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)![2]);
|
|
30
|
+
}
|
|
25
31
|
});
|
|
26
32
|
await anvil.start();
|
|
33
|
+
removeHandler();
|
|
34
|
+
|
|
27
35
|
return anvil;
|
|
28
36
|
},
|
|
29
37
|
'Start anvil',
|
|
30
38
|
makeBackoff([5, 5, 5]),
|
|
31
39
|
);
|
|
32
40
|
|
|
33
|
-
if (!
|
|
41
|
+
if (!port) {
|
|
34
42
|
throw new Error('Failed to start anvil');
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
// Monkeypatch the anvil instance to include the actually assigned port
|
|
46
|
+
Object.defineProperty(anvil, 'port', { value: port, writable: false });
|
|
47
|
+
return { anvil, rpcUrl: `http://127.0.0.1:${port}` };
|
|
39
48
|
}
|