@unruggable/gateways
Version:
Trustless Ethereum Multichain CCIP-Read Gateway
78 lines (77 loc) • 3.47 kB
JavaScript
import { AbstractRollup, } from '../rollup.mjs';
import { Contract } from 'ethers/contract';
import { dataSlice } from 'ethers/utils';
import { CHAINS } from '../chains.mjs';
import { EthProver } from '../eth/EthProver.mjs';
import { ABI_CODER } from '../utils.mjs';
import { ROLLUP_ABI } from './types.mjs';
// NOTE: this is very similar to Scroll...
// https://docs.morphl2.io/docs/about-morph/morphs-architecture
// https://docs.morphl2.io/docs/build-on-morph/build-on-morph/bridge-between-morph-and-ethereum
// https://docs.morphl2.io/docs/build-on-morph/developer-resources/morph-json-rpc-api-methods
export class MorphRollup extends AbstractRollup {
// https://docs.morphl2.io/docs/build-on-morph/developer-resources/contracts
static mainnetConfig = {
chain1: CHAINS.MAINNET,
chain2: CHAINS.MORPH,
Rollup: '0x759894ced0e6af42c26668076ffa84d02e3cef60',
poseidon: '0x3508174Fa966e75f70B15348209E33BC711AE63e',
};
static holeskyConfig = {
chain1: CHAINS.HOLESKY,
chain2: CHAINS.MORPH_HOLESKY,
Rollup: '0x759894ced0e6af42c26668076ffa84d02e3cef60',
poseidon: '0x3508174Fa966e75f70B15348209E33BC711AE63e',
};
Rollup;
poseidon;
constructor(providers, config) {
super(providers);
this.Rollup = new Contract(config.Rollup, ROLLUP_ABI, this.provider1);
this.poseidon = config.poseidon;
}
async fetchLatestCommitIndex() {
return this.Rollup.lastFinalizedBatchIndex({
blockTag: this.latestBlockTag,
});
}
async _fetchParentCommitIndex(commit) {
return this.Rollup.lastFinalizedBatchIndex({
blockTag: commit.l1BlockNumber - 1,
});
}
async _fetchCommit(index) {
const [[commitEvent], [finalEvent]] = await Promise.all([
this.Rollup.queryFilter(this.Rollup.filters.CommitBatch(index)),
this.Rollup.queryFilter(this.Rollup.filters.FinalizeBatch(index)),
]);
if (!commitEvent)
throw new Error(`unknown batch`);
if (!finalEvent)
throw new Error('not finalized');
const tx = await commitEvent.getTransaction();
const desc = this.Rollup.interface.parseTransaction(tx);
if (!desc)
throw new Error(`unknown transaction: ${tx.hash}`);
const prover = new EthProver(this.provider2, parseLastBlock(desc.args.batchDataInput.blockContexts));
return { index, prover, l1BlockNumber: finalEvent.blockNumber };
}
encodeWitness(commit, proofSeq) {
return ABI_CODER.encode(['(uint256, bytes[], bytes)'], [[commit.index, proofSeq.proofs, proofSeq.order]]);
}
windowFromSec(sec) {
// finalization time is not on-chain
// https://etherscan.io/advanced-filter?eladd=0x759894ced0e6af42c26668076ffa84d02e3cef60&eltpc=0x26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d
// https://explorer.morphl2.io/batches
// block time: ~4sec
// blocks per batch: ~285
return Math.ceil(sec / 300); // units of batchIndex
}
}
function parseLastBlock(context) {
// https://github.com/morph-l2/morph/blob/main/contracts/contracts/libraries/codec/BatchCodecV0.sol
const SIZE = 60;
const count = parseInt(context.slice(0, 6)); // uint16 => numBlocks
const pos = 2 + SIZE * (count - 1);
return BigInt(dataSlice(context, pos, pos + 8)); // uint64 => block[numBlocks - 1].blockNumber
}