Skip to content

Commit cf8bfbd

Browse files
authored
⚡️ Optimize bytecode size of mirror (#150)
1 parent 2ac0b12 commit cf8bfbd

File tree

3 files changed

+122
-110
lines changed

3 files changed

+122
-110
lines changed

.gas-snapshot

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -67,73 +67,73 @@ BenchTest:testMintDN420_13() (gas: 111184)
6767
BenchTest:testMintDN420_14() (gas: 112569)
6868
BenchTest:testMintDN420_15() (gas: 114041)
6969
BenchTest:testMintDN420_16() (gas: 115447)
70-
BenchTest:test__codesize() (gas: 28252)
70+
BenchTest:test__codesize() (gas: 28114)
7171
DN404CustomUnitTest:testInitializeCorrectUnitSuccess() (gas: 130120)
7272
DN404CustomUnitTest:testInitializeWithUnitTooLargeReverts() (gas: 33791)
7373
DN404CustomUnitTest:testInitializeWithZeroUnitReverts() (gas: 13864)
7474
DN404CustomUnitTest:testMint() (gas: 163237)
75-
DN404CustomUnitTest:testMintWithoutNFTs(uint256,uint256,uint256) (runs: 276, μ: 160409, ~: 162733)
75+
DN404CustomUnitTest:testMintWithoutNFTs(uint256,uint256,uint256) (runs: 276, μ: 160424, ~: 162733)
7676
DN404CustomUnitTest:testNFTMint() (gas: 64873666)
77-
DN404CustomUnitTest:testNFTMintAndBurn(uint256,uint256,uint256) (runs: 276, μ: 207856, ~: 159094)
78-
DN404CustomUnitTest:testNFTMintViaTransfer(uint256,uint256,uint256) (runs: 276, μ: 235488, ~: 250925)
79-
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256) (runs: 276, μ: 604, ~: 664)
80-
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256,uint256) (runs: 276, μ: 775, ~: 746)
77+
DN404CustomUnitTest:testNFTMintAndBurn(uint256,uint256,uint256) (runs: 276, μ: 203500, ~: 159083)
78+
DN404CustomUnitTest:testNFTMintViaTransfer(uint256,uint256,uint256) (runs: 276, μ: 232805, ~: 250925)
79+
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256) (runs: 276, μ: 603, ~: 664)
80+
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256,uint256) (runs: 276, μ: 776, ~: 746)
8181
DN404CustomUnitTest:testUnitInvalidCheckTrick(uint256) (runs: 276, μ: 526, ~: 527)
82-
DN404CustomUnitTest:test__codesize() (gas: 28781)
82+
DN404CustomUnitTest:test__codesize() (gas: 28643)
8383
DN404MirrorTest:testAutomaticPullOwnerWithOwnable() (gas: 3664602)
8484
DN404MirrorTest:testAutomaticPullOwnerWithOwnable2() (gas: 3652679)
8585
DN404MirrorTest:testBaseERC20() (gas: 114787)
8686
DN404MirrorTest:testFnSelectorNotRecognized() (gas: 6247)
8787
DN404MirrorTest:testLinkMirrorContract() (gas: 39410)
8888
DN404MirrorTest:testLogDirectTransfers() (gas: 395961)
8989
DN404MirrorTest:testLogTransfer() (gas: 120943)
90-
DN404MirrorTest:testNameAndSymbol(string,string) (runs: 276, μ: 203765, ~: 207074)
90+
DN404MirrorTest:testNameAndSymbol(string,string) (runs: 276, μ: 203835, ~: 207144)
9191
DN404MirrorTest:testNotLinked() (gas: 12794)
9292
DN404MirrorTest:testPullOwner() (gas: 112833)
93-
DN404MirrorTest:testSafeTransferFrom(uint32) (runs: 276, μ: 478719, ~: 478658)
93+
DN404MirrorTest:testSafeTransferFrom(uint32,bytes) (runs: 276, μ: 533926, ~: 502002)
9494
DN404MirrorTest:testSetAndGetApprovalForAll() (gas: 331059)
9595
DN404MirrorTest:testSetAndGetApproved() (gas: 328139)
96-
DN404MirrorTest:testSupportsInterface() (gas: 7567)
97-
DN404MirrorTest:testTokenURI(string,uint256) (runs: 276, μ: 261507, ~: 266260)
98-
DN404MirrorTest:testTransferFrom(uint32) (runs: 276, μ: 379939, ~: 379878)
99-
DN404MirrorTest:testTransferFromMixed(uint256) (runs: 276, μ: 785229, ~: 716434)
100-
DN404MirrorTest:test__codesize() (gas: 60249)
96+
DN404MirrorTest:testSupportsInterface() (gas: 7544)
97+
DN404MirrorTest:testTokenURI(string,uint256) (runs: 276, μ: 261981, ~: 266295)
98+
DN404MirrorTest:testTransferFrom(uint32) (runs: 276, μ: 379975, ~: 379913)
99+
DN404MirrorTest:testTransferFromMixed(uint256) (runs: 276, μ: 765680, ~: 687875)
100+
DN404MirrorTest:test__codesize() (gas: 61980)
101101
DN404OnlyERC20Test:testApprove() (gas: 35902)
102102
DN404OnlyERC20Test:testApprove(address,uint256) (runs: 276, μ: 31380, ~: 31453)
103103
DN404OnlyERC20Test:testBurn() (gas: 48166)
104-
DN404OnlyERC20Test:testBurn(address,uint256,uint256) (runs: 276, μ: 49031, ~: 49345)
105-
DN404OnlyERC20Test:testBurnInsufficientBalanceReverts(address,uint256,uint256) (runs: 276, μ: 42229, ~: 42379)
104+
DN404OnlyERC20Test:testBurn(address,uint256,uint256) (runs: 276, μ: 49020, ~: 49345)
105+
DN404OnlyERC20Test:testBurnInsufficientBalanceReverts(address,uint256,uint256) (runs: 276, μ: 42238, ~: 42379)
106106
DN404OnlyERC20Test:testInfiniteApproveTransferFrom() (gas: 82978)
107107
DN404OnlyERC20Test:testMaxSupplyTrick(uint256) (runs: 276, μ: 541, ~: 541)
108108
DN404OnlyERC20Test:testMetadata() (gas: 8962)
109109
DN404OnlyERC20Test:testMint() (gas: 44065)
110110
DN404OnlyERC20Test:testMintOverMaxLimitReverts() (gas: 42035)
111-
DN404OnlyERC20Test:testMintz(address,uint256) (runs: 276, μ: 44725, ~: 44487)
111+
DN404OnlyERC20Test:testMintz(address,uint256) (runs: 276, μ: 44640, ~: 44487)
112112
DN404OnlyERC20Test:testTransfer() (gas: 58082)
113-
DN404OnlyERC20Test:testTransfer(address,uint256) (runs: 276, μ: 58729, ~: 58491)
113+
DN404OnlyERC20Test:testTransfer(address,uint256) (runs: 276, μ: 58644, ~: 58491)
114114
DN404OnlyERC20Test:testTransferFrom() (gas: 79976)
115-
DN404OnlyERC20Test:testTransferFrom(address,address,address,uint256,uint256) (runs: 276, μ: 87059, ~: 88405)
115+
DN404OnlyERC20Test:testTransferFrom(address,address,address,uint256,uint256) (runs: 276, μ: 87062, ~: 88405)
116116
DN404OnlyERC20Test:testTransferFromInsufficientAllowanceReverts() (gas: 66880)
117-
DN404OnlyERC20Test:testTransferFromInsufficientAllowanceReverts(address,uint256,uint256) (runs: 276, μ: 67849, ~: 67959)
117+
DN404OnlyERC20Test:testTransferFromInsufficientAllowanceReverts(address,uint256,uint256) (runs: 276, μ: 67858, ~: 67959)
118118
DN404OnlyERC20Test:testTransferFromInsufficientBalanceReverts() (gas: 51747)
119-
DN404OnlyERC20Test:testTransferFromInsufficientBalanceReverts(address,uint256,uint256) (runs: 276, μ: 52378, ~: 52524)
119+
DN404OnlyERC20Test:testTransferFromInsufficientBalanceReverts(address,uint256,uint256) (runs: 276, μ: 52385, ~: 52534)
120120
DN404OnlyERC20Test:testTransferInsufficientBalanceReverts() (gas: 43948)
121-
DN404OnlyERC20Test:testTransferInsufficientBalanceReverts(address,uint256,uint256) (runs: 276, μ: 44927, ~: 45057)
122-
DN404OnlyERC20Test:test__codesize() (gas: 28201)
121+
DN404OnlyERC20Test:testTransferInsufficientBalanceReverts(address,uint256,uint256) (runs: 276, μ: 44936, ~: 45057)
122+
DN404OnlyERC20Test:test__codesize() (gas: 28063)
123123
DN404Test:testBatchNFTLog() (gas: 327308)
124124
DN404Test:testBurnOnTransfer(uint32,address) (runs: 274, μ: 275863, ~: 275863)
125125
DN404Test:testFnSelectorNotRecognized() (gas: 6256)
126-
DN404Test:testInitialize(uint32,address) (runs: 276, μ: 112730, ~: 113808)
126+
DN404Test:testInitialize(uint32,address) (runs: 276, μ: 112465, ~: 113808)
127127
DN404Test:testMintAndBurn() (gas: 346843)
128128
DN404Test:testMintAndBurn2() (gas: 283400)
129129
DN404Test:testMintNext() (gas: 707821)
130-
DN404Test:testMintNextContiguous(uint256) (runs: 276, μ: 588301, ~: 538017)
130+
DN404Test:testMintNextContiguous(uint256) (runs: 276, μ: 580234, ~: 530149)
131131
DN404Test:testMintOnTransfer(uint32,address) (runs: 274, μ: 290438, ~: 290438)
132-
DN404Test:testMixed(bytes32) (runs: 276, μ: 530506, ~: 477345)
132+
DN404Test:testMixed(bytes32) (runs: 276, μ: 535439, ~: 477565)
133133
DN404Test:testNameAndSymbol(string,string) (runs: 276, μ: 203567, ~: 206876)
134134
DN404Test:testNumAliasesOverflowReverts() (gas: 40944)
135135
DN404Test:testOwnedIds() (gas: 361141)
136-
DN404Test:testOwnedIds(uint256) (runs: 276, μ: 268325, ~: 284584)
136+
DN404Test:testOwnedIds(uint256) (runs: 276, μ: 270712, ~: 285417)
137137
DN404Test:testPermit2() (gas: 456313)
138138
DN404Test:testRegisterAndResolveAlias(address,address) (runs: 276, μ: 120191, ~: 120191)
139139
DN404Test:testSetAndGetAux(address,uint88) (runs: 276, μ: 22056, ~: 22274)
@@ -142,33 +142,33 @@ DN404Test:testSetAndGetSkipNFT() (gas: 708368)
142142
DN404Test:testTokenURI(string,uint256) (runs: 276, μ: 12997903, ~: 12975480)
143143
DN404Test:testTransferWithMirrorEvent() (gas: 411009)
144144
DN404Test:testTransfersAndBurns() (gas: 488426)
145-
DN404Test:testWrapAround(uint32,uint256) (runs: 276, μ: 392580, ~: 389199)
146-
DN404Test:test__codesize() (gas: 61066)
145+
DN404Test:testWrapAround(uint32,uint256) (runs: 276, μ: 392860, ~: 389199)
146+
DN404Test:test__codesize() (gas: 60928)
147147
DN404ZeroIndexedTest:testBatchNFTLog() (gas: 326747)
148-
DN404ZeroIndexedTest:testBurnOnTransfer(uint32,address) (runs: 274, μ: 255773, ~: 255783)
148+
DN404ZeroIndexedTest:testBurnOnTransfer(uint32,address) (runs: 274, μ: 255783, ~: 255783)
149149
DN404ZeroIndexedTest:testFnSelectorNotRecognized() (gas: 6256)
150-
DN404ZeroIndexedTest:testInitialize(uint32,address) (runs: 276, μ: 111139, ~: 113802)
150+
DN404ZeroIndexedTest:testInitialize(uint32,address) (runs: 276, μ: 111178, ~: 113802)
151151
DN404ZeroIndexedTest:testMintAndBurn() (gas: 343724)
152152
DN404ZeroIndexedTest:testMintAndBurn2() (gas: 280984)
153153
DN404ZeroIndexedTest:testMintNext() (gas: 704822)
154-
DN404ZeroIndexedTest:testMintNextContiguous(uint256) (runs: 276, μ: 574967, ~: 529585)
155-
DN404ZeroIndexedTest:testMintOnTransfer(uint32,address) (runs: 274, μ: 270394, ~: 270404)
156-
DN404ZeroIndexedTest:testMixed(bytes32) (runs: 276, μ: 517435, ~: 451049)
154+
DN404ZeroIndexedTest:testMintNextContiguous(uint256) (runs: 276, μ: 573892, ~: 527005)
155+
DN404ZeroIndexedTest:testMintOnTransfer(uint32,address) (runs: 274, μ: 270404, ~: 270404)
156+
DN404ZeroIndexedTest:testMixed(bytes32) (runs: 276, μ: 541159, ~: 483085)
157157
DN404ZeroIndexedTest:testNameAndSymbol(string,string) (runs: 276, μ: 203516, ~: 206825)
158158
DN404ZeroIndexedTest:testNumAliasesOverflowReverts() (gas: 40944)
159159
DN404ZeroIndexedTest:testOwnedIds() (gas: 348030)
160-
DN404ZeroIndexedTest:testOwnedIds(uint256) (runs: 276, μ: 268702, ~: 285180)
160+
DN404ZeroIndexedTest:testOwnedIds(uint256) (runs: 276, μ: 270873, ~: 287994)
161161
DN404ZeroIndexedTest:testPermit2() (gas: 455948)
162162
DN404ZeroIndexedTest:testRegisterAndResolveAlias(address,address) (runs: 276, μ: 120118, ~: 120191)
163-
DN404ZeroIndexedTest:testSetAndGetAux(address,uint88) (runs: 276, μ: 22074, ~: 22290)
163+
DN404ZeroIndexedTest:testSetAndGetAux(address,uint88) (runs: 276, μ: 22074, ~: 22292)
164164
DN404ZeroIndexedTest:testSetAndGetOperatorApprovals(address,address,bool) (runs: 276, μ: 130339, ~: 120534)
165165
DN404ZeroIndexedTest:testSetAndGetSkipNFT() (gas: 708390)
166166
DN404ZeroIndexedTest:testTokenURI(string,uint256) (runs: 276, μ: 157093, ~: 134670)
167167
DN404ZeroIndexedTest:testTransferWithMirrorEvent() (gas: 410515)
168168
DN404ZeroIndexedTest:testTransfersAndBurns() (gas: 486461)
169-
DN404ZeroIndexedTest:testWrapAround(uint32,uint256) (runs: 276, μ: 378032, ~: 383784)
170-
DN404ZeroIndexedTest:testWrapNFTIdEquivalence(uint256,uint256,bool) (runs: 276, μ: 1379, ~: 1418)
171-
DN404ZeroIndexedTest:test__codesize() (gas: 60861)
169+
DN404ZeroIndexedTest:testWrapAround(uint32,uint256) (runs: 276, μ: 376766, ~: 383784)
170+
DN404ZeroIndexedTest:testWrapNFTIdEquivalence(uint256,uint256,bool) (runs: 276, μ: 1382, ~: 1440)
171+
DN404ZeroIndexedTest:test__codesize() (gas: 60723)
172172
DN420OnlyERC20Test:testApprove() (gas: 35869)
173173
DN420OnlyERC20Test:testApprove(address,uint256) (runs: 276, μ: 31347, ~: 31420)
174174
DN420OnlyERC20Test:testBurn() (gas: 49038)
@@ -223,16 +223,16 @@ MaxUnitInvariant:invariantMirrorAndBaseRemainImmutable() (runs: 10, calls: 150,
223223
MaxUnitInvariant:invariantNoUserOwnsInvalidToken() (runs: 10, calls: 150, reverts: 0)
224224
MaxUnitInvariant:invariantTotalReflectionIsValid() (runs: 10, calls: 150, reverts: 0)
225225
MaxUnitInvariant:invariantUserReflectionIsValid() (runs: 10, calls: 150, reverts: 0)
226-
MintTests:test_WhenAmountIsGreaterThan_MAX_SUPPLYOrMintMakesNFTTotalSupplyExceed_MAX_SUPPLY(uint256) (runs: 276, μ: 60470, ~: 61591)
227-
MintTests:test_WhenRecipientAddressHasSkipNFTEnabled(uint256) (runs: 276, μ: 85931, ~: 85934)
228-
MintTests:test_WhenRecipientIsAddress0(uint256) (runs: 276, μ: 31043, ~: 30966)
229-
MintTests:test_WhenRecipientsBalanceDifferenceIsNotUpTo1e18(uint256) (runs: 276, μ: 82999, ~: 83101)
230-
MintTests:test_WhenRecipientsBalanceDifferenceIsUpTo1e18OrAbove(uint256) (runs: 276, μ: 89519, ~: 89631)
231-
MintTests:test__codesize() (gas: 27378)
226+
MintTests:test_WhenAmountIsGreaterThan_MAX_SUPPLYOrMintMakesNFTTotalSupplyExceed_MAX_SUPPLY(uint256) (runs: 276, μ: 60713, ~: 61594)
227+
MintTests:test_WhenRecipientAddressHasSkipNFTEnabled(uint256) (runs: 276, μ: 85951, ~: 85935)
228+
MintTests:test_WhenRecipientIsAddress0(uint256) (runs: 276, μ: 31047, ~: 30966)
229+
MintTests:test_WhenRecipientsBalanceDifferenceIsNotUpTo1e18(uint256) (runs: 276, μ: 82981, ~: 83100)
230+
MintTests:test_WhenRecipientsBalanceDifferenceIsUpTo1e18OrAbove(uint256) (runs: 276, μ: 89511, ~: 89631)
231+
MintTests:test__codesize() (gas: 27240)
232232
NFTMintDN404Test:testAllowlistMint() (gas: 258872)
233233
NFTMintDN404Test:testMint() (gas: 231574)
234234
NFTMintDN404Test:testTotalSupplyReached() (gas: 628086918)
235-
NFTMintDN404Test:test__codesize() (gas: 22437)
235+
NFTMintDN404Test:test__codesize() (gas: 22299)
236236
NonMultipleUnitInvariant:invariantBurnedPoolLengthIsTailMinusHead() (runs: 10, calls: 150, reverts: 0)
237237
NonMultipleUnitInvariant:invariantDN404BalanceSum() (runs: 10, calls: 150, reverts: 0)
238238
NonMultipleUnitInvariant:invariantMirror721BalanceSum() (runs: 10, calls: 150, reverts: 0)
@@ -244,7 +244,7 @@ SimpleDN404Test:testMint() (gas: 49579)
244244
SimpleDN404Test:testName() (gas: 9134)
245245
SimpleDN404Test:testSymbol() (gas: 9132)
246246
SimpleDN404Test:testWithdraw() (gas: 20732)
247-
SimpleDN404Test:test__codesize() (gas: 18359)
247+
SimpleDN404Test:test__codesize() (gas: 18221)
248248
SoladyTest:test__codesize() (gas: 840)
249249
TestPlus:test__codesize() (gas: 406)
250250
WADUnitInvariant:invariantBurnedPoolLengthIsTailMinusHead() (runs: 10, calls: 150, reverts: 0)

src/DN404Mirror.sol

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,12 @@ contract DN404Mirror {
280280

281281
/// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
282282
function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
283-
transferFrom(from, to, id);
284-
if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
283+
bytes calldata emptyData;
284+
/// @solidity memory-safe-assembly
285+
assembly {
286+
emptyData.length := 0
287+
}
288+
safeTransferFrom(from, to, id, emptyData);
285289
}
286290

287291
/// @dev Transfers token `id` from `from` to `to`.
@@ -302,7 +306,34 @@ contract DN404Mirror {
302306
virtual
303307
{
304308
transferFrom(from, to, id);
305-
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
309+
/// @solidity memory-safe-assembly
310+
assembly {
311+
if extcodesize(to) {
312+
// Prepare the calldata.
313+
let m := mload(0x40)
314+
let onERC721ReceivedSelector := 0x150b7a02
315+
mstore(m, onERC721ReceivedSelector)
316+
mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
317+
mstore(add(m, 0x40), shr(96, shl(96, from)))
318+
mstore(add(m, 0x60), id)
319+
mstore(add(m, 0x80), 0x80)
320+
mstore(add(m, 0xa0), data.length)
321+
calldatacopy(add(m, 0xc0), data.offset, data.length)
322+
// Revert if the call reverts.
323+
if iszero(call(gas(), to, 0, add(m, 0x1c), add(data.length, 0xa4), m, 0x20)) {
324+
if returndatasize() {
325+
// Bubble up the revert if the call reverts.
326+
returndatacopy(m, 0x00, returndatasize())
327+
revert(m, returndatasize())
328+
}
329+
}
330+
// Load the returndata and compare it.
331+
if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
332+
mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
333+
revert(0x1c, 0x04)
334+
}
335+
}
336+
}
306337
}
307338

308339
/// @dev Returns true if this contract implements the interface defined by `interfaceId`.
@@ -498,14 +529,6 @@ contract DN404Mirror {
498529
}
499530
}
500531

501-
/// @dev Returns if `a` has bytecode of non-zero length.
502-
function _hasCode(address a) private view returns (bool result) {
503-
/// @solidity memory-safe-assembly
504-
assembly {
505-
result := extcodesize(a) // Can handle dirty upper bits.
506-
}
507-
}
508-
509532
/// @dev More bytecode-efficient way to revert.
510533
function _rv(uint32 s) private pure {
511534
/// @solidity memory-safe-assembly
@@ -514,46 +537,4 @@ contract DN404Mirror {
514537
revert(0x1c, 0x04)
515538
}
516539
}
517-
518-
/// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
519-
/// Reverts if the target does not support the function correctly.
520-
function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
521-
private
522-
{
523-
/// @solidity memory-safe-assembly
524-
assembly {
525-
// Prepare the calldata.
526-
let m := mload(0x40)
527-
let onERC721ReceivedSelector := 0x150b7a02
528-
mstore(m, onERC721ReceivedSelector)
529-
mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
530-
mstore(add(m, 0x40), shr(96, shl(96, from)))
531-
mstore(add(m, 0x60), id)
532-
mstore(add(m, 0x80), 0x80)
533-
let n := mload(data)
534-
mstore(add(m, 0xa0), n)
535-
if n {
536-
let dst := add(m, 0xc0)
537-
let end := add(dst, n)
538-
for { let d := sub(add(data, 0x20), dst) } 1 {} {
539-
mstore(dst, mload(add(dst, d)))
540-
dst := add(dst, 0x20)
541-
if iszero(lt(dst, end)) { break }
542-
}
543-
}
544-
// Revert if the call reverts.
545-
if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
546-
if returndatasize() {
547-
// Bubble up the revert if the call reverts.
548-
returndatacopy(m, 0x00, returndatasize())
549-
revert(m, returndatasize())
550-
}
551-
}
552-
// Load the returndata and compare it.
553-
if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
554-
mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
555-
revert(0x1c, 0x04)
556-
}
557-
}
558-
}
559540
}

0 commit comments

Comments
 (0)