Skip to content

Commit

Permalink
fix(core): keep previous deployment artifacts
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goldman committed Feb 29, 2024
1 parent 1c74fa8 commit e8b2c20
Show file tree
Hide file tree
Showing 10 changed files with 790 additions and 205 deletions.
6 changes: 6 additions & 0 deletions .changeset/rich-turkeys-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sphinx-labs/plugins': patch
'@sphinx-labs/core': patch
---

Keep previous deployment artifacts
84 changes: 40 additions & 44 deletions packages/core/src/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
DeploymentConfig,
ConfigArtifacts,
NetworkConfig,
BuildInfos,
} from './config/types'
import {
fetchNetworkConfigFromDeploymentConfig,
Expand All @@ -32,7 +33,7 @@ import {
} from './utils'
import { ExecutionMode } from './constants'

type NetworkArtifacts = {
export type NetworkArtifacts = {
executionArtifacts: {
[txArtifactFileName: string]: ExecutionArtifact
}
Expand Down Expand Up @@ -268,23 +269,24 @@ export const convertEthersTransactionReceipt = (
}

/**
* Makes contract deployment artifacts on a single network for a single deployment.
* Makes contract deployment artifacts on a single network for a single deployment. Mutates the
* input `artifacts` object.
*
* @param previousArtifacts An object containing all previous contract deployment artifacts on the
* network for the project.
* @param artifacts An object containing all previous contract deployment artifacts on the network
* for the project.
*/
const makeContractDeploymentArtifacts = async (
export const makeContractDeploymentArtifacts = async (
merkleRoot: string,
networkConfig: NetworkConfig,
deploymentConfig: DeploymentConfig,
buildInfos: BuildInfos,
receipts: Array<SphinxTransactionReceipt>,
configArtifacts: ConfigArtifacts,
previousArtifacts: {
artifacts: {
[fileName: string]: ContractDeploymentArtifact | undefined
},
provider: SphinxJsonRpcProvider
): Promise<{ [fileName: string]: ContractDeploymentArtifact }> => {
const isSuffixed = Object.keys(previousArtifacts).every((fileName) =>
): Promise<void> => {
const isSuffixed = Object.keys(artifacts).every((fileName) =>
fileName.endsWith('.json')
)
if (!isSuffixed) {
Expand All @@ -297,14 +299,13 @@ const makeContractDeploymentArtifacts = async (
const moduleInterface = new ethers.Interface(SphinxModuleABI)

const { gitCommit, chainId } = networkConfig
const artifacts: { [fileName: string]: ContractDeploymentArtifact } = {}
const numDeployments: { [fileName: string]: number | undefined } = {}
for (const action of networkConfig.actionInputs) {
for (const contract of action.contracts) {
const { fullyQualifiedName, initCodeWithArgs, address } = contract
const { artifact: compilerArtifact, buildInfoId } =
configArtifacts[fullyQualifiedName]
const buildInfo = deploymentConfig.buildInfos[buildInfoId]
const buildInfo = buildInfos[buildInfoId]

if (!compilerArtifact || !buildInfo) {
throw new Error(`Could not find artifact for: ${fullyQualifiedName}`)
Expand Down Expand Up @@ -393,7 +394,7 @@ const makeContractDeploymentArtifacts = async (
? `${contractName}_${previousNumDeployments}.json`
: `${contractName}.json`

const previousArtifact = previousArtifacts[fileName]
const previousArtifact = artifacts[fileName]
if (previousArtifact) {
// Separate the previous artifact into two components: its `history` array and the other
// fields.
Expand All @@ -416,8 +417,6 @@ const makeContractDeploymentArtifacts = async (
numDeployments[contractName] = previousNumDeployments + 1
}
}

return artifacts
}

export const writeDeploymentArtifacts = (
Expand Down Expand Up @@ -528,12 +527,11 @@ export const isContractDeploymentArtifact = (
}

/**
* Make deployment artifacts for the most recent deployment in a project.
* Make deployment artifacts for the most recent deployment in a project. Mutates the input
* `artifacts` object.
*
* @param deployments An object containing deployment information for each network where the most
* recent deployment was executed. The `previousContractArtifacts` field contains all previous
* contract deployment artifacts on the network for the project. Note that the `fileName` keys of
* the `previousContractArtifacts` object must be suffixed with `.json`.
* recent deployment was executed.
*
* @returns {DeploymentArtifacts} The artifacts for the most recent deployment.
*/
Expand All @@ -543,23 +541,28 @@ export const makeDeploymentArtifacts = async (
deploymentConfig: DeploymentConfig
receipts: Array<SphinxTransactionReceipt>
provider: SphinxJsonRpcProvider
previousContractArtifacts: {
[fileName: string]: ContractDeploymentArtifact
}
}
},
merkleRoot: string,
configArtifacts: ConfigArtifacts
): Promise<DeploymentArtifacts> => {
const allNetworkArtifacts: DeploymentArtifacts['networks'] = {}
const compilerInputArtifacts: DeploymentArtifacts['compilerInputs'] = {}
for (const chainId of Object.keys(deployments)) {
const { provider, deploymentConfig, receipts, previousContractArtifacts } =
deployments[chainId]
configArtifacts: ConfigArtifacts,
artifacts: DeploymentArtifacts
): Promise<void> => {
// We'll mutate these variables to update the existing artifacts.
const {
networks: allNetworkArtifacts,
compilerInputs: compilerInputArtifacts,
} = artifacts

const networkArtifacts: NetworkArtifacts = {
contractDeploymentArtifacts: {},
executionArtifacts: {},
for (const chainId of Object.keys(deployments)) {
const { provider, deploymentConfig, receipts } = deployments[chainId]

// Define the network artifacts if it doesn't exist. Otherwise, we'll attempt to operate on an
// object that doesn't exist, leading to an error.
if (allNetworkArtifacts[chainId] === undefined) {
allNetworkArtifacts[chainId] = {
contractDeploymentArtifacts: {},
executionArtifacts: {},
}
}

const networkConfig = fetchNetworkConfigFromDeploymentConfig(
Expand All @@ -568,16 +571,15 @@ export const makeDeploymentArtifacts = async (
)

// Make the contract artifacts.
const contractArtifacts = await makeContractDeploymentArtifacts(
await makeContractDeploymentArtifacts(
merkleRoot,
networkConfig,
deploymentConfig,
deploymentConfig.buildInfos,
receipts,
configArtifacts,
previousContractArtifacts,
allNetworkArtifacts[chainId].contractDeploymentArtifacts,
provider
)
networkArtifacts.contractDeploymentArtifacts = contractArtifacts

// Make the execution artifact.
const executionArtifact = await makeExecutionArtifact(
Expand All @@ -587,21 +589,15 @@ export const makeDeploymentArtifacts = async (
merkleRoot,
provider
)
networkArtifacts.executionArtifacts[`${remove0x(merkleRoot)}.json`] =
executionArtifact

allNetworkArtifacts[chainId] = networkArtifacts
allNetworkArtifacts[chainId].executionArtifacts[
`${remove0x(merkleRoot)}.json`
] = executionArtifact

// Make the compiler input artifacts.
for (const compilerInput of deploymentConfig.inputs) {
compilerInputArtifacts[`${compilerInput.id}.json`] = compilerInput
}
}

return {
networks: allNetworkArtifacts,
compilerInputs: compilerInputArtifacts,
}
}

const makeExecutionArtifact = async (
Expand Down
62 changes: 62 additions & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as fs from 'fs'
import { promisify } from 'util'
import { exec, spawn } from 'child_process'
import { join } from 'path'
import { existsSync } from 'fs'

import yesno from 'yesno'
import axios from 'axios'
Expand Down Expand Up @@ -60,6 +62,11 @@ import {
shouldUseHigherMaxGasLimit,
} from './networks'
import { RelayProposal, StoreDeploymentConfig } from './types'
import {
NetworkArtifacts,
isContractDeploymentArtifact,
isExecutionArtifact,
} from './artifacts'

export const sphinxLog = (
logLevel: 'warning' | 'error' = 'warning',
Expand Down Expand Up @@ -1569,3 +1576,58 @@ export const fetchNetworkConfigFromDeploymentConfig = (

return networkConfig
}

const isDirectory = (path: string): boolean =>
existsSync(path) && fs.statSync(path).isDirectory()

export const readDeploymentArtifactsForNetwork = (
projectName: string,
chainId: BigInt,
executionMode: ExecutionMode
): NetworkArtifacts => {
const networkArtifacts: NetworkArtifacts = {
contractDeploymentArtifacts: {},
executionArtifacts: {},
}

const networkArtifactDirPath = join(
`deployments`,
projectName,
getNetworkNameDirectory(chainId.toString(), executionMode)
)

if (!isDirectory(networkArtifactDirPath)) {
return networkArtifacts
}

const contractArtifactFileNames = fs
.readdirSync(networkArtifactDirPath)
.filter((fileName) => fileName.endsWith('.json'))
for (const fileName of contractArtifactFileNames) {
const filePath = join(networkArtifactDirPath, fileName)
const artifact = JSON.parse(fs.readFileSync(filePath, 'utf8'))
if (isContractDeploymentArtifact(artifact)) {
networkArtifacts.contractDeploymentArtifacts[fileName] = artifact
}
}

const executionArtifactFilePath = join(networkArtifactDirPath, `execution`)

if (!isDirectory(executionArtifactFilePath)) {
return networkArtifacts
}

const executionArtifactFileNames = fs
.readdirSync(executionArtifactFilePath)
.filter((fileName) => fileName.endsWith('.json'))

for (const fileName of executionArtifactFileNames) {
const filePath = join(executionArtifactFilePath, fileName)
const artifact = JSON.parse(fs.readFileSync(filePath, 'utf8'))
if (isExecutionArtifact(artifact)) {
networkArtifacts.executionArtifacts[fileName] = artifact
}
}

return networkArtifacts
}
45 changes: 19 additions & 26 deletions packages/plugins/src/cli/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { join, relative } from 'path'
import { existsSync, readFileSync, readdirSync, unlinkSync } from 'fs'
import { existsSync, readFileSync, unlinkSync } from 'fs'

import {
displayDeploymentTable,
fundAccountMaxBalance,
getNetworkNameDirectory,
getSphinxWalletPrivateKey,
isFile,
readDeploymentArtifactsForNetwork,
signMerkleRoot,
spawnAsync,
} from '@sphinx-labs/core/dist/utils'
Expand All @@ -17,8 +17,6 @@ import {
SphinxPreview,
makeDeploymentData,
makeDeploymentArtifacts,
ContractDeploymentArtifact,
isContractDeploymentArtifact,
DeploymentConfig,
makeDeploymentConfig,
verifyDeploymentWithRetries,
Expand Down Expand Up @@ -400,40 +398,35 @@ export const deploy = async (

const { projectName } = networkConfig.newConfig

// Get the existing contract deployment artifacts
const contractArtifactDirPath = join(
`deployments`,
// Get the existing contract deployment artifacts and execution artifacts for the current network.
// This object will potentially be modified when we make the new deployment artifacts.
// Specifically, the `history` field of the contract deployment artifacts could be modified. Even
// though we don't currently modify the execution artifacts, we include them anyways in case we
// add logic in the future that modifies them. We don't include the compiler input artifacts
// mainly as a performance optimization and because we don't expect to modify them in the future.
const networkArtifacts = readDeploymentArtifactsForNetwork(
projectName,
getNetworkNameDirectory(chainId.toString(), networkConfig.executionMode)
chainId,
executionMode
)
const artifactFileNames = existsSync(contractArtifactDirPath)
? readdirSync(contractArtifactDirPath)
: []
const previousContractArtifacts: {
[fileName: string]: ContractDeploymentArtifact
} = {}
for (const fileName of artifactFileNames) {
if (fileName.endsWith('.json')) {
const filePath = join(contractArtifactDirPath, fileName)
const fileContent = readFileSync(filePath, 'utf8')
const artifact = JSON.parse(fileContent)
if (isContractDeploymentArtifact(artifact)) {
previousContractArtifacts[fileName] = artifact
}
}
const deploymentArtifacts = {
networks: {
[chainId.toString()]: networkArtifacts,
},
compilerInputs: {},
}

const deploymentArtifacts = await makeDeploymentArtifacts(
await makeDeploymentArtifacts(
{
[chainId.toString()]: {
provider,
deploymentConfig,
receipts,
previousContractArtifacts,
},
},
merkleTree.root,
configArtifacts
configArtifacts,
deploymentArtifacts
)

spinner.succeed(`Built deployment artifacts.`)
Expand Down
Loading

0 comments on commit e8b2c20

Please sign in to comment.