Skip to content

Commit 6118a5e

Browse files
committed
Optimize bytecode size of mirror
1 parent 2ac0b12 commit 6118a5e

File tree

2 files changed

+75
-63
lines changed

2 files changed

+75
-63
lines changed

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
}

test/DN404Mirror.t.sol

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,19 @@ import {MockDN404Ownable} from "./utils/mocks/MockDN404Ownable.sol";
77
import {DN404Mirror, MockDN404Mirror} from "./utils/mocks/MockDN404Mirror.sol";
88
import {LibSort} from "solady/utils/LibSort.sol";
99

10-
contract Invalid721Receiver {}
10+
contract InvalidERC721Receiver {}
11+
12+
contract ERC721Receiver {
13+
bytes32 public lastReceivedHash;
14+
15+
function onERC721Received(address by, address from, uint256 id, bytes calldata data)
16+
public
17+
returns (bytes4)
18+
{
19+
lastReceivedHash = keccak256(abi.encode(by, from, id, keccak256(data)));
20+
return msg.sig;
21+
}
22+
}
1123

1224
contract DN404MirrorTest is SoladyTest {
1325
event Transfer(address indexed from, address indexed to, uint256 indexed id);
@@ -206,25 +218,44 @@ contract DN404MirrorTest is SoladyTest {
206218
}
207219
}
208220

209-
function testSafeTransferFrom(uint32 totalNFTSupply) public {
221+
function testSafeTransferFrom(uint32 totalNFTSupply, bytes memory randomBytes) public {
210222
totalNFTSupply = uint32(_bound(totalNFTSupply, 5, 1000000));
211223
address alice = address(111);
212224
address bob = address(222);
213-
address invalid = address(new Invalid721Receiver());
214225

215226
dn.initializeDN404(uint96(uint256(totalNFTSupply) * _WAD), address(this), address(mirror));
216227
dn.transfer(alice, _WAD * uint256(5));
217228
assertEq(mirror.balanceOf(alice), 5);
218229
assertEq(mirror.balanceOf(bob), 0);
219230

220-
vm.prank(alice);
221-
vm.expectRevert(DN404Mirror.TransferToNonERC721ReceiverImplementer.selector);
222-
mirror.safeTransferFrom(alice, invalid, 1);
231+
if (_random() % 2 == 0) {
232+
address to = address(new InvalidERC721Receiver());
233+
234+
vm.prank(alice);
235+
vm.expectRevert(DN404Mirror.TransferToNonERC721ReceiverImplementer.selector);
236+
mirror.safeTransferFrom(alice, to, 1);
237+
238+
vm.prank(alice);
239+
mirror.safeTransferFrom(alice, bob, 1);
240+
assertEq(mirror.balanceOf(alice), 4);
241+
assertEq(mirror.balanceOf(bob), 1);
242+
} else {
243+
address to = address(new ERC721Receiver());
244+
address operator = _randomNonZeroAddress();
245+
vm.prank(alice);
246+
mirror.setApprovalForAll(operator, true);
247+
248+
if (randomBytes.length == 0 && _random() % 2 == 0) {
249+
vm.prank(operator);
250+
mirror.safeTransferFrom(alice, to, 1);
251+
} else {
252+
vm.prank(operator);
253+
mirror.safeTransferFrom(alice, to, 1, randomBytes);
254+
}
223255

224-
vm.prank(alice);
225-
mirror.safeTransferFrom(alice, bob, 1);
226-
assertEq(mirror.balanceOf(alice), 4);
227-
assertEq(mirror.balanceOf(bob), 1);
256+
bytes32 h = keccak256(abi.encode(operator, alice, 1, keccak256(randomBytes)));
257+
assertEq(ERC721Receiver(to).lastReceivedHash(), h);
258+
}
228259
}
229260

230261
function testLinkMirrorContract() public {

0 commit comments

Comments
 (0)