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

Save contract deployment data to deployments folder #2

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# Web3 Monorepo Template

This is an opinionated monorepo template for web3 projects. It uses Hardhat for [smart contracts](./apps/contracts/README.md) and Next.js for the [web app](./apps/web/README.md).

## Contract Deployments

When contracts are deployed, their deployment data (address, ABI, constructor arguments) is automatically saved to the `apps/contracts/deployments/{chainId}/` directory. This data can be accessed from the web app using the `getDeployment` helper function:

```typescript
import { getDeployment } from '@/lib/getDeployment'

// Get deployment data for a contract on a specific chain
const { address, abi } = getDeployment(chainId, 'Contract')
```
2 changes: 1 addition & 1 deletion apps/contracts/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contracts

Hardhat project for building smart contracts. The deploy script uses CREATE2 to easily mine a vanity address.
Hardhat project for building smart contracts. The deploy script uses CREATE2 to easily mine a vanity address and automatically saves deployment data (address, ABI, constructor arguments) to the `deployments/{chainId}/` directory.

## Local Development

Expand Down
8 changes: 7 additions & 1 deletion apps/contracts/scripts/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import hre from 'hardhat'
import { encodeAbiParameters } from 'viem/utils'
import { Contract } from 'ethers'

import { generateSaltAndDeploy } from './lib/create2'
import { saveDeployment } from './lib/saveDeployment'

async function main() {
const contractName = 'Contract'
Expand All @@ -15,7 +17,7 @@ async function main() {
constructorArguments
)

const { address } = await generateSaltAndDeploy({
const { address, contract } = await generateSaltAndDeploy({
vanity: '0x000',
encodedArgs,
contractName,
Expand All @@ -25,6 +27,10 @@ async function main() {

console.log(`Deployed ${contractName} to ${address}`)

// Save deployment data
const chainId = (await hre.ethers.provider.getNetwork()).chainId
await saveDeployment(contract as Contract, Number(chainId), constructorArguments)

try {
// Wait 30 seconds for block explorers to index the deployment
await new Promise((resolve) => setTimeout(resolve, 30_000))
Expand Down
6 changes: 5 additions & 1 deletion apps/contracts/scripts/lib/create2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,9 @@ export async function generateSaltAndDeploy({

await publicClient.waitForTransactionReceipt({ hash: deployTx })

return { salt, address: expectedAddress }
// Get the contract instance
const Contract = await hre.ethers.getContractFactory(contractName)
const contract = Contract.attach(expectedAddress)

return { salt, address: expectedAddress, contract }
}
41 changes: 41 additions & 0 deletions apps/contracts/scripts/lib/saveDeployment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import fs from 'fs'
import path from 'path'
import { Contract } from 'ethers'

export interface DeploymentData {
address: string
abi: any
constructorArguments?: any[]
chainId: number
deploymentDate: string
}

export async function saveDeployment(
contract: Contract,
chainId: number,
constructorArguments?: any[]
) {
const deploymentData: DeploymentData = {
address: await contract.getAddress(),
abi: contract.interface.format(),
constructorArguments,
chainId,
deploymentDate: new Date().toISOString(),
}

const deploymentsDir = path.join(__dirname, '../../deployments')
if (!fs.existsSync(deploymentsDir)) {
fs.mkdirSync(deploymentsDir, { recursive: true })
}

const chainDir = path.join(deploymentsDir, chainId.toString())
if (!fs.existsSync(chainDir)) {
fs.mkdirSync(chainDir, { recursive: true })
}

const contractName = await contract.name?.() || 'Contract'
const filePath = path.join(chainDir, `${contractName}.json`)

fs.writeFileSync(filePath, JSON.stringify(deploymentData, null, 2))
console.log(`Deployment data saved to ${filePath}`)
}
21 changes: 21 additions & 0 deletions apps/web/src/lib/getDeployment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import fs from 'fs'
import path from 'path'

export interface DeploymentData {
address: string
abi: any
constructorArguments?: any[]
chainId: number
deploymentDate: string
}

export function getDeployment(chainId: number, contractName: string): DeploymentData {
const deploymentsPath = path.join(process.cwd(), '../contracts/deployments', chainId.toString(), `${contractName}.json`)

if (!fs.existsSync(deploymentsPath)) {
throw new Error(`No deployment found for contract ${contractName} on chain ${chainId}`)
}

const deploymentData = JSON.parse(fs.readFileSync(deploymentsPath, 'utf-8'))
return deploymentData
}