Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 1 addition & 12 deletions packages/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1 @@
import * as path from 'path'
import { ethers } from 'ethers'
import { Interface } from 'ethers/lib/utils'

export const getContractDefinition = (name: string): any => {
return require(path.join(__dirname, 'artifacts', `${name}.json`))
}

export const getContractInterface = (name: string): Interface => {
const definition = getContractDefinition(name)
return new ethers.utils.Interface(definition.abi)
}
export * from './src'
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"scripts": {
"all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint",
"test": "yarn run test:contracts",
"test:contracts": "buidler test --show-stack-traces",
"test:contracts": "buidler test \"test/deployment/deployment.spec.ts\" --show-stack-traces",
"coverage": "yarn run coverage:contracts",
"coverage:contracts": "buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"",
"build": "yarn run build:contracts && yarn run build:typescript",
Expand Down
21 changes: 21 additions & 0 deletions packages/contracts/src/contract-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as path from 'path'
import { ethers, ContractFactory, Signer } from 'ethers'
import { Interface } from 'ethers/lib/utils'

export const getContractDefinition = (name: string): any => {
return require(path.join(__dirname, '../artifacts', `${name}.json`))
}

export const getContractInterface = (name: string): Interface => {
const definition = getContractDefinition(name)
return new ethers.utils.Interface(definition.abi)
}

export const getContractFactory = (
name: string,
signer?: Signer
): ContractFactory => {
const definition = getContractDefinition(name)
const contractInterface = getContractInterface(name)
return new ContractFactory(contractInterface, definition.bytecode, signer)
}
86 changes: 86 additions & 0 deletions packages/contracts/src/deployment/contract-deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* External Imports */
import { Contract } from 'ethers'

/* Internal Imports */
import { getContractFactory } from '../contract-imports'
import { mergeDefaultConfig } from './default-config'
import {
ContractDeployOptions,
RollupDeployConfig,
factoryToContractName,
AddressResolverMapping,
} from './types'

/**
* Deploys a single contract.
* @param config Contract deployment configuration.
* @return Deployed contract.
*/
const deployContract = async (
config: ContractDeployOptions
): Promise<Contract> => {
config.factory = config.factory.connect(config.signer)
const deployedContract = await config.factory.deploy(...config.params)
return deployedContract
}

/**
* Deploys a contract and registers it with the address resolver.
* @param addressResolver Address resolver to register to.
* @param signer Wallet to deploy the contract from.
* @param name Name of the contract within the resolver.
* @param deployConfig Contract deployment configuration.
* @returns Ethers Contract instance.
*/
export const deployAndRegister = async (
addressResolver: Contract,
name: string,
deployConfig: ContractDeployOptions
): Promise<Contract> => {
const deployedContract = await deployContract(deployConfig)
await addressResolver.setAddress(name, deployedContract.address)
return deployedContract
}

/**
* Deploys all contracts according to a config.
* @param config Contract deployment config.
* @return AddressResolver and all other contracts.
*/
export const deployAllContracts = async (
config: RollupDeployConfig
): Promise<AddressResolverMapping> => {
if (!config.addressResolverConfig) {
config.addressResolverConfig = {
factory: getContractFactory('AddressResolver'),
params: [],
signer: config.signer,
}
}

const addressResolver = await deployContract(config.addressResolverConfig)

const deployConfig = await mergeDefaultConfig(
config.contractDeployConfig,
addressResolver,
config.signer,
config.rollupOptions
)

const contracts: any = {}
for (const name of Object.keys(deployConfig)) {
if (!config.dependencies || config.dependencies.includes(name as any)) {
const contractName = factoryToContractName[name]
contracts[contractName] = await deployAndRegister(
addressResolver,
name,
deployConfig[name]
)
}
}

return {
addressResolver,
contracts,
}
}
117 changes: 117 additions & 0 deletions packages/contracts/src/deployment/default-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* External Imports */
import { Contract, Signer } from 'ethers'

/* Internal Imports */
import { getContractFactory } from '../contract-imports'
import { ContractDeployConfig, RollupOptions } from './types'

/**
* Generates the default deployment configuration. Runs as an async function
* because we need to get the contract factories async via buidler.
* @param addressResolver Address resolver contract to connect to.
* @returns Default address resolver deployment configuration.
*/
export const getDefaultContractDeployConfig = async (
addressResolver: Contract,
wallet: Signer,
options: RollupOptions
): Promise<ContractDeployConfig> => {
return {
L1ToL2TransactionQueue: {
factory: getContractFactory('L1ToL2TransactionQueue'),
params: [
addressResolver.address,
await options.l1ToL2TransactionPasser.getAddress(),
],
signer: wallet,
},
SafetyTransactionQueue: {
factory: getContractFactory('SafetyTransactionQueue'),
params: [addressResolver.address],
signer: wallet,
},
CanonicalTransactionChain: {
factory: getContractFactory('CanonicalTransactionChain'),
params: [
addressResolver.address,
await options.sequencer.getAddress(),
await options.l1ToL2TransactionPasser.getAddress(),
options.forceInclusionPeriod,
],
signer: wallet,
},
StateCommitmentChain: {
factory: getContractFactory('StateCommitmentChain'),
params: [addressResolver.address],
signer: wallet,
},
StateManager: {
factory: getContractFactory('FullStateManager'),
params: [],
signer: wallet,
},
ExecutionManager: {
factory: getContractFactory('ExecutionManager'),
params: [
addressResolver.address,
await options.owner.getAddress(),
options.gasLimit,
],
signer: wallet,
},
SafetyChecker: {
factory: getContractFactory('StubSafetyChecker'),
params: [],
signer: wallet,
},
FraudVerifier: {
factory: getContractFactory('FraudVerifier'),
params: [addressResolver.address, true],
signer: wallet,
},
ContractAddressGenerator: {
factory: getContractFactory('ContractAddressGenerator'),
params: [],
signer: wallet,
},
EthMerkleTrie: {
factory: getContractFactory('EthMerkleTrie'),
params: [],
signer: wallet,
},
RLPEncode: {
factory: getContractFactory('RLPEncode'),
params: [],
signer: wallet,
},
RollupMerkleUtils: {
factory: getContractFactory('RollupMerkleUtils'),
params: [],
signer: wallet,
},
}
}

/**
* Merges the given config with the default config.
* @param config Config to merge with default.
* @param addressResolver AddressResolver contract reference.
* @param signer Signer to use to deploy contracts.
* @param options Rollup chain options.
*/
export const mergeDefaultConfig = async (
config: Partial<ContractDeployConfig>,
addressResolver: Contract,
signer?: Signer,
options?: RollupOptions
): Promise<ContractDeployConfig> => {
const defaultConfig = await getDefaultContractDeployConfig(
addressResolver,
signer,
options
)
return {
...defaultConfig,
...config,
}
}
1 change: 1 addition & 0 deletions packages/contracts/src/deployment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './contract-deploy'
88 changes: 88 additions & 0 deletions packages/contracts/src/deployment/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* External Imports */
import { Contract, ContractFactory, Signer } from 'ethers'

export interface ContractDeployOptions {
factory: ContractFactory
params: any[]
signer: Signer
}

export interface RollupOptions {
gasLimit: number
forceInclusionPeriod: number
owner: Signer
sequencer: Signer
l1ToL2TransactionPasser: Signer
}

export type ContractFactoryName =
| 'L1ToL2TransactionQueue'
| 'SafetyTransactionQueue'
| 'CanonicalTransactionChain'
| 'StateCommitmentChain'
| 'StateManager'
| 'ExecutionManager'
| 'SafetyChecker'
| 'FraudVerifier'
| 'ContractAddressGenerator'
| 'EthMerkleTrie'
| 'RLPEncode'
| 'RollupMerkleUtils'

export interface ContractDeployConfig {
L1ToL2TransactionQueue: ContractDeployOptions
SafetyTransactionQueue: ContractDeployOptions
CanonicalTransactionChain: ContractDeployOptions
StateCommitmentChain: ContractDeployOptions
StateManager: ContractDeployOptions
ExecutionManager: ContractDeployOptions
SafetyChecker: ContractDeployOptions
FraudVerifier: ContractDeployOptions
ContractAddressGenerator: ContractDeployOptions
EthMerkleTrie: ContractDeployOptions
RLPEncode: ContractDeployOptions
RollupMerkleUtils: ContractDeployOptions
}

interface ContractMapping {
l1ToL2TransactionQueue: Contract
safetyTransactionQueue: Contract
canonicalTransactionChain: Contract
stateCommitmentChain: Contract
stateManager: Contract
executionManager: Contract
safetyChecker: Contract
fraudVerifier: Contract
contractAddressGenerator: Contract
ethMerkleTrie: Contract
rlpEncode: Contract
rollupMerkleUtils: Contract
}

export interface AddressResolverMapping {
addressResolver: Contract
contracts: ContractMapping
}

export const factoryToContractName = {
L1ToL2TransactionQueue: 'l1ToL2TransactionQueue',
SafetyTransactionQueue: 'safetyTransactionQueue',
CanonicalTransactionChain: 'canonicalTransactionChain',
StateCommitmentChain: 'stateCommitmentChain',
StateManager: 'stateManager',
ExecutionManager: 'executionManager',
SafetyChecker: 'safetyChecker',
FraudVerifier: 'fraudVerifier',
ContractAddressGenerator: 'contractAddressGenerator',
EthMerkleTrie: 'ethMerkleTrie',
RLPEncode: 'rlpEncode',
RollupMerkleUtils: 'rollupMerkleUtils',
}

export interface RollupDeployConfig {
signer: Signer
rollupOptions: RollupOptions
addressResolverConfig?: ContractDeployOptions
contractDeployConfig?: Partial<ContractDeployConfig>
dependencies?: ContractFactoryName[]
}
2 changes: 2 additions & 0 deletions packages/contracts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './deployment'
export * from './contract-imports'
45 changes: 45 additions & 0 deletions packages/contracts/test/deployment/deployment.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from '../setup'

/* External Imports */
import { ethers } from '@nomiclabs/buidler'

/* Internal Imports */
import { deployAllContracts } from '../../src'
import {
RollupDeployConfig,
factoryToContractName,
} from '../../src/deployment/types'
import { Signer } from 'ethers'
import { GAS_LIMIT, DEFAULT_FORCE_INCLUSION_PERIOD } from '../test-helpers'

describe('Contract Deployment', () => {
let wallet: Signer
let sequencer: Signer
let l1ToL2TransactionPasser: Signer
before(async () => {
;[wallet, sequencer, l1ToL2TransactionPasser] = await ethers.getSigners()
})

describe('deployAllContracts', () => {
it('should deploy contracts in a default configuration', async () => {
const config: RollupDeployConfig = {
signer: wallet,
rollupOptions: {
gasLimit: GAS_LIMIT,
forceInclusionPeriod: DEFAULT_FORCE_INCLUSION_PERIOD,
owner: wallet,
sequencer,
l1ToL2TransactionPasser,
},
}

const resolver = await deployAllContracts(config)

expect(
Object.values(factoryToContractName).every((contractName) => {
return contractName in resolver.contracts
})
).to.be.true
})
})
})