Skip to content

Commit 7cc4a00

Browse files
committed
fix(deploy): accept the key in the shape wallets actually export it
The owner's first mainnet deploy attempt failed on this, and the error was correct and useless: "vm.envUint: failed parsing $DEPLOYER_PRIVATE_KEY as type uint256: missing hex prefix (0x)". vm.envUint requires the prefix. MetaMask - the obvious place an operator gets the key from - exports it WITHOUT one. So the first attempt fails on a formatting detail unrelated to the deployment, after the operator has already typed a private key into a terminal. That is the worst moment to make someone retry blind, and the second worst to make them re-handle the key. Read as a string and normalised now. Both shapes work; the key is never logged, only its length is checked, and a wrong length is refused with a message that says what is expected. Everything before it in the trace had already passed, which is the part worth noting: the governance quorum guard read getThreshold() = 2 and getOwners() = 3 owners off the owner's real Safe and let it through. The guard added earlier today works against a real Safe on the real chain, not just a mock. Tested all three shapes: 64 chars bare, 66 with 0x, and garbage refused.
1 parent 1c2672d commit 7cc4a00

1 file changed

Lines changed: 31 additions & 1 deletion

File tree

contracts/script/Deploy.s.sol

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ contract Deploy is Script {
5656

5757
_assertSafeForChain(config);
5858

59-
uint256 deployerKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
59+
uint256 deployerKey = _deployerKey();
6060

6161
vm.startBroadcast(deployerKey);
6262

@@ -137,6 +137,36 @@ contract Deploy is Script {
137137
/// integration test legitimately runs with an EOA holding governance,
138138
/// and a check that blocked that would only teach people to comment it
139139
/// out — which is worse than not having it.
140+
/**
141+
* @dev The deployer key, with or without a `0x` prefix.
142+
*
143+
* `vm.envUint` requires the prefix and fails with "missing hex prefix"
144+
* when it is absent. That is a correct error and a useless one: every
145+
* wallet this key comes out of exports it WITHOUT the prefix. MetaMask
146+
* does, and it is the obvious place an operator gets it from.
147+
*
148+
* So the first attempt at a mainnet deploy fails on a formatting detail
149+
* that has nothing to do with the deployment, after the operator has
150+
* already typed a private key into a terminal — which is exactly the
151+
* moment to not make someone retry blind.
152+
*
153+
* Read as a string and normalised instead. The key itself is never
154+
* logged; only its shape is checked.
155+
*/
156+
function _deployerKey() internal view returns (uint256) {
157+
string memory raw = vm.envString("DEPLOYER_PRIVATE_KEY");
158+
bytes memory b = bytes(raw);
159+
160+
require(b.length == 64 || b.length == 66, "Deploy: DEPLOYER_PRIVATE_KEY must be 64 hex chars, 0x optional");
161+
162+
if (b.length == 66) {
163+
require(b[0] == "0" && (b[1] == "x" || b[1] == "X"), "Deploy: 66-char key must start with 0x");
164+
return vm.parseUint(raw);
165+
}
166+
167+
return vm.parseUint(string.concat("0x", raw));
168+
}
169+
140170
function _assertSafeForChain(Config memory config) internal view {
141171
require(config.governance != address(0), "Deploy: governance is zero");
142172
require(config.treasury != address(0), "Deploy: treasury is zero");

0 commit comments

Comments
 (0)