Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom parent chains #181

Merged
merged 48 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
422ccdb
refactor: split getters
spsjvc Sep 8, 2024
615cecd
init custom parent chain
spsjvc Sep 8, 2024
9605b75
hmm
spsjvc Sep 8, 2024
c054413
temp
spsjvc Sep 9, 2024
87f50cf
Merge branch 'main' into feat-custom-parent-chain
spsjvc Sep 9, 2024
ed0cefa
add test
spsjvc Sep 9, 2024
88d1a21
more
spsjvc Sep 10, 2024
b45ca09
more
spsjvc Sep 10, 2024
4bb418d
fix
spsjvc Sep 10, 2024
085c714
stuff
spsjvc Sep 10, 2024
8d4069c
export
spsjvc Sep 10, 2024
c2ae2f3
Merge branch 'main' into feat-custom-parent-chain
spsjvc Sep 10, 2024
b455803
bump
spsjvc Sep 10, 2024
f595846
Merge branch 'main' into feat-custom-parent-chain
spsjvc Sep 30, 2024
3125366
clean up
spsjvc Oct 1, 2024
c2abe88
update snapshot
spsjvc Oct 1, 2024
4fe4483
Merge branch 'main' into feat-custom-parent-chain
spsjvc Oct 2, 2024
38bfe25
Merge branch 'main' into feat-custom-parent-chain
spsjvc Oct 3, 2024
1d7174c
clean up a bit
spsjvc Oct 3, 2024
a41659b
Merge branch 'main' into feat-custom-parent-chain
spsjvc Oct 8, 2024
bf103fc
Merge branch 'main' into feat-custom-parent-chain
spsjvc Oct 8, 2024
ea0d34d
wip
spsjvc Oct 8, 2024
7ede730
nice
spsjvc Oct 8, 2024
4b7678e
remove
spsjvc Oct 8, 2024
5f73c3d
tmp
spsjvc Oct 8, 2024
e44f7e9
update test
spsjvc Oct 8, 2024
28b3ded
fix
spsjvc Oct 8, 2024
8e8b420
fix
spsjvc Oct 8, 2024
b7cb59d
fixes
spsjvc Oct 8, 2024
6e17a34
fix
spsjvc Oct 8, 2024
4629f0f
nice
spsjvc Oct 8, 2024
9ff31f8
simplify
spsjvc Oct 8, 2024
34fc9fb
refactor
spsjvc Oct 9, 2024
e33bfc8
fix
spsjvc Oct 9, 2024
645889d
fix
spsjvc Oct 9, 2024
d49fc26
refactor
spsjvc Oct 9, 2024
f403266
fix
spsjvc Oct 9, 2024
3ab6740
move stuff
spsjvc Oct 9, 2024
a311170
fix error
spsjvc Oct 9, 2024
cba60f6
refactor
spsjvc Oct 9, 2024
2f7dc7e
clean up
spsjvc Oct 9, 2024
be8c257
clean up
spsjvc Oct 9, 2024
68c7899
rename file plus add test case
spsjvc Oct 9, 2024
8c0d960
move stuff
spsjvc Oct 9, 2024
761052f
use default value
spsjvc Oct 9, 2024
a542da3
add validation and tests
spsjvc Oct 9, 2024
54cf124
add another test
spsjvc Oct 9, 2024
2fb05c6
wrap up
spsjvc Oct 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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