Skip to content

Commit 7db0967

Browse files
committed
Fix salt sniping. Lean ThirdwebContract. Test.
1 parent 76f350e commit 7db0967

File tree

3 files changed

+82
-20
lines changed

3 files changed

+82
-20
lines changed

contracts/ByocFactory.sol

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pragma solidity ^0.8.11;
55
import "@openzeppelin/contracts/utils/Create2.sol";
66
import "@openzeppelin/contracts/proxy/Clones.sol";
77
import "@openzeppelin/contracts/utils/Address.sol";
8+
import "@openzeppelin/contracts/utils/Multicall.sol";
89
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
910
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
1011

@@ -13,7 +14,7 @@ import { IByocFactory } from "./interfaces/IByocFactory.sol";
1314
import { TWRegistry } from "./TWRegistry.sol";
1415
import "./ThirdwebContract.sol";
1516

16-
contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable {
17+
contract ByocFactory is IByocFactory, ERC2771Context, Multicall, AccessControlEnumerable {
1718
/*///////////////////////////////////////////////////////////////
1819
State variables
1920
//////////////////////////////////////////////////////////////*/
@@ -24,8 +25,8 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable {
2425
/// @dev Whether the registry is paused.
2526
bool public isPaused;
2627

27-
/// @dev Empty var used in deployment.
28-
address public deployer;
28+
/// @dev contract address deployed through the factory => deployer
29+
mapping(address => address) public getContractDeployer;
2930

3031
/*///////////////////////////////////////////////////////////////
3132
Constructor + modifiers
@@ -56,12 +57,15 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable {
5657
uint256 _value,
5758
string memory publishMetadataUri
5859
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
59-
deployer = _msgSender();
60-
6160
require(bytes(publishMetadataUri).length > 0, "No publish metadata");
6261

6362
bytes memory contractBytecode = abi.encodePacked(_contractBytecode, _constructorArgs);
64-
bytes32 salt = _salt == "" ? keccak256(abi.encodePacked(_msgSender(), block.number)) : _salt;
63+
bytes32 salt = _salt == ""
64+
? keccak256(abi.encodePacked(_msgSender(), block.number, keccak256(contractBytecode)))
65+
: keccak256(abi.encodePacked(_msgSender(), _salt));
66+
67+
address computedContractAddress = Create2.computeAddress(salt, keccak256(contractBytecode), address(this));
68+
getContractDeployer[computedContractAddress] = _msgSender();
6569

6670
deployedAddress = Create2.deploy(_value, salt, contractBytecode);
6771

@@ -74,8 +78,6 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable {
7478

7579
registry.add(_publisher, deployedAddress);
7680

77-
delete deployer;
78-
7981
emit ContractDeployed(_msgSender(), _publisher, deployedAddress);
8082
}
8183

@@ -88,7 +90,13 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable {
8890
uint256 _value,
8991
string memory publishMetadataUri
9092
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
91-
bytes32 salt = _salt == "" ? keccak256(abi.encodePacked(_msgSender(), block.number)) : _salt;
93+
bytes32 salt = _salt == ""
94+
? keccak256(abi.encodePacked(_msgSender(), block.number, _implementation, _initializeData))
95+
: keccak256(abi.encodePacked(_msgSender(), _salt));
96+
97+
address computedContractAddress = Clones.predictDeterministicAddress(_implementation, salt, address(this));
98+
getContractDeployer[computedContractAddress] = _msgSender();
99+
92100
deployedAddress = Clones.cloneDeterministic(_implementation, salt);
93101

94102
ThirdwebContract(deployedAddress).setPublishMetadataUri(publishMetadataUri);

contracts/ThirdwebContract.sol

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity ^0.8.0;
33

4-
interface IDeployer {
5-
function deployer() external view returns (address);
4+
interface IContractDeployer {
5+
function getContractDeployer(address _contract) external view returns (address);
66
}
77

8+
error ThirdwebContract_MetadataAlreadyInitialized();
9+
810
contract ThirdwebContract {
911
/// @dev The publish metadata of the contract of which this contract is an instance.
1012
string private publishMetadataUri;
11-
/// @dev Address of the contract deployer.
12-
address private deployer;
13-
14-
constructor() {
15-
deployer = IDeployer(msg.sender).deployer();
16-
}
1713

1814
/// @dev Returns the publish metadata for this contract.
1915
function getPublishMetadataUri() external view returns (string memory) {
@@ -22,12 +18,22 @@ contract ThirdwebContract {
2218

2319
/// @dev Initializes the publish metadata and at deploy time.
2420
function setPublishMetadataUri(string memory uri) external {
25-
require(bytes(publishMetadataUri).length == 0, "Published metadata already initialized");
21+
if (bytes(publishMetadataUri).length != 0) {
22+
revert ThirdwebContract_MetadataAlreadyInitialized();
23+
}
2624
publishMetadataUri = uri;
2725
}
2826

29-
/// @dev Returns msg.sender, if caller is not thirdweb factory. Returns the intended msg.sender if caller is factory.
27+
/// @dev Enable access to the original contract deployer in the constructor. If this function is called outside of a constructor, it will return address(0) instead.
28+
/// Save 1 storage slot from not storing the factory address and not having to hardcode the factory address.
3029
function _contractDeployer() internal view returns (address) {
31-
return deployer;
30+
if (address(this).code.length == 0) {
31+
try IContractDeployer(msg.sender).getContractDeployer(address(this)) returns (address deployer) {
32+
return deployer;
33+
} catch {
34+
return address(0);
35+
}
36+
}
37+
return address(0);
3238
}
3339
}

src/test/ThirdwebContract.t.sol

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.11;
3+
4+
// Test imports
5+
import "./utils/BaseTest.sol";
6+
import "contracts/TWFactory.sol";
7+
import "contracts/TWRegistry.sol";
8+
9+
// Helpers
10+
import "@openzeppelin/contracts/utils/Create2.sol";
11+
import "@openzeppelin/contracts/proxy/Clones.sol";
12+
import "contracts/TWProxy.sol";
13+
import "contracts/ThirdwebContract.sol";
14+
15+
contract MyThirdwebContract is ThirdwebContract {
16+
address public contractDeployerFromConstructor;
17+
18+
constructor() {
19+
contractDeployerFromConstructor = _contractDeployer();
20+
}
21+
22+
function getContractDeployerOutsideFromConstructor() public view returns (address) {
23+
return _contractDeployer();
24+
}
25+
}
26+
27+
contract ThirdwebContractTest is BaseTest {
28+
function setUp() public override {
29+
super.setUp();
30+
}
31+
32+
function getContractDeployer(address) public pure returns (address) {
33+
return address(42);
34+
}
35+
36+
function deployContract() public returns (address) {
37+
return Create2.deploy(0, keccak256(abi.encode(0)), type(MyThirdwebContract).creationCode);
38+
}
39+
40+
function test_ThirdwebContract_ContractDeployerConstructor() external {
41+
address addy = deployContract();
42+
address contractDeployer = MyThirdwebContract(addy).contractDeployerFromConstructor();
43+
address contractDeployerNotConstructor = MyThirdwebContract(addy).getContractDeployerOutsideFromConstructor();
44+
45+
assertEq(contractDeployer, getContractDeployer(addy));
46+
assertEq(contractDeployerNotConstructor, address(0));
47+
}
48+
}

0 commit comments

Comments
 (0)