Skip to content

Commit 7fe0239

Browse files
committed
Add Multicall3 deployment and pagination support for Ethscriptions
- Implemented the deployment of the Multicall3 contract in L2Genesis. - Introduced pagination structures and methods in the Ethscriptions contract for efficient batch queries. - Added tests for Multicall3 functionality, including deployment verification and mixed call scenarios. - Enhanced error handling for pagination limits and added relevant constants.
1 parent 7b46837 commit 7fe0239

File tree

4 files changed

+359
-0
lines changed

4 files changed

+359
-0
lines changed

contracts/script/L2Genesis.s.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ contract L2Genesis is Script {
130130
dealEthToPrecompiles();
131131
setOPStackPredeploys();
132132
setEthscriptionsPredeploys(); // This now includes createGenesisEthscriptions()
133+
deployMulticall3();
133134

134135
// Fund dev accounts if enabled
135136
if (config.fundDevAccounts) {
@@ -263,6 +264,14 @@ contract L2Genesis is Script {
263264
vm.store(impl, ownerSlot, bytes32(uint256(uint160(config.proxyAdminOwner))));
264265
}
265266

267+
/// @notice Deploy Multicall3 contract
268+
function deployMulticall3() internal {
269+
console.log("Deploying Multicall3 at", Predeploys.MultiCall3);
270+
vm.etch(Predeploys.MultiCall3, Predeploys.MultiCall3Code);
271+
// Set nonce to 1 for consistency with other predeploys
272+
vm.setNonce(Predeploys.MultiCall3, 1);
273+
}
274+
266275
/// @notice Fund development accounts with ETH
267276
function fundDevAccounts() internal {
268277
address[] memory accounts = L2GenesisConfig.getDevAccounts();

contracts/src/Ethscriptions.sol

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ contract Ethscriptions is ERC721EthscriptionsSequentialEnumerableUpgradeable {
6060
ProtocolParams protocolParams; // Protocol operation data (optional)
6161
}
6262

63+
/// @notice Paginated result for batch queries
64+
struct PaginatedEthscriptionsResponse {
65+
Ethscription[] items;
66+
uint256 total; // total items available (totalSupply or balanceOf(owner))
67+
uint256 start; // start index used for this page
68+
uint256 limit; // effective limit used for this page (after clamping)
69+
uint256 nextStart; // next page start index (end of this page)
70+
bool hasMore; // true if nextStart < total
71+
}
72+
6373
/// @notice Complete denormalized ethscription data for external/off-chain consumption
6474
/// @dev Includes all EthscriptionStorage fields plus owner and content
6575
struct Ethscription {
@@ -99,6 +109,14 @@ contract Ethscriptions is ERC721EthscriptionsSequentialEnumerableUpgradeable {
99109
/// @dev Ethscriptions Prover contract (pre-deployed at known address)
100110
EthscriptionsProver public constant prover = EthscriptionsProver(Predeploys.ETHSCRIPTIONS_PROVER);
101111

112+
// =============================================================
113+
// PAGINATION CONSTANTS
114+
// =============================================================
115+
116+
/// @dev Maximum page sizes for pagination helpers
117+
uint256 private constant MAX_PAGE_WITH_CONTENT = 50;
118+
uint256 private constant MAX_PAGE_WITHOUT_CONTENT = 1000;
119+
102120
// =============================================================
103121
// STATE VARIABLES
104122
// =============================================================
@@ -140,6 +158,7 @@ contract Ethscriptions is ERC721EthscriptionsSequentialEnumerableUpgradeable {
140158
error PreviousOwnerMismatch();
141159
error NoSuccessfulTransfers();
142160
error TokenDoesNotExist();
161+
error InvalidPaginationLimit();
143162

144163
// =============================================================
145164
// EVENTS
@@ -464,6 +483,110 @@ contract Ethscriptions is ERC721EthscriptionsSequentialEnumerableUpgradeable {
464483
return _buildEthscription(ethscriptionId, includeContent);
465484
}
466485

486+
/// @notice Paginate all ethscriptions by global tokenId range
487+
/// @param start Starting tokenId (inclusive)
488+
/// @param limit Maximum number of items to return (clamped by includeContent)
489+
/// @param includeContent Whether to include content bytes in the returned structs
490+
/// @return page Paginated result containing items and metadata
491+
function getEthscriptions(uint256 start, uint256 limit, bool includeContent)
492+
external
493+
view
494+
returns (PaginatedEthscriptionsResponse memory page)
495+
{
496+
return _createPaginatedEthscriptionsResponse({
497+
byOwner: false,
498+
owner: address(0),
499+
start: start,
500+
limit: limit,
501+
includeContent: includeContent
502+
});
503+
}
504+
505+
/// @notice Overload with includeContent defaulting to true
506+
function getEthscriptions(uint256 start, uint256 limit)
507+
external
508+
view
509+
returns (PaginatedEthscriptionsResponse memory)
510+
{
511+
return _createPaginatedEthscriptionsResponse({
512+
byOwner: false,
513+
owner: address(0),
514+
start: start,
515+
limit: limit,
516+
includeContent: true
517+
});
518+
}
519+
520+
/// @notice Paginate ethscriptions owned by a specific address
521+
/// @param owner The owner address to filter by
522+
/// @param start Start index within the owner's token set (inclusive)
523+
/// @param limit Maximum number of items to return (clamped by includeContent)
524+
/// @param includeContent Whether to include content bytes in the returned structs
525+
/// @return page Paginated result containing items and metadata
526+
function getOwnerEthscriptions(address owner, uint256 start, uint256 limit, bool includeContent)
527+
external
528+
view
529+
returns (PaginatedEthscriptionsResponse memory page)
530+
{
531+
return _createPaginatedEthscriptionsResponse({
532+
byOwner: true,
533+
owner: owner,
534+
start: start,
535+
limit: limit,
536+
includeContent: includeContent
537+
});
538+
}
539+
540+
/// @notice Overload with includeContent defaulting to true
541+
function getOwnerEthscriptions(address owner, uint256 start, uint256 limit)
542+
external
543+
view
544+
returns (PaginatedEthscriptionsResponse memory)
545+
{
546+
return _createPaginatedEthscriptionsResponse({
547+
byOwner: true,
548+
owner: owner,
549+
start: start,
550+
limit: limit,
551+
includeContent: true
552+
});
553+
}
554+
555+
/// @notice Internal generic paginator shared by global and owner-scoped pagination
556+
function _createPaginatedEthscriptionsResponse(
557+
bool byOwner,
558+
address owner,
559+
uint256 start,
560+
uint256 limit,
561+
bool includeContent
562+
) internal view returns (PaginatedEthscriptionsResponse memory page) {
563+
if (limit == 0) revert InvalidPaginationLimit();
564+
565+
uint256 totalCount = byOwner ? balanceOf(owner) : totalSupply();
566+
page.total = totalCount;
567+
page.start = start;
568+
569+
uint256 maxPerPage = includeContent ? MAX_PAGE_WITH_CONTENT : MAX_PAGE_WITHOUT_CONTENT;
570+
uint256 effectiveLimit = limit > maxPerPage ? maxPerPage : limit;
571+
572+
uint256 endExclusive = start >= totalCount ? start : start + effectiveLimit;
573+
if (endExclusive > totalCount) endExclusive = totalCount;
574+
uint256 resultsCount = start >= totalCount ? 0 : (endExclusive - start);
575+
576+
Ethscription[] memory items = new Ethscription[](resultsCount);
577+
for (uint256 index = 0; index < resultsCount;) {
578+
uint256 tokenId = byOwner ? tokenOfOwnerByIndex(owner, start + index) : (start + index);
579+
bytes32 id = tokenIdToEthscriptionId[tokenId];
580+
items[index] = _buildEthscription(id, includeContent);
581+
unchecked { ++index; }
582+
}
583+
584+
page.items = items;
585+
page.limit = resultsCount;
586+
page.nextStart = start + resultsCount;
587+
page.hasMore = page.nextStart < totalCount;
588+
}
589+
467590
// -------------------- Internal helper for content retrieval --------------------
468591

469592
/// @notice Internal: Get content for an ethscription

contracts/src/libraries/Predeploys.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ library Predeploys {
1818
/// @notice ProxyAdmin predeploy (manages all proxy upgrades)
1919
address constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018;
2020

21+
address constant MultiCall3 = 0xcA11bde05977b3631167028862bE2a173976CA11;
22+
2123
// ============ Ethscriptions System Predeploys ============
2224
// Using 0x3300… namespace for Ethscriptions contracts
2325

@@ -67,4 +69,7 @@ library Predeploys {
6769
uint160(uint256(uint160(_addr)) & 0xffff | uint256(uint160(0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000)))
6870
);
6971
}
72+
73+
bytes internal constant MultiCall3Code =
74+
hex"6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c0033";
7075
}

0 commit comments

Comments
 (0)