UNPKG

@unruggable/gateways

Version:

Trustless Ethereum Multichain CCIP-Read Gateway

214 lines (213 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OPFaultRollup = exports.GAME_ABI = exports.GAME_FINDER_ABI = exports.PORTAL_ABI = void 0; const contract_1 = require("ethers/contract"); const abi_1 = require("ethers/abi"); const chains_js_1 = require("../chains.cjs"); const utils_js_1 = require("../utils.cjs"); const AbstractOPRollup_js_1 = require("./AbstractOPRollup.cjs"); // https://docs.optimism.io/chain/differences // https://specs.optimism.io/fault-proof/stage-one/bridge-integration.html exports.PORTAL_ABI = new abi_1.Interface([ `function disputeGameFactory() view returns (address)`, `function respectedGameType() view returns (uint32)`, `function disputeGameBlacklist(address game) view returns (bool)`, ]); exports.GAME_FINDER_ABI = new abi_1.Interface([ `error GameNotFound()`, `error InvalidGameTypeBitMask()`, `function findGameIndex(address portal, uint256 minAge, uint256 gameTypeBitMask, uint256 gameCount) view returns (uint256)`, `function gameAtIndex(address portal, uint256 minAge, uint256 gameTypeBitMask, uint256 gameIndex) view returns ( uint256 gameType, uint256 created, address gameProxy, uint256 l2BlockNumber, bytes32 rootClaim )`, ]); exports.GAME_ABI = new abi_1.Interface([ `function rootClaim() view returns (bytes32)`, ]); const GAME_FINDER_MAINNET = '0x61F50A76bfb2Ad8620A3E8F81aa27f3bEb1Db0D7'; const GAME_FINDER_SEPOLIA = '0x1577670E6AC3307D17A8f81464ADc2070A2EFcaa'; //const GAME_FINDER_HOLESKY = '0xedb18cd8d9D6AF54C4Ac1FbDBF2E098F413c3fe9'; class OPFaultRollup extends AbstractOPRollup_js_1.AbstractOPRollup { minAgeSec; // https://docs.optimism.io/chain/addresses static mainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.OP, OptimismPortal: '0xbEb5Fc579115071764c7423A4f12eDde41f106Ed', GameFinder: GAME_FINDER_MAINNET, }; static sepoliaConfig = { chain1: chains_js_1.CHAINS.SEPOLIA, chain2: chains_js_1.CHAINS.OP_SEPOLIA, OptimismPortal: '0x16Fc5058F25648194471939df75CF27A2fdC48BC', GameFinder: GAME_FINDER_SEPOLIA, }; // https://docs.base.org/docs/base-contracts#l1-contract-addresses static baseMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.BASE, OptimismPortal: '0x49048044D57e1C92A77f79988d21Fa8fAF74E97e', GameFinder: GAME_FINDER_MAINNET, }; // https://docs.base.org/docs/base-contracts/#ethereum-testnet-sepolia static baseSepoliaConfig = { chain1: chains_js_1.CHAINS.SEPOLIA, chain2: chains_js_1.CHAINS.BASE_SEPOLIA, OptimismPortal: '0x49f53e41452C74589E85cA1677426Ba426459e85', GameFinder: GAME_FINDER_SEPOLIA, }; // https://docs.inkonchain.com/useful-information/contracts#l1-contract-addresses static inkMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.INK, OptimismPortal: '0x5d66c1782664115999c47c9fa5cd031f495d3e4f', GameFinder: GAME_FINDER_MAINNET, }; static inkSepoliaConfig = { chain1: chains_js_1.CHAINS.SEPOLIA, chain2: chains_js_1.CHAINS.INK_SEPOLIA, OptimismPortal: '0x5c1d29C6c9C8b0800692acC95D700bcb4966A1d7', GameFinder: GAME_FINDER_SEPOLIA, }; // https://docs.unichain.org/docs/technical-information/contract-addresses static unichainMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.UNICHAIN, OptimismPortal: '0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2', GameFinder: GAME_FINDER_MAINNET, }; static unichainSepoliaConfig = { chain1: chains_js_1.CHAINS.SEPOLIA, chain2: chains_js_1.CHAINS.UNICHAIN_SEPOLIA, OptimismPortal: '0x0d83dab629f0e0F9d36c0Cbc89B69a489f0751bD', GameFinder: GAME_FINDER_SEPOLIA, }; // https://docs.soneium.org/docs/builders/contracts static soneiumMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.SONEIUM, OptimismPortal: '0x88e529a6ccd302c948689cd5156c83d4614fae92', GameFinder: GAME_FINDER_MAINNET, }; static soneiumMinatoConfig = { chain1: chains_js_1.CHAINS.SEPOLIA, chain2: chains_js_1.CHAINS.SONEIUM_SEPOLIA, OptimismPortal: '0x65ea1489741A5D72fFdD8e6485B216bBdcC15Af3', GameFinder: GAME_FINDER_SEPOLIA, }; // https://build.swellnetwork.io/docs/developer-resources/contract-addresses static swellMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.SWELL, OptimismPortal: '0x758E0EE66102816F5C3Ec9ECc1188860fbb87812', GameFinder: GAME_FINDER_MAINNET, }; static swellSepoliaConfig = { chain1: chains_js_1.CHAINS.SEPOLIA, chain2: chains_js_1.CHAINS.SWELL_SEPOLIA, OptimismPortal: '0x595329c60c0b9e54a5246e98fb0fa7fcfd454f64', GameFinder: GAME_FINDER_SEPOLIA, }; // https://docs.worldcoin.org/world-chain/developers/world-chain-contracts static worldMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.WORLD, OptimismPortal: '0xd5ec14a83B7d95BE1E2Ac12523e2dEE12Cbeea6C', GameFinder: GAME_FINDER_MAINNET, }; // https://storage.googleapis.com/cel2-rollup-files/celo/deployment-l1.json static celoMainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.CELO, OptimismPortal: '0xc5c5D157928BDBD2ACf6d0777626b6C75a9EAEDC', GameFinder: GAME_FINDER_MAINNET, }; // 20240917: delayed constructor not needed OptimismPortal; GameFinder; gameTypes = []; // if empty, dynamically uses respectedGameType() unfinalizedRootClaimTimeoutMs = 30000; constructor(providers, config, minAgeSec = 0) { super(providers); this.minAgeSec = minAgeSec; this.OptimismPortal = new contract_1.Contract(config.OptimismPortal, exports.PORTAL_ABI, providers.provider1); this.GameFinder = new contract_1.Contract(config.GameFinder, exports.GAME_FINDER_ABI, providers.provider1); } get gameTypeBitMask() { return this.gameTypes.reduce((a, x) => a | (1 << x), 0); } get unfinalized() { return !!this.minAgeSec; // nonzero => unfinalized } async fetchRespectedGameType() { return this.OptimismPortal.respectedGameType({ blockTag: this.latestBlockTag, }); } async _ensureRootClaim(index) { // dodge canary by requiring a valid root claim // finalized claims are assumed valid if (this.unfinalized) { const timeout = Date.now() + this.unfinalizedRootClaimTimeoutMs; // prevent "infinite" loop for (;;) { try { await this.fetchCommit(index); break; } catch (err) { // NOTE: this could fail for a variety of reasons // so we can't just catch "invalid root claim" // canary often has invalid block <== likely triggers first // canary has invalid time // canary has invalid root claim // 20250503: this can infinite loop when the rpc errors suck if ((0, utils_js_1.isEthersError)(err)) throw err; if (Date.now() > timeout) throw new Error(`timeout _ensureRootClaim()`); index = await this.GameFinder.findGameIndex(this.OptimismPortal.target, this.minAgeSec, this.gameTypeBitMask, index); } } } return index; } async fetchLatestCommitIndex() { // the primary assumption is that the anchor root is the finalized state // however, this is strangely conditional on the gameType // (apparently because the anchor state registry is *not* intended for finalization) // after a gameType switch, the finalized state "rewinds" to the latest game of the new type // to solve this, we use the latest finalized game of *any* supported gameType // 20240820: correctly handles the aug 16 respectedGameType change // this should be simplified in the future once there is a better policy // 20240822: once again uses a helper contract to reduce rpc burden return this._ensureRootClaim(await this.GameFinder.findGameIndex(this.OptimismPortal.target, this.minAgeSec, this.gameTypeBitMask, 0, // most recent game { blockTag: this.latestBlockTag })); } async _fetchParentCommitIndex(commit) { return this._ensureRootClaim(await this.GameFinder.findGameIndex(this.OptimismPortal.target, this.minAgeSec, this.gameTypeBitMask, commit.index)); } async _fetchCommit(index) { // note: GameFinder checks isCommitStillValid() const game = (await this.GameFinder.gameAtIndex(this.OptimismPortal.target, this.minAgeSec, this.gameTypeBitMask, index)).toObject(); if (!game.l2BlockNumber) throw new Error('invalid game'); const commit = await this.createCommit(index, game.l2BlockNumber); if (this.unfinalized) { const gameProxy = new contract_1.Contract(game.gameProxy, exports.GAME_ABI, this.provider1); const expected = await gameProxy.rootClaim(); const computed = (0, AbstractOPRollup_js_1.hashOutputRootProof)(commit); if (expected !== computed) throw new Error(`invalid root claim`); } return { ...commit, game }; } async isCommitStillValid(commit) { return !(await this.OptimismPortal.disputeGameBlacklist(commit.game.gameProxy)); } windowFromSec(sec) { // finalization time is on-chain // https://github.com/ethereum-optimism/optimism/blob/a81de910dc2fd9b2f67ee946466f2de70d62611a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L590 return sec; } } exports.OPFaultRollup = OPFaultRollup;