-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSentrixV2Factory.sol
More file actions
100 lines (86 loc) · 4.69 KB
/
SentrixV2Factory.sol
File metadata and controls
100 lines (86 loc) · 4.69 KB
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ISentrixV2Factory} from "./interfaces/ISentrixV2Factory.sol";
import {SentrixV2Pair} from "./SentrixV2Pair.sol";
// SentrixV2Factory — pair creator. CREATE2 with salt = keccak256(token0, token1)
// so the pair address is deterministically derivable off-chain (Library.pairFor
// uses this same salt to compute pair addresses without an on-chain lookup).
//
// The pair's INIT_CODE_HASH is whatever the compiled SentrixV2Pair bytecode
// hashes to. After this contract is compiled and deployed, `INIT_CODE_HASH`
// must be patched into SentrixV2Library before Router deploys, or pairFor()
// will return wrong addresses. Provided as `pairCodeHash()` view for that
// off-chain computation.
contract SentrixV2Factory is ISentrixV2Factory {
address public override feeTo;
address public override feeToSetter;
// Audit H1 (2026-05-07): two-step rotation for feeToSetter. Single-step
// setFeeToSetter() can brick fee admin permanently if the operator
// mistypes the new address (or types an address whose private key is
// unknown). Two-step requires the new admin to actually CALL the
// accept function from their own address, proving they control it.
//
// Note: the currently-deployed mainnet factory at chain 7119 uses the
// pre-fix single-step pattern. This change applies only to future
// redeployments. Existing fee admin discipline: verify the new address
// works (e.g. signed-tx round-trip from it) BEFORE calling
// setFeeToSetter() on the deployed factory.
address public pendingFeeToSetter;
event FeeToSetterRotationProposed(address indexed current, address indexed pending);
event FeeToSetterRotationAccepted(address indexed previous, address indexed current);
mapping(address => mapping(address => address)) public override getPair;
address[] public override allPairs;
constructor(address _feeToSetter) {
feeToSetter = _feeToSetter;
}
function allPairsLength() external view override returns (uint256) {
return allPairs.length;
}
// Helper for Library.pairFor — emits the bytecode hash that off-chain
// tooling needs to embed in the Library constant.
function pairCodeHash() external pure returns (bytes32) {
return keccak256(type(SentrixV2Pair).creationCode);
}
function createPair(address tokenA, address tokenB) external override returns (address pair) {
require(tokenA != tokenB, "SentrixV2: IDENTICAL_ADDRESSES");
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), "SentrixV2: ZERO_ADDRESS");
require(getPair[token0][token1] == address(0), "SentrixV2: PAIR_EXISTS");
bytes memory bytecode = type(SentrixV2Pair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
require(pair != address(0), "SentrixV2: PAIR_DEPLOY_FAILED");
SentrixV2Pair(pair).initialize(token0, token1);
getPair[token0][token1] = pair;
getPair[token1][token0] = pair; // populate reverse mapping for O(1) bidirectional lookup
allPairs.push(pair);
emit PairCreated(token0, token1, pair, allPairs.length);
}
function setFeeTo(address _feeTo) external override {
require(msg.sender == feeToSetter, "SentrixV2: FORBIDDEN");
feeTo = _feeTo;
}
/// Step 1 of two-step rotation: current feeToSetter PROPOSES a successor.
/// The successor doesn't take effect until they call acceptFeeToSetter()
/// from their own address. Proposal is freely revocable: re-call with a
/// different `_feeToSetter` (or `address(this)` to clear) before accept.
function setFeeToSetter(address _feeToSetter) external override {
require(msg.sender == feeToSetter, "SentrixV2: FORBIDDEN");
require(_feeToSetter != address(0), "SentrixV2: ZERO_ADDRESS");
pendingFeeToSetter = _feeToSetter;
emit FeeToSetterRotationProposed(feeToSetter, _feeToSetter);
}
/// Step 2 of two-step rotation: pending successor accepts the role.
/// The caller MUST equal `pendingFeeToSetter`, proving they control the
/// address. This blocks the "typo to non-controlled EOA bricks fee admin
/// forever" failure mode the single-step pattern allows.
function acceptFeeToSetter() external {
require(msg.sender == pendingFeeToSetter, "SentrixV2: NOT_PENDING");
address previous = feeToSetter;
feeToSetter = pendingFeeToSetter;
pendingFeeToSetter = address(0);
emit FeeToSetterRotationAccepted(previous, feeToSetter);
}
}