Skip to content

Commit c6f5780

Browse files
committed
Merge remote-tracking branch 'origin/master' into sparrowDom/deploy_004
2 parents fa9454c + 49a4012 commit c6f5780

File tree

5 files changed

+141
-95
lines changed

5 files changed

+141
-95
lines changed

contracts/contracts/strategies/CurvePoolBooster.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ contract CurvePoolBooster is Initializable, Strategizable {
280280
campaignId = 0;
281281
emit CampaignClosed(_campaignId);
282282
}
283+
283284
// slither-disable-end reentrancy-eth
284285

285286
/// @notice calculate the fee amount and transfer it to the feeCollector

contracts/contracts/strategies/CurvePoolBoosterFactory.sol

Lines changed: 105 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,24 @@ import { Initializable } from "../utils/Initializable.sol";
1111
/// @author Origin Protocol
1212
/// @notice Factory contract to create CurvePoolBoosterPlain instances
1313
contract CurvePoolBoosterFactory is Initializable, Strategizable {
14-
1514
/// @notice Address of the CreateX contract
16-
ICreateX public constant CREATEX = ICreateX(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed);
15+
ICreateX public constant CREATEX =
16+
ICreateX(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed);
1717
event CurvePoolBoosterPlainCreated(address indexed poolBoosterAddress);
1818

1919
/// @notice Initialize the contract. Normally we'd rather have the governor and strategist set in the constructor,
2020
/// but since this contract is deployed by CreateX we need to set them in the initialize function because
2121
/// the constructor's parameters influence the address of the contract when deployed using CreateX.
22-
/// And having different governor and strategist on the same address on different chains would
22+
/// And having different governor and strategist on the same address on different chains would
2323
/// cause issues.
2424
/// @param _governor Address of the governor
2525
/// @param _strategist Address of the strategist
26-
function initialize(address _governor, address _strategist) external initializer {
27-
_setGovernor(_governor);
28-
_setStrategistAddr(_strategist);
26+
function initialize(address _governor, address _strategist)
27+
external
28+
initializer
29+
{
30+
_setGovernor(_governor);
31+
_setStrategistAddr(_strategist);
2932
}
3033

3134
/// @notice Create a new CurvePoolBoosterPlain instance
@@ -42,60 +45,71 @@ contract CurvePoolBoosterFactory is Initializable, Strategizable {
4245
/// was deployed at the expected address, otherwise the transaction batch will revert. If set to 0 then the
4346
/// address verification is skipped.
4447
function createCurvePoolBoosterPlain(
45-
address _rewardToken,
46-
address _gauge,
47-
address _feeCollector,
48-
uint16 _fee,
49-
address _campaignRemoteManager,
50-
address _votemarket,
51-
bytes32 _salt,
52-
address _expectedAddress
48+
address _rewardToken,
49+
address _gauge,
50+
address _feeCollector,
51+
uint16 _fee,
52+
address _campaignRemoteManager,
53+
address _votemarket,
54+
bytes32 _salt,
55+
address _expectedAddress
5356
) external onlyGovernorOrStrategist returns (address) {
54-
require(governor() != address(0), "Governor not set");
55-
require(strategistAddr != address(0), "Strategist not set");
56-
// salt encoded sender
57-
address senderAddress = address(bytes20(_salt));
58-
// the contract that calls the CreateX should be encoded in the salt to protect against front-running
59-
require(senderAddress == address(this), "Front-run protection failed");
60-
61-
address poolBoosterAddress = CREATEX.deployCreate2(
62-
_salt,
63-
getInitCode(_rewardToken, _gauge)
64-
);
65-
66-
require(
67-
_expectedAddress == address(0) ||
68-
poolBoosterAddress == _expectedAddress,
69-
"Pool booster deployed at unexpected address"
70-
);
71-
72-
CurvePoolBoosterPlain(payable(poolBoosterAddress)).initialize(
73-
governor(),
74-
strategistAddr,
75-
_fee,
76-
_feeCollector,
77-
_campaignRemoteManager,
78-
_votemarket);
79-
80-
emit CurvePoolBoosterPlainCreated(poolBoosterAddress);
81-
return poolBoosterAddress;
57+
require(governor() != address(0), "Governor not set");
58+
require(strategistAddr != address(0), "Strategist not set");
59+
// salt encoded sender
60+
address senderAddress = address(bytes20(_salt));
61+
// the contract that calls the CreateX should be encoded in the salt to protect against front-running
62+
require(senderAddress == address(this), "Front-run protection failed");
63+
64+
address poolBoosterAddress = CREATEX.deployCreate2(
65+
_salt,
66+
getInitCode(_rewardToken, _gauge)
67+
);
68+
69+
require(
70+
_expectedAddress == address(0) ||
71+
poolBoosterAddress == _expectedAddress,
72+
"Pool booster deployed at unexpected address"
73+
);
74+
75+
CurvePoolBoosterPlain(payable(poolBoosterAddress)).initialize(
76+
governor(),
77+
strategistAddr,
78+
_fee,
79+
_feeCollector,
80+
_campaignRemoteManager,
81+
_votemarket
82+
);
83+
84+
emit CurvePoolBoosterPlainCreated(poolBoosterAddress);
85+
return poolBoosterAddress;
8286
}
8387

8488
// get initialisation code contract code + constructor arguments
85-
function getInitCode(
86-
address _rewardToken,
87-
address _gauge
88-
) internal pure returns (bytes memory) {
89-
return abi.encodePacked(
90-
type(CurvePoolBoosterPlain).creationCode,
91-
abi.encode(_rewardToken, _gauge)
92-
);
89+
function getInitCode(address _rewardToken, address _gauge)
90+
internal
91+
pure
92+
returns (bytes memory)
93+
{
94+
return
95+
abi.encodePacked(
96+
type(CurvePoolBoosterPlain).creationCode,
97+
abi.encode(_rewardToken, _gauge)
98+
);
9399
}
94100

95101
/// @notice Compute the guarded salt for CreateX protections. This version of guarded
96102
/// salt expects that this factory contract is the one doing calls to the CreateX contract.
97-
function _computeGuardedSalt(bytes32 _salt) internal view returns (bytes32) {
98-
return _efficientHash({a: bytes32(uint256(uint160(address(this)))), b: _salt});
103+
function _computeGuardedSalt(bytes32 _salt)
104+
internal
105+
view
106+
returns (bytes32)
107+
{
108+
return
109+
_efficientHash({
110+
a: bytes32(uint256(uint160(address(this)))),
111+
b: _salt
112+
});
99113
}
100114

101115
/**
@@ -104,9 +118,13 @@ contract CurvePoolBoosterFactory is Initializable, Strategizable {
104118
* @param b The second 32-byte value to be concatenated and hashed.
105119
* @return hash The 32-byte `keccak256` hash of `a` and `b`.
106120
*/
107-
function _efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 hash) {
108-
// solhint-disable-next-line no-inline-assembly
109-
assembly ("memory-safe") {
121+
function _efficientHash(bytes32 a, bytes32 b)
122+
internal
123+
pure
124+
returns (bytes32 hash)
125+
{
126+
// solhint-disable-next-line no-inline-assembly
127+
assembly {
110128
mstore(0x00, a)
111129
mstore(0x20, b)
112130
hash := keccak256(0x00, 0x40)
@@ -124,42 +142,47 @@ contract CurvePoolBoosterFactory is Initializable, Strategizable {
124142
address _gauge,
125143
bytes32 _salt
126144
) external view returns (address) {
127-
bytes32 guardedSalt = _computeGuardedSalt(_salt);
128-
return CREATEX.computeCreate2Address(
129-
guardedSalt,
130-
keccak256(getInitCode(_rewardToken, _gauge)),
131-
address(CREATEX)
132-
);
145+
bytes32 guardedSalt = _computeGuardedSalt(_salt);
146+
return
147+
CREATEX.computeCreate2Address(
148+
guardedSalt,
149+
keccak256(getInitCode(_rewardToken, _gauge)),
150+
address(CREATEX)
151+
);
133152
}
134153

135154
/**
136-
* @dev Encodes a salt for CreateX by concatenating deployer address (bytes20), cross-chain protection flag (bytes1),
137-
* and the first 11 bytes of the provided salt (most significant bytes). This function is exposed for easier
138-
* operations. For the salt value itself just use the epoch time when the operation is performed.
139-
* @param salt The raw salt as uint256; converted to bytes32, then only the first 11 bytes (MSB) are used.
140-
* @return encodedSalt The resulting 32-byte encoded salt.
141-
*/
142-
function encodeSaltForCreateX(
143-
uint256 salt
144-
) external view returns (bytes32 encodedSalt) {
145-
// prepare encoded salt guarded by this factory address. When the deployer part of the salt is the same as the
155+
* @dev Encodes a salt for CreateX by concatenating deployer address (bytes20), cross-chain protection flag
156+
* (bytes1), and the first 11 bytes of the provided salt (most significant bytes). This function is exposed
157+
* for easier operations. For the salt value itself just use the epoch time when the operation is performed.
158+
* @param salt The raw salt as uint256; converted to bytes32, then only the first 11 bytes (MSB) are used.
159+
* @return encodedSalt The resulting 32-byte encoded salt.
160+
*/
161+
function encodeSaltForCreateX(uint256 salt)
162+
external
163+
view
164+
returns (bytes32 encodedSalt)
165+
{
166+
// only the right most 11 bytes are considered when encoding salt. Which is limited by the number in the below
167+
// require. If salt were higher, the higher bytes would need to be set to 0 to not affect the "or" way of
168+
// encoding the salt.
169+
require(salt <= 309485009821345068724781055, "Invalid salt");
170+
171+
// prepare encoded salt guarded by this factory address. When the deployer part of the salt is the same as the
146172
// caller of CreateX the salt is re-hashed and thus guarded from front-running.
147173
address deployer = address(this);
148-
174+
149175
// Flag as uint8 (0)
150176
uint8 flag = 0;
151-
152-
// Salt prefix: high 11 bytes (88 bits) shifted to low position
153-
uint256 saltPrefix = uint256(bytes32(salt)) >> 168;
154-
177+
155178
// Precompute parts
156-
uint256 deployerPart = uint256(uint160(deployer)) << 96; // 20 bytes shifted left 96 bits (12 bytes)
157-
uint256 flagPart = uint256(flag) << 88; // 1 byte shifted left 88 bits (11 bytes)
158-
179+
uint256 deployerPart = uint256(uint160(deployer)) << 96; // 20 bytes shifted left 96 bits (12 bytes)
180+
uint256 flagPart = uint256(flag) << 88; // 1 byte shifted left 88 bits (11 bytes)
181+
159182
// Concat via nested OR
160183
// solhint-disable-next-line no-inline-assembly
161-
assembly ("memory-safe") {
162-
encodedSalt := or(or(deployerPart, flagPart), saltPrefix)
184+
assembly {
185+
encodedSalt := or(or(deployerPart, flagPart), salt)
163186
}
164187
}
165-
}
188+
}

contracts/test/_fixture.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ async function poolBoosterCodeUpdatedFixture() {
11931193
"CurvePoolBoosterFactory",
11941194
// hardcoded as generated with CreateX.
11951195
// TODO: replace these once deployed on mainnet
1196-
"0xa5721C0d670bB61d6FA315995B8F15b53CCB6662"
1196+
"0x28e25d059fF55e5ADdB95A4187A980649CfFa7Bc"
11971197
);
11981198

11991199
const implementationAddress = await curvePoolBoosterProxy.implementation();

contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -460,16 +460,40 @@ describe("ForkTest: CurvePoolBooster", function () {
460460
});
461461

462462
it("Should produce matching encoded salt", async () => {
463-
const saltBase = ethers.utils.keccak256(12345);
464463
const manualEncodedSalt = encodeSaltForCreateX(
465464
curvePoolBoosterFactory.address,
466465
false,
467-
saltBase
466+
12345
468467
);
469468

469+
const hardcodedSalt =
470+
"0x28e25d059ff55e5addb95a4187a980649cffa7bc000000000000000000003039";
471+
expect(hardcodedSalt).to.equal(
472+
await curvePoolBoosterFactory.encodeSaltForCreateX(12345)
473+
);
470474
expect(manualEncodedSalt).to.equal(
471-
await curvePoolBoosterFactory.encodeSaltForCreateX(saltBase)
475+
await curvePoolBoosterFactory.encodeSaltForCreateX(12345)
476+
);
477+
});
478+
479+
it("Should not produce matching encoded salt", async () => {
480+
const manualEncodedSalt = encodeSaltForCreateX(
481+
curvePoolBoosterFactory.address,
482+
false,
483+
12346
472484
);
485+
486+
expect(manualEncodedSalt).not.to.equal(
487+
await curvePoolBoosterFactory.encodeSaltForCreateX(12345)
488+
);
489+
});
490+
491+
it("Should throw an exception if salt value too big", async () => {
492+
await expect(
493+
curvePoolBoosterFactory.encodeSaltForCreateX(
494+
"309485009821345068724781056"
495+
)
496+
).to.be.revertedWith("Invalid salt");
473497
});
474498

475499
it("Should create a new pool booster instance", async () => {
@@ -503,11 +527,10 @@ describe("ForkTest: CurvePoolBooster", function () {
503527
});
504528

505529
it("Should not create a new pool booster that doesn't have salt guarded with deployer address", async () => {
506-
const saltBase = ethers.utils.keccak256(12345);
507530
const saltWithNoDeployerGuard = encodeSaltForCreateX(
508531
josh.address,
509532
false,
510-
saltBase
533+
12345
511534
);
512535

513536
await expect(
@@ -521,9 +544,8 @@ describe("ForkTest: CurvePoolBooster", function () {
521544
});
522545

523546
async function computePoolBoosterAddress(saltNumber, gaugeAddress) {
524-
const saltBase = ethers.utils.keccak256(saltNumber);
525547
const encodedSalt = await curvePoolBoosterFactory.encodeSaltForCreateX(
526-
saltBase
548+
saltNumber
527549
);
528550

529551
return await curvePoolBoosterFactory.computePoolBoosterAddress(
@@ -539,9 +561,8 @@ describe("ForkTest: CurvePoolBooster", function () {
539561
expectedAddress,
540562
encodedSaltOverride = null
541563
) {
542-
const saltBase = ethers.utils.keccak256(saltNumber);
543564
const encodedSalt = await curvePoolBoosterFactory.encodeSaltForCreateX(
544-
saltBase
565+
saltNumber
545566
);
546567

547568
return curvePoolBoosterFactory

contracts/utils/deploy.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,9 +1141,10 @@ function encodeSaltForCreateX(deployer, crossChainProtectionFlag, salt) {
11411141
? "0x01"
11421142
: "0x00";
11431143

1144-
// convert salt to bytes11
1145-
const saltBytes11 = "0x" + salt.slice(2, 24);
1146-
1144+
// this portion hexifies salt to bytes11
1145+
const saltBytes11 = ethers.utils.hexlify(
1146+
ethers.utils.zeroPad(ethers.utils.hexlify(salt), 11)
1147+
);
11471148
// concat all bytes into a bytes32
11481149
const encodedSalt = ethers.utils.hexlify(
11491150
ethers.utils.concat([

0 commit comments

Comments
 (0)