Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,15 @@ contract Deploy is Script {
// therefore be governance, never the hot CI deploy key — the same
// separation the beacon owner enforces. Fail the deploy loudly rather
// than silently leaving the feed under the deploy key's control.
//
// ST0X_SIGNER gets the SAME guard: `updatePrice` is permissionless and
// authorised solely by this signer's EIP-712 signature, so the signer
// controls every served price even MORE directly than the two admin
// roles — a copy-pasted signer equal to the hot deploy key would ship
// the feed under CI's control with no other guard catching it.
require(admin != deployer, "ST0X_ADMIN must not be the deploy key");
require(oracleAdmin != deployer, "ST0X_ORACLE_ADMIN must not be the deploy key");
require(signer != deployer, "ST0X_SIGNER must not be the deploy key");

ST0xPriceOracle oracleImpl = new ST0xPriceOracle();
oracleBSD = new ST0xPriceOracleBeaconSetDeployer(
Expand Down
10 changes: 9 additions & 1 deletion src/concrete/deploy/DIAVaultOracleBeaconSetDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ struct DIAVaultOracleBeaconSetDeployerConfig {
contract DIAVaultOracleBeaconSetDeployer {
/// @notice Emitted when a new DIAVaultOracle proxy is deployed.
/// @param caller The direct on-chain caller of `newDIAVaultOracle`.
/// Indexed so monitoring can filter by deployer.
/// Indexed for filtering — but note minting is PERMISSIONLESS and the salt
/// is derived from the config alone (msg.sender excluded), so a third party
/// who sees the intended public config can front-run the mint and appear
/// here as `caller`. Monitoring that must identify a specific operator
/// should key on the deterministic proxy address (a commitment to the
/// config), not on this field. A front-run mint is config-identical,
/// `initializer`-guarded and sits behind the governance-owned beacon, so it
/// grants the front-runner no authority — it only reverts the operator's own
/// later mint on the CREATE2 collision.
/// @param oracle The address of the new proxy. Indexed for filtering.
event Deployment(address indexed caller, address indexed oracle);

Expand Down
24 changes: 19 additions & 5 deletions src/concrete/deploy/MorphoPairAdapterBeaconSetDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ error ZeroBeaconOwner();
/// constructor.
/// @param initialOwner The initial owner of the beacon (controls upgrades).
/// @param central The central `ST0xPriceOracle` store every adapter deployed
/// through this beacon reads. Baked into the implementation as an immutable, so
/// it is fixed for the beacon's whole life (a fresh central needs a fresh
/// deployer). A zero central reverts `MorphoPairAdapter.ZeroCentral` in the
/// implementation constructor.
/// through this beacon reads. Baked into the FIRST implementation as an
/// immutable, and `iCentral` on this deployer records that value permanently. It
/// is NOT, however, structurally fixed for the beacon's whole life: the beacon
/// owner (governance) can upgrade the beacon to a `MorphoPairAdapter` built with
/// a different central, after which every live adapter reads the new central
/// while this deployer's `iCentral()` still reports the original. Treat
/// `iCentral()` as "the central at deploy time", not a live invariant — a
/// central change is an owner-authorised beacon upgrade, the same trust
/// boundary as any other implementation swap. A zero central reverts
/// `MorphoPairAdapter.ZeroCentral` in the implementation constructor.
struct MorphoPairAdapterBeaconSetDeployerConfig {
address initialOwner;
ST0xPriceOracle central;
Expand All @@ -41,7 +47,15 @@ struct MorphoPairAdapterBeaconSetDeployerConfig {
contract MorphoPairAdapterBeaconSetDeployer {
/// @notice Emitted when a new MorphoPairAdapter proxy is deployed.
/// @param caller The direct on-chain caller of `newMorphoPairAdapter`.
/// Indexed so monitoring can filter by deployer.
/// Indexed for filtering — but note minting is PERMISSIONLESS and the salt
/// is derived from the config alone (msg.sender excluded), so a third party
/// who sees the intended public config can front-run the mint and appear
/// here as `caller`. Monitoring that must identify a specific operator
/// should key on the deterministic proxy address (a commitment to the
/// config), not on this field. A front-run mint is config-identical,
/// `initializer`-guarded and sits behind the governance-owned beacon, so it
/// grants the front-runner no authority — it only reverts the operator's own
/// later mint on the CREATE2 collision.
/// @param oracle The address of the new proxy. Indexed for filtering.
event Deployment(address indexed caller, address indexed oracle);

Expand Down
10 changes: 9 additions & 1 deletion src/concrete/deploy/ST0xPriceOracleBeaconSetDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ struct ST0xPriceOracleBeaconSetDeployerConfig {
contract ST0xPriceOracleBeaconSetDeployer {
/// @notice Emitted when a new ST0xPriceOracle proxy is deployed.
/// @param caller The direct on-chain caller of `newST0xPriceOracle`.
/// Indexed so monitoring can filter by deployer.
/// Indexed for filtering — but note minting is PERMISSIONLESS and the salt
/// is derived from the config alone (msg.sender excluded), so a third party
/// who sees the intended public config can front-run the mint and appear
/// here as `caller`. Monitoring that must identify a specific operator
/// should key on the deterministic proxy address (a commitment to the
/// config), not on this field. A front-run mint is config-identical,
/// `initializer`-guarded and sits behind the governance-owned beacon, so it
/// grants the front-runner no authority — it only reverts the operator's own
/// later mint on the CREATE2 collision.
/// @param oracle The address of the new proxy. Indexed for filtering.
event Deployment(address indexed caller, address indexed oracle);

Expand Down
9 changes: 9 additions & 0 deletions test/src/script/Deploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ contract DeployTest is Test {
vm.stopBroadcast();
vm.setEnv("ST0X_ORACLE_ADMIN", vm.toString(ST0X_ORACLE_ADMIN));

// And for ST0X_SIGNER: `updatePrice` is permissionless and authorised
// solely by this signer, so a signer == deploy key ships the feed under
// CI's control. The guard must reject it by MESSAGE.
vm.setEnv("ST0X_SIGNER", vm.toString(deployer));
vm.expectRevert("ST0X_SIGNER must not be the deploy key");
deploy.run();
vm.stopBroadcast();
vm.setEnv("ST0X_SIGNER", vm.toString(ST0X_SIGNER));

// ----- ST0X_TIMEOUT uint64 bound (#267) -----
// `ST0X_TIMEOUT` is read as a uint256 and narrowed to uint64. Solidity's
// explicit downcast TRUNCATES silently, so a value of 2**64 + 3600 would
Expand Down
Loading