@@ -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
0 commit comments