Files
zeekay 32956c020d Play on Lux Testnet, and publish the site from main
The only live network was Sepolia, where deploy.sepolia.json pointed at
OpenZeppelin's own deployment of the levels; its Motorbike still checks
the old condition.  Lux Testnet, C-Chain 96368, replaces it, defined once
in src/chains.js for the client and the supersede script.  Until the
levels are deployed there, the game offers to deploy them from the
browser, as it does on any network without deploy data.

A push to main that passes the tests now publishes client/build to the
Sites plane as project `game`.

Claude-Session: https://claude.ai/code/session_019ABm6hF8RNyUFpbjQWqfxY
2026-09-23 14:52:34 -07:00

3.2 KiB

OZ-Lux contract level replacement tool

Description

Change the contract code directly in the repo will change the front-end contract but not the on-chain bytecode of that level. Therefore, while players try to attack a level instance, the front-end will show a different contract than what they actually have to attack, they may differ with function names for example (i.e. selectors).

supersede_level.mjs script implements a tool to supersede contract deployed on-chain when it will be required (when vulnerable code displayed on the front-end will not exactly match the deployed contracts).

The tool performs the substitution by following these steps:

  • Deploys new level contract.
  • Registers level in Lux.sol
  • Upgrades Statistics.sol to StatisticsLevelSuperseder.sol, a implementation that contains the logic to change statistics storage:
    • levelFirstInstanceCreationTime - dump from old level address to new one.
    • levelFirstCompletionTime - dump from old level address to new one.
    • playerStats - dump from old level address to new one.
    • levelStats - dump from old level address to new one.
    • levelExists - set false old level address.
    • levels - fix supersede old level with new one and wipe extra entry.
  • Dumps the data
  • Downgrades the Statistics.sol to original one.

All this process is required in order to avoid to the users to resolve both versions and score double, maintain the statistics of a specific level, and maintain the consistency of the leaderboard scores.

Usage

NETWORK=lux-testnet RPC_URL=https://api.lux-test.network/v1/chain/C/rpc PRIV_KEY=<operator key> yarn supersede:level

NETWORK picks client/src/gamedata/deploy.<network>.json (local by default) and RPC_URL the node (http://127.0.0.1:8545 by default). The account behind PRIV_KEY signs every transaction and becomes the operator that runs the storage dump, so it must own Lux and the ProxyAdmin. Without PRIV_KEY, the node signs for FROM, or for its first unlocked account when FROM is unset, which is what anvil offers. The tool reads ABIs and bytecode from contracts/out, so run forge build first.

The tool asks for the deployId and a confirmation; answers can also be piped in (printf '8\ny\n' | yarn supersede:level). If a replacement was interrupted, running it again resumes from the dump stage stored on chain.

Lux is halted during the operation: any call to createLevelInstance(Level _level) or submitLevelInstance(address payable _instance) reverts with "Contract locked due maintenance operations".

At the end the proxy returns to the Statistics implementation named by implementation in the deploy data, and the deploy data records the substitution under supersededAddresses. Dump transactions carry a fixed gas limit (DUMP_GAS) because the dump functions work until gasleft() runs low.

Test in a local fork

anvil --fork-url <rpc url> --auto-impersonate
NETWORK=lux-testnet FROM=<Lux owner> yarn supersede:level

With --auto-impersonate anvil signs for any address, so FROM can be the real owner of Lux and the ProxyAdmin.