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

Fix deploy of protocol from scratch #273

Merged
merged 6 commits into from
Jul 12, 2024
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types'
import { TokenSymbol } from '@eonian/upgradeable'
import { TokenSymbol, sendTxWithRetry } from '@eonian/upgradeable'
import { type ApeLendingStrategy } from '../../typechain-types'
import { type DeployResult, DeployStatus } from '@eonian/upgradeable'
import { Addresses } from './addresses'
Expand Down Expand Up @@ -29,9 +29,12 @@ export default async function deployApeLendingStrategy(token: TokenSymbol, hre:
}

async function attachToVault(strategyAddress: string, vaultAddress: string, hre: HardhatRuntimeEnvironment) {
console.log(`Attaching strategy to vault:\nStrategy:${strategyAddress}\nVault:${vaultAddress}`)

const vault = await hre.ethers.getContractAt('Vault', vaultAddress)
const tx = await vault.addStrategy(strategyAddress, 10000) // Allocate 100%
await tx.wait()
await sendTxWithRetry(() => vault.addStrategy(strategyAddress, 10000)) // Allocate 100%

console.log(`Strategy attached successfully:\nStrategy:${strategyAddress}\nVault:${vaultAddress}`)
}

async function getAddresses(token: TokenSymbol, hre: HardhatRuntimeEnvironment) {
Expand Down
9 changes: 6 additions & 3 deletions packages/contracts/hardhat/deployment/deployVFT.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types'
import { type DeployResult, type TokenSymbol, DeployStatus } from '@eonian/upgradeable'
import { type DeployResult, type TokenSymbol, DeployStatus, sendTxWithRetry } from '@eonian/upgradeable'
import type { VaultFounderToken } from '../../typechain-types'
import { Addresses } from './addresses'

Expand All @@ -24,9 +24,12 @@ export default async function deployVFT(token: TokenSymbol, hre: HardhatRuntimeE
}

async function attachToVault(vftAddress: string, vaultAddress: string, hre: HardhatRuntimeEnvironment) {
console.log(`Attaching Vault Founder Token to vault:\nVFT:${vftAddress}\nVault:${vaultAddress}`)

const vault = await hre.ethers.getContractAt('Vault', vaultAddress)
const tx = await vault.setFounders(vftAddress)
await tx.wait()
await sendTxWithRetry(() => vault.setFounders(vftAddress))

console.log(`VFT attached successfully:\nVFT:${vftAddress}\nVault:${vaultAddress}`)
}

async function getAddreses(token: TokenSymbol, hre: HardhatRuntimeEnvironment) {
Expand Down
2 changes: 1 addition & 1 deletion packages/upgradeable/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export * from './availableNetworks'
export * from './addresses/BaseAddresses'
export * from './execute'


export * from './sendTxWithRetry'
31 changes: 23 additions & 8 deletions packages/upgradeable/src/plugins/Deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,32 @@ export class Deployer {
await this.executePreDeployHook()

const proxyAddress = await this.getOrCreateProxy()
this.log(`Proxy retrived: ${proxyAddress}`)
const oldImplAddress = await this.getImplementation(proxyAddress)
this.log(`Existing implemntation retrived: ${oldImplAddress}`)
const newImplAddress = await this.deployImplementationIfNeeded(proxyAddress)
this.log(`New implementation retrived: ${newImplAddress}`)

const proxyImplementationAddress = await this.getImplementation(proxyAddress)
const implementationAddress = await this.deployImplementationIfNeeded(proxyAddress)

const sameImplementationCode = await this.haveSameBytecode(proxyImplementationAddress, implementationAddress)
const sameImplementationCode = await this.haveSameBytecode(oldImplAddress, newImplAddress)
if (!sameImplementationCode) {
this.log(`Implementation changed: ${implementationAddress} (new) != ${proxyImplementationAddress} (old)!`)
this.log(`Implementation changed:\n new:${newImplAddress}\nold:${oldImplAddress}`)
await this.upgradeProxy(proxyAddress)
} else {
this.log('Implementation has not been changed...')
}

const verified = await this.hre.etherscanVerifier.verifyIfNeeded(proxyAddress, this.upgradeOptions.constructorArgs)
if (verified) {
this.log(`Contract ${proxyAddress} have been verified on etherscan!`)
}

return (this.hre.lastDeployments[proxyAddress] = {
proxyAddress,
implementationAddress,
implementationAddress: newImplAddress,
contractName: this.contractName,
deploymentId: this.deploymentId,
status: this.deployStatus,
verified: await this.hre.etherscanVerifier.verifyIfNeeded(proxyAddress, this.upgradeOptions.constructorArgs),
verified,
})
}

Expand Down Expand Up @@ -103,10 +112,12 @@ export class Deployer {
private async deployProxy(): Promise<string> {
this.log('Starting proxy deployment...')
const contract = await this.hre.upgrades.deployProxy(await this.getContractFactory(), this.initArgs, this.upgradeOptions)
await contract.waitForDeployment()
const address = await contract.getAddress()

this.log(`Saving proxy address "${address}" to the deployment data file...`)
this.log(`Succesfully deployed proxy to "${address}"`)
await this.hre.proxyRegister.saveProxy(this.contractName, this.deploymentId, address)
this.log(`Proxy with address "${address}" saved to the deployment data file...`)

this.changeDeployStatus(DeployStatus.DEPLOYED)

Expand All @@ -122,6 +133,7 @@ export class Deployer {
await this.hre.upgrades.upgradeProxy(proxyAddress, await this.getContractFactory(), this.upgradeOptions)

this.changeDeployStatus(DeployStatus.UPGRADED)
this.log(`Proxy "${proxyAddress}" has been upgraded...`)
}

/**
Expand All @@ -131,6 +143,7 @@ export class Deployer {
* @returns Implementation address. Returns the current implementation of the proxy if no deployment has been made.
*/
private async deployImplementationIfNeeded(proxyAddress: string): Promise<string> {
this.log('Checking and deploy new implementation if needed...')
const contractFactory = await this.hre.ethers.getContractFactory(this.contractName)
const response = await this.hre.upgrades.prepareUpgrade(proxyAddress, contractFactory, this.upgradeOptions)
if (typeof response !== 'string') {
Expand Down Expand Up @@ -178,6 +191,7 @@ export class Deployer {
}

private async haveSameBytecode(implementationA: string, implementationB: string): Promise<boolean> {
this.log(`Checking if implementation has been changed...\nnew:${implementationA}\nold:${implementationB}`)
if (implementationA === implementationB) {
return true
}
Expand All @@ -189,4 +203,5 @@ export class Deployer {
return addresses.includes(implementationA) && addresses.includes(implementationB)
})
}

}
19 changes: 14 additions & 5 deletions packages/upgradeable/src/plugins/EtherscanVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import { Etherscan } from '@nomicfoundation/hardhat-verify/etherscan'
import type { HardhatRuntimeEnvironment } from 'hardhat/types'
import debug from 'debug'
import { NetworkEnvironment, resolveNetworkEnvironment } from '../environment/NetworkEnvironment'
import { timeout } from '../sendTxWithRetry'

export class EtherscanVerifier {
private log: debug.Debugger = debug(EtherscanVerifier.name)

constructor(private hre: HardhatRuntimeEnvironment) {}
constructor(
private hre: HardhatRuntimeEnvironment,
private safetyDelay: number = 5000
) {}

public async verifyIfNeeded(proxyAddress: string, constructorArgs?: unknown[]): Promise<boolean> {
this.log(`Will verify proxy if need for address: "${proxyAddress}" on etherscan...`)
const networkEnvironment = resolveNetworkEnvironment(this.hre)
if (networkEnvironment === NetworkEnvironment.LOCAL) {
this.log(`Verification is disabled on "${networkEnvironment}" environment!`)
Expand All @@ -21,20 +26,24 @@ export class EtherscanVerifier {
return false
}

this.log('Starting to verify deployed (or upgraded) contracts...')
this.log(`Starting to verify deployed (or upgraded) contracts: ${proxyAddress}`)
let message = ''
try {
this.log(`Will wait for ${this.safetyDelay}ms in case contact is not yet available for etherscan`)
await timeout(this.safetyDelay)
message = await this.interceptOutput(async () => {
await this.hre.run('verify:verify', {
address: proxyAddress,
constructorArguments: constructorArgs,
})
})
await new Promise(resolve => setTimeout(resolve, 5000))
}
catch (e) {

this.log(`Will wait fro ${this.safetyDelay}ms till verification is tracked by etherscan!`)
await timeout(this.safetyDelay)
} catch (e) {
console.error(`Error during proxy verification: ${e instanceof Error ? e.message : String(e)}`)
}

const success = await this.isContractVerified(proxyAddress)
if (!success) {
console.log('Verification was not successful!', message)
Expand Down
38 changes: 38 additions & 0 deletions packages/upgradeable/src/sendTxWithRetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ContractTransactionReceipt, ContractTransactionResponse } from "ethers";

export interface TransactionResult {
tx: ContractTransactionResponse,
receipt: null | ContractTransactionReceipt
}

/**
* Will execute a transaction with retries in case of failure.
*/
export const sendTxWithRetry = async (
txProducer: () => Promise<ContractTransactionResponse>,
retries: number = 5,
delay: number = 5000
): Promise<TransactionResult> => {
let error: any;

for (let i = 0; i < retries; i++) {
try {
const tx = await txProducer();
const receipt = await tx.wait();

return { tx: tx, receipt: receipt };
} catch (e) {
error = e;
console.warn(`Failed to send transaction with error`, e);
if(i + 1 < retries) {
console.log(`Will retry after ${delay}ms...`);
await timeout(delay)
}
}
}

console.error(`Failed to send transaction after ${retries} retries`);
throw error;
};

export const timeout = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
Loading