Skip to content

chore(injective): mainnet deploy config - #4

Merged
danvaneijck merged 1 commit into
injectivefrom
chore/injective-mainnet-deploy-config
Sep 8, 2026
Merged

chore(injective): mainnet deploy config#4
danvaneijck merged 1 commit into
injectivefrom
chore/injective-mainnet-deploy-config

Conversation

@danvaneijck

Copy link
Copy Markdown
Member

Adds script/config/injective-mainnet.json, so SCRIPT_CONFIG=injective-mainnet resolves.
Closes the infinity-core third of G-6 in SECURITY_AUDIT_2026-09-08.md — the audit's
finding was that the AMM could not be deployed to mainnet from the repo at all, because
BaseScript resolves SCRIPT_CONFIG to a file that did not exist.

A3 of Batch A. No src/ change, no test change, no deployed bytecode change.

The two filled addresses are DERIVED, not chosen

key value how it is known today
create3Factory 0xa4753315…07Df5 CREATE(0x02E0d5Fd…, 0) — confirmed with cast compute-address
poolOwner, protocolFeeControllerOwner 0x8a3c2cDa…bE10 CREATE3 at salt CHOICE-V2/TimelockController/1.0.0

The factory is deployed by plain CREATE from a dedicated nonce-0 EOA, never through the
Arachnid CREATE2 deployer — its constructor does Ownable(msg.sender) and
isUserWhitelisted[msg.sender] = true, so going through the deployer would make that stub the
owner and sole whitelisted user and brick the factory permanently. CREATE(eoa, 0) depends on
nothing else, which is what makes the address knowable before anything is deployed.

The timelock follows because CREATE3 derives an address from the salt and the factory alone.
It was derived by executing the deployed testnet factory's own bytecode at the mainnet factory
address (cast call --override-code) — and the control that makes it trustworthy rather than
merely computed is that the same method reproduces testnet's live timelock 0xfE9811111C…
exactly
.

🔴 Both hold only if the factory is deployed from 0x02E0d5Fd… at nonce 0. Every predicted
address was checked EMPTY on 1776 before this was written.

Why every deployed-address field is "0x"

Following ethereum-mainnet.json, and it is not cosmetic. getAddressFromConfig decodes an
unset value to address(0x20) and reverts "Address not set" — so a script run before its
dependency has landed stops instead of deploying something wired to address(0). The fields
are filled in as each phase lands.

Checked

  • Valid JSON; key set identical to injective-testnet.json, so no script finds a key missing
    at deploy time
  • Every filled key decodes through BaseScriptcreate3Factory, poolOwner and
    protocolFeeControllerOwner all resolve to the addresses above
  • Reading vault reverts "Address not set", proving the placeholder convention works in the
    direction that matters
  • Script 01 against SCRIPT_CONFIG=injective-mainnet, no broadcast: reads the config, resolves
    the factory, and fails only on the factory not existing yet on 1776 — which is correct
  • check-fork-pins.sh green

⚠️ Note for the reviewer, pre-existing and not from this PR: injective is 5 commits ahead of
the pin contracts/deployments/*.json names (c53aef3a). Those commits are CI, docs, a licence
notice and one test fix — src/ is byte-identical between the two, so no deployed bytecode is
affected. The fork-pin invariant is all copies of a fork agree with each other, not every pin is
at its fork's HEAD
, so nothing is red.

🤖 Generated with Claude Code

Injective mainnet (chain 1776). Closes G-6's core third — the AMM could not be
deployed from this repo at all, because `BaseScript` resolves `SCRIPT_CONFIG` to
a file that did not exist.

Two addresses are filled and both are DERIVED, not chosen:

- `create3Factory` 0xa4753315… is CREATE(0x02E0d5Fd…, 0). The factory is deployed
  by plain CREATE from a dedicated nonce-0 EOA — never through the Arachnid
  CREATE2 deployer, which would make that stub the owner and sole whitelisted
  user and brick the factory permanently. CREATE(eoa, 0) depends on nothing else,
  so the address is knowable today. Confirmed with `cast compute-address`.

- `poolOwner` / `protocolFeeControllerOwner` 0x8a3c2cDa… is the mainnet
  TimelockController at salt CHOICE-V2/TimelockController/1.0.0. CREATE3 derives
  an address from the salt and the factory alone, so this is knowable before
  script 01 has ever run. Derived by executing the DEPLOYED testnet factory's
  bytecode at the mainnet factory address (`cast call --override-code`); the same
  method reproduces testnet's own timelock 0xfE9811111C… exactly, which is the
  control that makes the number trustworthy rather than merely computed.

Every deployed-address field is "0x", following ethereum-mainnet.json. That is
not cosmetic: `getAddressFromConfig` decodes an unset value to address(0x20) and
reverts "Address not set", so a script run before its dependency has landed stops
instead of deploying something wired to address(0). Proven both ways — the two
filled keys decode to the addresses above, and reading `vault` reverts.

🔴 These hold only if the factory is deployed from 0x02E0d5Fd… at nonce 0. All
predicted addresses were checked EMPTY on 1776 before this was written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@danvaneijck
danvaneijck merged commit 073d9cd into injective Sep 8, 2026
3 checks passed
@danvaneijck
danvaneijck deleted the chore/injective-mainnet-deploy-config branch September 8, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant