1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Currency} from "./lib/v4-core/src/types/Currency.sol";
import {CASHCATBRRR} from "./CASHCATBRRR.sol";
/// @dev Minimal CREATE3-style proxy. Its address is CREATE2-derived and its first CREATE address is independent of
/// the token init code, which lets the token bind its own address into its immutable PoolKey.
contract CASHCATBRRRCreateProxy {
bytes32 public constant launchFingerprint = 0x37db434b5794be97d7e0af298e0edd73113ff31782d35e23aef7cef5e55645e0;
function deploy(bytes calldata initCode) external returns (address deployed) {
bytes memory code = initCode;
assembly ("memory-safe") {
deployed := create(0, add(code, 0x20), mload(code))
}
if (deployed == address(0)) revert();
}
}
/// @notice Salted deployer for the permission-bit-constrained V4 native-tax hook token.
contract CASHCATBRRRTokenFactory {
error InvalidHookPermissions(address token);
error ProxyDeploymentFailed();
error TokenAddressMismatch(address expected, address actual);
event TokenDeployed(address indexed token, bytes32 indexed salt, address indexed proxy);
function deploy(CASHCATBRRR.LaunchConfig calldata config, bytes32 salt) external returns (address token) {
address proxy;
bytes memory proxyCode = type(CASHCATBRRRCreateProxy).creationCode;
assembly ("memory-safe") {
proxy := create2(0, add(proxyCode, 0x20), mload(proxyCode), salt)
}
if (proxy == address(0)) revert ProxyDeploymentFailed();
address expected = _computeCreateAddress(proxy, 1);
CASHCATBRRR.LaunchConfig memory launch = config;
launch.poolKey.currency0 = Currency.wrap(address(0));
launch.poolKey.currency1 = Currency.wrap(expected);
bytes memory initCode = abi.encodePacked(type(CASHCATBRRR).creationCode, abi.encode(launch));
token = CASHCATBRRRCreateProxy(proxy).deploy(initCode);
if (token != expected) revert TokenAddressMismatch(expected, token);
if (!CASHCATBRRR(payable(token)).hasRequiredHookPermissions()) revert InvalidHookPermissions(token);
// The token deliberately skips distributor synchronization during its
// constructor mint. Synchronize the initial holder only after the
// token exists, so a real distributor can register the deployer's
// eligible balance without making deployments with a deferred/empty
// distributor endpoint revert.
CASHCATBRRR(payable(token)).syncShare(config.deployer);
emit TokenDeployed(token, salt, proxy);
}
function predictToken(bytes32 salt) external view returns (address token, address proxy) {
bytes32 proxyCodeHash = keccak256(type(CASHCATBRRRCreateProxy).creationCode);
proxy = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, proxyCodeHash)))));
token = _computeCreateAddress(proxy, 1);
}
function _computeCreateAddress(address deployer, uint256 nonce) private pure returns (address) {
if (nonce != 1) revert ProxyDeploymentFailed();
bytes32 digest = keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x01)));
// forge-lint: disable-next-line(unsafe-typecast)
return address(uint160(uint256(digest)));
}
}