Skip to content

Commit

Permalink
feat: custom parent chains (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
spsjvc authored Oct 10, 2024
1 parent f225ca0 commit 5b9a587
Show file tree
Hide file tree
Showing 19 changed files with 620 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`creates a config for a chain on top of a custom parent chain 1`] = `
{
"baseStake": 100000000000000000n,
"chainConfig": "{\\"homesteadBlock\\":0,\\"daoForkBlock\\":null,\\"daoForkSupport\\":true,\\"eip150Block\\":0,\\"eip150Hash\\":\\"0x0000000000000000000000000000000000000000000000000000000000000000\\",\\"eip155Block\\":0,\\"eip158Block\\":0,\\"byzantiumBlock\\":0,\\"constantinopleBlock\\":0,\\"petersburgBlock\\":0,\\"istanbulBlock\\":0,\\"muirGlacierBlock\\":0,\\"berlinBlock\\":0,\\"londonBlock\\":0,\\"clique\\":{\\"period\\":0,\\"epoch\\":0},\\"arbitrum\\":{\\"EnableArbOS\\":true,\\"AllowDebugPrecompiles\\":false,\\"DataAvailabilityCommittee\\":false,\\"InitialArbOSVersion\\":32,\\"GenesisBlockNum\\":0,\\"MaxCodeSize\\":24576,\\"MaxInitCodeSize\\":49152,\\"InitialChainOwner\\":\\"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\\"},\\"chainId\\":123}",
"chainId": 123n,
"confirmPeriodBlocks": 1n,
"extraChallengeTimeBlocks": 0n,
"genesisBlockNum": 0n,
"loserStakeEscrow": "0x0000000000000000000000000000000000000000",
"owner": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"sequencerInboxMaxTimeVariation": {
"delayBlocks": 2n,
"delaySeconds": 4n,
"futureBlocks": 3n,
"futureSeconds": 5n,
},
"stakeToken": "0x0000000000000000000000000000000000000000",
"wasmModuleRoot": "0x184884e1eb9fefdc158f6c8ac912bb183bf3cf83f0090317e0bc4ac5860baa39",
}
`;

exports[`creates config for a chain on top of arbitrum one with defaults 1`] = `
{
"baseStake": 100000000000000000n,
Expand Down
68 changes: 67 additions & 1 deletion src/chains.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineChain } from 'viem';
import { defineChain, Chain, ChainContract, isAddress, zeroAddress } from 'viem';
import {
mainnet,
arbitrum as arbitrumOne,
Expand Down Expand Up @@ -59,6 +59,72 @@ const nitroTestnodeL3 = defineChain({
testnet: true,
});

const customParentChains: Record<number, Chain> = {};

export function getCustomParentChains(): Chain[] {
return Object.values(customParentChains);
}

/**
* Registers a custom parent chain.
*
* @param {Chain} chain Regular `Chain` object with mandatory `contracts.rollupCreator` and `contracts.tokenBridgeCreator` fields.
*
* @example
* registerCustomParentChain({
* id: 123_456,
* name: `My Chain`,
* network: `my-chain`,
* nativeCurrency: {
* name: 'Ether',
* symbol: 'ETH',
* decimals: 18,
* },
* rpcUrls: {
* public: {
* http: ['http://localhost:8080'],
* },
* default: {
* http: ['http://localhost:8080'],
* },
* },
* // these are mandatory
* contracts: {
* rollupCreator: {
* address: '0x0000000000000000000000000000000000000001',
* },
* tokenBridgeCreator: {
* address: '0x0000000000000000000000000000000000000002',
* },
* },
* });
*/
export function registerCustomParentChain(
chain: Chain & {
contracts: {
rollupCreator: ChainContract;
tokenBridgeCreator: ChainContract;
};
},
) {
const rollupCreator = chain.contracts.rollupCreator.address;
const tokenBridgeCreator = chain.contracts.tokenBridgeCreator.address;

if (!isAddress(rollupCreator) || rollupCreator === zeroAddress) {
throw new Error(
`"contracts.rollupCreator.address" is invalid for custom parent chain with id ${chain.id}`,
);
}

if (!isAddress(tokenBridgeCreator) || tokenBridgeCreator === zeroAddress) {
throw new Error(
`"contracts.tokenBridgeCreator.address" is invalid for custom parent chain with id ${chain.id}`,
);
}

customParentChains[chain.id] = chain;
}

export const chains = [
// mainnet L1
mainnet,
Expand Down
62 changes: 62 additions & 0 deletions src/chains.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, expect, it } from 'vitest';

import { getCustomParentChains, registerCustomParentChain } from './chains';
import { testHelper_createCustomParentChain } from './testHelpers';

describe('registerCustomParentChain', () => {
it(`throws if "contracts.rollupCreator.address" is invalid`, () => {
// omit contracts from the chain
const { contracts, ...chain } = testHelper_createCustomParentChain();

expect(() =>
registerCustomParentChain({
...chain,
contracts: {
rollupCreator: {
address: '0x123',
},
tokenBridgeCreator: {
address: '0x123',
},
},
}),
).toThrowError(
`"contracts.rollupCreator.address" is invalid for custom parent chain with id ${chain.id}`,
);
});

it(`throws if "contracts.tokenBridgeCreator.address" is invalid`, () => {
// omit contracts from the chain
const { contracts, ...chain } = testHelper_createCustomParentChain();

expect(() =>
registerCustomParentChain({
...chain,
contracts: {
rollupCreator: {
// use a correct address for the RollupCreator
address: contracts.rollupCreator.address,
},
tokenBridgeCreator: {
address: '0x0',
},
},
}),
).toThrowError(
`"contracts.tokenBridgeCreator.address" is invalid for custom parent chain with id ${chain.id}`,
);
});

it('successfully registers a custom parent chain', () => {
const chain = testHelper_createCustomParentChain();

// assert before
expect(getCustomParentChains().map((c) => c.id)).not.includes(chain.id);

// register
registerCustomParentChain(chain);

// assert after
expect(getCustomParentChains().map((c) => c.id)).includes(chain.id);
});
});
16 changes: 9 additions & 7 deletions src/createRollupFetchTransactionHash.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Address, PublicClient, Transport, Chain } from 'viem';
import { AbiEvent } from 'abitype';

import { validateParentChain } from './types/ParentChain';
import { ParentChainId, validateParentChain } from './types/ParentChain';
import {
mainnet,
arbitrumOne,
Expand Down Expand Up @@ -40,7 +40,9 @@ const RollupInitializedEventAbi: AbiEvent = {
type: 'event',
};

const earliestRollupCreatorDeploymentBlockNumber = {
const earliestRollupCreatorDeploymentBlockNumber: {
[Key in ParentChainId]: bigint;
} = {
// mainnet L1
[mainnet.id]: 18736164n,
// mainnet L2
Expand All @@ -62,12 +64,12 @@ export async function createRollupFetchTransactionHash<TChain extends Chain | un
rollup,
publicClient,
}: CreateRollupFetchTransactionHashParams<TChain>) {
const { chainId } = validateParentChain(publicClient);
const { chainId: parentChainId, isCustom: parentChainIsCustom } =
validateParentChain(publicClient);

const fromBlock =
chainId in earliestRollupCreatorDeploymentBlockNumber
? earliestRollupCreatorDeploymentBlockNumber[chainId]
: 'earliest';
const fromBlock = parentChainIsCustom
? 'earliest'
: earliestRollupCreatorDeploymentBlockNumber[parentChainId];

// Find the RollupInitialized event from that Rollup contract
const rollupInitializedEvents = await publicClient.getLogs({
Expand Down
49 changes: 42 additions & 7 deletions src/createRollupPrepareDeploymentParamsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { prepareChainConfig } from './prepareChainConfig';

import { defaults } from './createRollupPrepareDeploymentParamsConfigDefaults';
import { getDefaultConfirmPeriodBlocks } from './getDefaultConfirmPeriodBlocks';
import { getDefaultSequencerInboxMaxTimeVariation } from './getDefaultSequencerInboxMaxTimeVariation';
import {
SequencerInboxMaxTimeVariation,
getDefaultSequencerInboxMaxTimeVariation,
} from './getDefaultSequencerInboxMaxTimeVariation';

export type CreateRollupPrepareDeploymentParamsConfigResult =
CreateRollupFunctionInputs[0]['config'];
Expand Down Expand Up @@ -65,18 +68,50 @@ export type CreateRollupPrepareDeploymentParamsConfigParams = Prettify<
*/
export function createRollupPrepareDeploymentParamsConfig<TChain extends Chain | undefined>(
client: Client<Transport, TChain>,
{ chainConfig, ...params }: CreateRollupPrepareDeploymentParamsConfigParams,
{
chainConfig,
confirmPeriodBlocks,
sequencerInboxMaxTimeVariation,
...params
}: CreateRollupPrepareDeploymentParamsConfigParams,
): CreateRollupPrepareDeploymentParamsConfigResult {
const { chainId: parentChainId } = validateParentChain(client);
const { chainId: parentChainId, isCustom: parentChainIsCustom } = validateParentChain(client);

const defaultsBasedOnParentChain = {
confirmPeriodBlocks: getDefaultConfirmPeriodBlocks(parentChainId),
sequencerInboxMaxTimeVariation: getDefaultSequencerInboxMaxTimeVariation(parentChainId),
let paramsByParentBlockTime: {
confirmPeriodBlocks: bigint;
sequencerInboxMaxTimeVariation: SequencerInboxMaxTimeVariation;
};

if (parentChainIsCustom) {
if (typeof confirmPeriodBlocks === 'undefined') {
throw new Error(
`"params.confirmPeriodBlocks" must be provided when using a custom parent chain.`,
);
}

if (typeof sequencerInboxMaxTimeVariation === 'undefined') {
throw new Error(
`"params.sequencerInboxMaxTimeVariation" must be provided when using a custom parent chain.`,
);
}

paramsByParentBlockTime = {
confirmPeriodBlocks,
sequencerInboxMaxTimeVariation,
};
} else {
const defaultConfirmPeriodBlocks = getDefaultConfirmPeriodBlocks(parentChainId);
const defaultSequencerInboxMTV = getDefaultSequencerInboxMaxTimeVariation(parentChainId);

paramsByParentBlockTime = {
confirmPeriodBlocks: confirmPeriodBlocks ?? defaultConfirmPeriodBlocks,
sequencerInboxMaxTimeVariation: sequencerInboxMaxTimeVariation ?? defaultSequencerInboxMTV,
};
}

return {
...defaults,
...defaultsBasedOnParentChain,
...paramsByParentBlockTime,
...params,
chainConfig: JSON.stringify(
chainConfig ??
Expand Down
77 changes: 76 additions & 1 deletion src/createRollupPrepareDeploymentParamsConfig.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { it, expect } from 'vitest';
import { Address, createPublicClient, http } from 'viem';

import { arbitrumOne, arbitrumSepolia, base, baseSepolia } from './chains';
import {
arbitrumOne,
arbitrumSepolia,
base,
baseSepolia,
registerCustomParentChain,
} from './chains';
import { prepareChainConfig } from './prepareChainConfig';
import { createRollupPrepareDeploymentParamsConfig } from './createRollupPrepareDeploymentParamsConfig';

import { testHelper_createCustomParentChain } from './testHelpers';

const chainId = 69_420n;
const vitalik: `0x${string}` = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045';

Expand Down Expand Up @@ -117,3 +125,70 @@ it('creates config for a chain on top of base sepolia with defaults', () => {
}),
).toMatchSnapshot();
});

it('fails to create a config for a chain on top of a custom parent chain if "confirmPeriodBlocks" is not provided', () => {
const chain = testHelper_createCustomParentChain();

const publicClient = createPublicClient({
chain,
transport: http(),
});

registerCustomParentChain(chain);

expect(() =>
createRollupPrepareDeploymentParamsConfig(publicClient, {
owner: vitalik,
chainId: BigInt(chain.id),
}),
).toThrowError('"params.confirmPeriodBlocks" must be provided when using a custom parent chain');
});

it('fails to create a config for a chain on top of a custom parent chain if "sequencerInboxMaxTimeVariation" is not provided', () => {
const chain = testHelper_createCustomParentChain();

const publicClient = createPublicClient({
chain,
transport: http(),
});

registerCustomParentChain(chain);

expect(() =>
createRollupPrepareDeploymentParamsConfig(publicClient, {
owner: vitalik,
chainId: BigInt(chain.id),
confirmPeriodBlocks: 1n,
}),
).toThrowError(
'"params.sequencerInboxMaxTimeVariation" must be provided when using a custom parent chain.',
);
});

it('creates a config for a chain on top of a custom parent chain', () => {
const chain = testHelper_createCustomParentChain({
// using a specific chain id here as it's a snapshot test
id: 123,
});

const publicClient = createPublicClient({
chain,
transport: http(),
});

registerCustomParentChain(chain);

expect(
createRollupPrepareDeploymentParamsConfig(publicClient, {
owner: vitalik,
chainId: BigInt(chain.id),
confirmPeriodBlocks: 1n,
sequencerInboxMaxTimeVariation: {
delayBlocks: 2n,
futureBlocks: 3n,
delaySeconds: 4n,
futureSeconds: 5n,
},
}),
).toMatchSnapshot();
});
Loading

0 comments on commit 5b9a587

Please sign in to comment.