Skip to content

Commit 8ff143e

Browse files
authored
Merge branch 'main' into yash/btt-vote-erc20
2 parents e005aed + 49a573c commit 8ff143e

34 files changed

+1649
-166
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ node_modules/
99
typechain/
1010

1111
# files
12+
src/test/smart-wallet/utils/AABenchmarkArtifacts.sol
1213
coverage.json

contracts/base/ERC721Base.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity ^0.8.0;
33

44
/// @author thirdweb
55

6-
import { ERC721A } from "../eip/ERC721AVirtualApprove.sol";
6+
import "../eip/queryable/ERC721AQueryable.sol";
77

88
import "../extension/ContractMetadata.sol";
99
import "../extension/Multicall.sol";
@@ -30,7 +30,7 @@ import "../lib/TWStrings.sol";
3030
* - EIP 2981 compliance for royalty support on NFT marketplaces.
3131
*/
3232

33-
contract ERC721Base is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, BatchMintMetadata {
33+
contract ERC721Base is ERC721AQueryable, ContractMetadata, Multicall, Ownable, Royalty, BatchMintMetadata {
3434
using TWStrings for uint256;
3535

3636
/*//////////////////////////////////////////////////////////////
@@ -89,7 +89,7 @@ contract ERC721Base is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, B
8989
*
9090
* @param _tokenId The tokenId of an NFT.
9191
*/
92-
function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
92+
function tokenURI(uint256 _tokenId) public view virtual override(ERC721A, IERC721Metadata) returns (string memory) {
9393
string memory fullUriForToken = fullURI[_tokenId];
9494
if (bytes(fullUriForToken).length > 0) {
9595
return fullUriForToken;
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// SPDX-License-Identifier: MIT
2+
// ERC721A Contracts v3.3.0
3+
// Creator: Chiru Labs
4+
5+
pragma solidity ^0.8.4;
6+
7+
import "./IERC721AQueryable.sol";
8+
import "../ERC721AVirtualApprove.sol";
9+
10+
/**
11+
* @title ERC721A Queryable
12+
* @dev ERC721A subclass with convenience query functions.
13+
*/
14+
abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable {
15+
/**
16+
* @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
17+
*
18+
* If the `tokenId` is out of bounds:
19+
* - `addr` = `address(0)`
20+
* - `startTimestamp` = `0`
21+
* - `burned` = `false`
22+
*
23+
* If the `tokenId` is burned:
24+
* - `addr` = `<Address of owner before token was burned>`
25+
* - `startTimestamp` = `<Timestamp when token was burned>`
26+
* - `burned = `true`
27+
*
28+
* Otherwise:
29+
* - `addr` = `<Address of owner>`
30+
* - `startTimestamp` = `<Timestamp of start of ownership>`
31+
* - `burned = `false`
32+
*/
33+
function explicitOwnershipOf(uint256 tokenId) public view override returns (TokenOwnership memory) {
34+
TokenOwnership memory ownership;
35+
if (tokenId < _startTokenId() || tokenId >= _currentIndex) {
36+
return ownership;
37+
}
38+
ownership = _ownerships[tokenId];
39+
if (ownership.burned) {
40+
return ownership;
41+
}
42+
return _ownershipOf(tokenId);
43+
}
44+
45+
/**
46+
* @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
47+
* See {ERC721AQueryable-explicitOwnershipOf}
48+
*/
49+
function explicitOwnershipsOf(uint256[] memory tokenIds) external view override returns (TokenOwnership[] memory) {
50+
unchecked {
51+
uint256 tokenIdsLength = tokenIds.length;
52+
TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
53+
for (uint256 i; i != tokenIdsLength; ++i) {
54+
ownerships[i] = explicitOwnershipOf(tokenIds[i]);
55+
}
56+
return ownerships;
57+
}
58+
}
59+
60+
/**
61+
* @dev Returns an array of token IDs owned by `owner`,
62+
* in the range [`start`, `stop`)
63+
* (i.e. `start <= tokenId < stop`).
64+
*
65+
* This function allows for tokens to be queried if the collection
66+
* grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
67+
*
68+
* Requirements:
69+
*
70+
* - `start` < `stop`
71+
*/
72+
/* solhint-disable*/
73+
function tokensOfOwnerIn(
74+
address owner,
75+
uint256 start,
76+
uint256 stop
77+
) external view override returns (uint256[] memory) {
78+
unchecked {
79+
if (start >= stop) revert InvalidQueryRange();
80+
uint256 tokenIdsIdx;
81+
uint256 stopLimit = _currentIndex;
82+
// Set `start = max(start, _startTokenId())`.
83+
if (start < _startTokenId()) {
84+
start = _startTokenId();
85+
}
86+
// Set `stop = min(stop, _currentIndex)`.
87+
if (stop > stopLimit) {
88+
stop = stopLimit;
89+
}
90+
uint256 tokenIdsMaxLength = balanceOf(owner);
91+
// Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
92+
// to cater for cases where `balanceOf(owner)` is too big.
93+
if (start < stop) {
94+
uint256 rangeLength = stop - start;
95+
if (rangeLength < tokenIdsMaxLength) {
96+
tokenIdsMaxLength = rangeLength;
97+
}
98+
} else {
99+
tokenIdsMaxLength = 0;
100+
}
101+
uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
102+
if (tokenIdsMaxLength == 0) {
103+
return tokenIds;
104+
}
105+
// We need to call `explicitOwnershipOf(start)`,
106+
// because the slot at `start` may not be initialized.
107+
TokenOwnership memory ownership = explicitOwnershipOf(start);
108+
address currOwnershipAddr;
109+
// If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.
110+
// `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.
111+
if (!ownership.burned) {
112+
currOwnershipAddr = ownership.addr;
113+
}
114+
for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
115+
ownership = _ownerships[i];
116+
if (ownership.burned) {
117+
continue;
118+
}
119+
if (ownership.addr != address(0)) {
120+
currOwnershipAddr = ownership.addr;
121+
}
122+
if (currOwnershipAddr == owner) {
123+
tokenIds[tokenIdsIdx++] = i;
124+
}
125+
}
126+
// Downsize the array to fit.
127+
assembly {
128+
mstore(tokenIds, tokenIdsIdx)
129+
}
130+
return tokenIds;
131+
}
132+
}
133+
134+
/* solhint-enable */
135+
136+
/**
137+
* @dev Returns an array of token IDs owned by `owner`.
138+
*
139+
* This function scans the ownership mapping and is O(totalSupply) in complexity.
140+
* It is meant to be called off-chain.
141+
*
142+
* See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
143+
* multiple smaller scans if the collection is large enough to cause
144+
* an out-of-gas error (10K pfp collections should be fine).
145+
*/
146+
function tokensOfOwner(address owner) external view override returns (uint256[] memory) {
147+
unchecked {
148+
uint256 tokenIdsIdx;
149+
address currOwnershipAddr;
150+
uint256 tokenIdsLength = balanceOf(owner);
151+
uint256[] memory tokenIds = new uint256[](tokenIdsLength);
152+
TokenOwnership memory ownership;
153+
for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
154+
ownership = _ownerships[i];
155+
if (ownership.burned) {
156+
continue;
157+
}
158+
if (ownership.addr != address(0)) {
159+
currOwnershipAddr = ownership.addr;
160+
}
161+
if (currOwnershipAddr == owner) {
162+
tokenIds[tokenIdsIdx++] = i;
163+
}
164+
}
165+
return tokenIds;
166+
}
167+
}
168+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: MIT
2+
// ERC721A Contracts v3.3.0
3+
// Creator: Chiru Labs
4+
5+
pragma solidity ^0.8.4;
6+
7+
import "../interface/IERC721A.sol";
8+
9+
/**
10+
* @dev Interface of an ERC721AQueryable compliant contract.
11+
*/
12+
interface IERC721AQueryable is IERC721A {
13+
/**
14+
* Invalid query range (`start` >= `stop`).
15+
*/
16+
error InvalidQueryRange();
17+
18+
/**
19+
* @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
20+
*
21+
* If the `tokenId` is out of bounds:
22+
* - `addr` = `address(0)`
23+
* - `startTimestamp` = `0`
24+
* - `burned` = `false`
25+
*
26+
* If the `tokenId` is burned:
27+
* - `addr` = `<Address of owner before token was burned>`
28+
* - `startTimestamp` = `<Timestamp when token was burned>`
29+
* - `burned = `true`
30+
*
31+
* Otherwise:
32+
* - `addr` = `<Address of owner>`
33+
* - `startTimestamp` = `<Timestamp of start of ownership>`
34+
* - `burned = `false`
35+
*/
36+
function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
37+
38+
/**
39+
* @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
40+
* See {ERC721AQueryable-explicitOwnershipOf}
41+
*/
42+
function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
43+
44+
/**
45+
* @dev Returns an array of token IDs owned by `owner`,
46+
* in the range [`start`, `stop`)
47+
* (i.e. `start <= tokenId < stop`).
48+
*
49+
* This function allows for tokens to be queried if the collection
50+
* grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
51+
*
52+
* Requirements:
53+
*
54+
* - `start` < `stop`
55+
*/
56+
function tokensOfOwnerIn(
57+
address owner,
58+
uint256 start,
59+
uint256 stop
60+
) external view returns (uint256[] memory);
61+
62+
/**
63+
* @dev Returns an array of token IDs owned by `owner`.
64+
*
65+
* This function scans the ownership mapping and is O(totalSupply) in complexity.
66+
* It is meant to be called off-chain.
67+
*
68+
* See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
69+
* multiple smaller scans if the collection is large enough to cause
70+
* an out-of-gas error (10K pfp collections should be fine).
71+
*/
72+
function tokensOfOwner(address owner) external view returns (uint256[] memory);
73+
}

0 commit comments

Comments
 (0)