hardhat#upgrades TypeScript Examples
The following examples show how to use
hardhat#upgrades.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: OzContractDeployer.ts From perpetual-protocol with GNU General Public License v3.0 | 6 votes |
async deploy(contractFullyQualifiedName: string, args: any[]): Promise<string> {
// deploy contract by open zeppelin upgrade plugin
const contract = await ethers.getContractFactory(contractFullyQualifiedName)
const proxyInstance = await upgrades.deployProxy(contract, args, {
initializer: this.ozInitMethodName,
})
await this.syncNonce(proxyInstance.deployTransaction.hash)
const impAddr = await getImplementation(proxyInstance.address)
console.log(
`deploy: contractFullyQualifiedName=${contractFullyQualifiedName}, proxy=${proxyInstance.address}, implementation=${impAddr}`,
)
await initImplementation(impAddr, contractFullyQualifiedName, this.confirmations, args)
return proxyInstance.address
}
Example #2
Source File: OzContractDeployer.ts From perpetual-protocol with GNU General Public License v3.0 | 6 votes |
// different from deploy() and upgrade(), this function returns the "implementation" address
async prepareUpgrade(proxy: string, contractFullyQualifiedName: string, args: any[]): Promise<string> {
const factory = await ethers.getContractFactory(contractFullyQualifiedName)
const impAddr = await upgrades.prepareUpgrade(proxy, factory, { unsafeAllowCustomTypes: false })
console.log(
`prepareUpgrade: contractFullyQualifiedName=${contractFullyQualifiedName}, proxy=${proxy}, implementation=${impAddr}`,
)
await initImplementation(impAddr, contractFullyQualifiedName, this.confirmations, args)
// proxyInstance.deployTransaction only exists in deployProxy() and does not exist in prepareUpgrade()
return impAddr
}
Example #3
Source File: OzContractDeployer.ts From perpetual-protocol with GNU General Public License v3.0 | 6 votes |
// only admin
async upgrade(proxy: string, contractFullyQualifiedName: string, args: any[]): Promise<void> {
const contract = await ethers.getContractFactory(contractFullyQualifiedName)
const proxyInstance = await upgrades.upgradeProxy(proxy, contract)
const impAddr = await getImplementation(proxyInstance.address)
console.log(
`upgrade: contractFullyQualifiedName=${contractFullyQualifiedName}, proxy=${proxy}, implementation=${impAddr}`,
)
await initImplementation(impAddr, contractFullyQualifiedName, this.confirmations, args)
// proxyInstance.deployTransaction only exists in deployProxy() and does not exist in upgradeProxy()
// await this.syncNonce(proxyInstance.deployTransaction.hash)
}
Example #4
Source File: OzContractDeployer.ts From perpetual-protocol with GNU General Public License v3.0 | 5 votes |
static async transferProxyAdminOwnership(newAdmin: string): Promise<void> {
// TODO this is a hack due to @openzeppelin/hardhat-upgrades doesn't expose "admin" in type-extensions.d.ts
await (upgrades as any).admin.transferProxyAdminOwnership(newAdmin)
}
Example #5
Source File: OzContractDeployer.ts From perpetual-protocol with GNU General Public License v3.0 | 5 votes |
async prepareUpgradeLegacy(proxy: string, contractFullyQualifiedName: string): Promise<string> {
const factory = await ethers.getContractFactory(contractFullyQualifiedName)
const impAddr = await upgrades.prepareUpgrade(proxy, factory, { unsafeAllowCustomTypes: false })
console.log(
`prepareUpgradeLegacy proxy=${proxy}, contractFullyQualifiedName=${contractFullyQualifiedName}, address=${impAddr}`,
)
return impAddr
}
Example #6
Source File: DeployUtil.ts From perpetual-protocol with GNU General Public License v3.0 | 5 votes |
export async function getImplementation(proxyAddr: string) {
const proxyAdmin = await upgrades.admin.getInstance()
// const proxyAdminAbi = ["function getProxyImplementation(address proxy) view returns (address)"]
// const proxyAdminAddr = (await upgrades.admin.getInstance()).address
// const proxyAdmin = await ethers.getContractAt(proxyAdminAbi, proxyAdminAddr)
return proxyAdmin.getProxyImplementation(proxyAddr)
}
Example #7
Source File: deploy-reward-vesting.ts From perpetual-protocol with GNU General Public License v3.0 | 5 votes |
async function main() {
const perpAddress = "0xaFfB148304D38947193785D194972a7d0d9b7F68"
const Vesting = await ethers.getContractFactory(ContractFullyQualifiedName.PerpRewardVesting)
const instance = await upgrades.deployProxy(Vesting, [perpAddress])
await instance.deployed()
}
Example #8
Source File: auction.test.ts From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
describe('NounsAuctionHouse', () => {
let nounsAuctionHouse: NounsAuctionHouse;
let nounsToken: NounsToken;
let weth: WETH;
let deployer: SignerWithAddress;
let noundersDAO: SignerWithAddress;
let bidderA: SignerWithAddress;
let bidderB: SignerWithAddress;
let snapshotId: number;
const TIME_BUFFER = 15 * 60;
const RESERVE_PRICE = 2;
const MIN_INCREMENT_BID_PERCENTAGE = 5;
const DURATION = 60 * 60 * 24;
async function deploy(deployer?: SignerWithAddress) {
const auctionHouseFactory = await ethers.getContractFactory('NounsAuctionHouse', deployer);
return upgrades.deployProxy(auctionHouseFactory, [
nounsToken.address,
weth.address,
TIME_BUFFER,
RESERVE_PRICE,
MIN_INCREMENT_BID_PERCENTAGE,
DURATION,
]) as Promise<NounsAuctionHouse>;
}
before(async () => {
[deployer, noundersDAO, bidderA, bidderB] = await ethers.getSigners();
nounsToken = await deployNounsToken(deployer, noundersDAO.address, deployer.address);
weth = await deployWeth(deployer);
nounsAuctionHouse = await deploy(deployer);
const descriptor = await nounsToken.descriptor();
await populateDescriptor(NounsDescriptorFactory.connect(descriptor, deployer));
await nounsToken.setMinter(nounsAuctionHouse.address);
});
beforeEach(async () => {
snapshotId = await ethers.provider.send('evm_snapshot', []);
});
afterEach(async () => {
await ethers.provider.send('evm_revert', [snapshotId]);
});
it('should revert if a second initialization is attempted', async () => {
const tx = nounsAuctionHouse.initialize(
nounsToken.address,
weth.address,
TIME_BUFFER,
RESERVE_PRICE,
MIN_INCREMENT_BID_PERCENTAGE,
DURATION,
);
await expect(tx).to.be.revertedWith('Initializable: contract is already initialized');
});
it('should allow the noundersDAO to unpause the contract and create the first auction', async () => {
const tx = await nounsAuctionHouse.unpause();
await tx.wait();
const auction = await nounsAuctionHouse.auction();
expect(auction.startTime.toNumber()).to.be.greaterThan(0);
});
it('should revert if a user creates a bid for an inactive auction', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
const tx = nounsAuctionHouse.connect(bidderA).createBid(nounId.add(1), {
value: RESERVE_PRICE,
});
await expect(tx).to.be.revertedWith('Noun not up for auction');
});
it('should revert if a user creates a bid for an expired auction', async () => {
await (await nounsAuctionHouse.unpause()).wait();
await ethers.provider.send('evm_increaseTime', [60 * 60 * 25]); // Add 25 hours
const { nounId } = await nounsAuctionHouse.auction();
const tx = nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
await expect(tx).to.be.revertedWith('Auction expired');
});
it('should revert if a user creates a bid with an amount below the reserve price', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
const tx = nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE - 1,
});
await expect(tx).to.be.revertedWith('Must send at least reservePrice');
});
it('should revert if a user creates a bid less than the min bid increment percentage', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE * 50,
});
const tx = nounsAuctionHouse.connect(bidderB).createBid(nounId, {
value: RESERVE_PRICE * 51,
});
await expect(tx).to.be.revertedWith(
'Must send more than last bid by minBidIncrementPercentage amount',
);
});
it('should refund the previous bidder when the following user creates a bid', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
const bidderAPostBidBalance = await bidderA.getBalance();
await nounsAuctionHouse.connect(bidderB).createBid(nounId, {
value: RESERVE_PRICE * 2,
});
const bidderAPostRefundBalance = await bidderA.getBalance();
expect(bidderAPostRefundBalance).to.equal(bidderAPostBidBalance.add(RESERVE_PRICE));
});
it('should cap the maximum bid griefing cost at 30K gas + the cost to wrap and transfer WETH', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
const maliciousBidderFactory = new MaliciousBidderFactory(bidderA);
const maliciousBidder = await maliciousBidderFactory.deploy();
const maliciousBid = await maliciousBidder
.connect(bidderA)
.bid(nounsAuctionHouse.address, nounId, {
value: RESERVE_PRICE,
});
await maliciousBid.wait();
const tx = await nounsAuctionHouse.connect(bidderB).createBid(nounId, {
value: RESERVE_PRICE * 2,
gasLimit: 1_000_000,
});
const result = await tx.wait();
expect(result.gasUsed.toNumber()).to.be.lessThan(200_000);
expect(await weth.balanceOf(maliciousBidder.address)).to.equal(RESERVE_PRICE);
});
it('should emit an `AuctionBid` event on a successful bid', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
const tx = nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
await expect(tx)
.to.emit(nounsAuctionHouse, 'AuctionBid')
.withArgs(nounId, bidderA.address, RESERVE_PRICE, false);
});
it('should emit an `AuctionExtended` event if the auction end time is within the time buffer', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId, endTime } = await nounsAuctionHouse.auction();
await ethers.provider.send('evm_setNextBlockTimestamp', [endTime.sub(60 * 5).toNumber()]); // Subtract 5 mins from current end time
const tx = nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
await expect(tx)
.to.emit(nounsAuctionHouse, 'AuctionExtended')
.withArgs(nounId, endTime.add(60 * 10));
});
it('should revert if auction settlement is attempted while the auction is still active', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
const tx = nounsAuctionHouse.connect(bidderA).settleCurrentAndCreateNewAuction();
await expect(tx).to.be.revertedWith("Auction hasn't completed");
});
it('should emit `AuctionSettled` and `AuctionCreated` events if all conditions are met', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
await ethers.provider.send('evm_increaseTime', [60 * 60 * 25]); // Add 25 hours
const tx = await nounsAuctionHouse.connect(bidderA).settleCurrentAndCreateNewAuction();
const receipt = await tx.wait();
const { timestamp } = await ethers.provider.getBlock(receipt.blockHash);
const settledEvent = receipt.events?.find(e => e.event === 'AuctionSettled');
const createdEvent = receipt.events?.find(e => e.event === 'AuctionCreated');
expect(settledEvent?.args?.nounId).to.equal(nounId);
expect(settledEvent?.args?.winner).to.equal(bidderA.address);
expect(settledEvent?.args?.amount).to.equal(RESERVE_PRICE);
expect(createdEvent?.args?.nounId).to.equal(nounId.add(1));
expect(createdEvent?.args?.startTime).to.equal(timestamp);
expect(createdEvent?.args?.endTime).to.equal(timestamp + DURATION);
});
it('should not create a new auction if the auction house is paused and unpaused while an auction is ongoing', async () => {
await (await nounsAuctionHouse.unpause()).wait();
await (await nounsAuctionHouse.pause()).wait();
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
expect(nounId).to.equal(1);
});
it('should create a new auction if the auction house is paused and unpaused after an auction is settled', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
await ethers.provider.send('evm_increaseTime', [60 * 60 * 25]); // Add 25 hours
await (await nounsAuctionHouse.pause()).wait();
const settleTx = nounsAuctionHouse.connect(bidderA).settleAuction();
await expect(settleTx)
.to.emit(nounsAuctionHouse, 'AuctionSettled')
.withArgs(nounId, bidderA.address, RESERVE_PRICE);
const unpauseTx = await nounsAuctionHouse.unpause();
const receipt = await unpauseTx.wait();
const { timestamp } = await ethers.provider.getBlock(receipt.blockHash);
const createdEvent = receipt.events?.find(e => e.event === 'AuctionCreated');
expect(createdEvent?.args?.nounId).to.equal(nounId.add(1));
expect(createdEvent?.args?.startTime).to.equal(timestamp);
expect(createdEvent?.args?.endTime).to.equal(timestamp + DURATION);
});
it('should settle the current auction and pause the contract if the minter is updated while the auction house is unpaused', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await nounsAuctionHouse.connect(bidderA).createBid(nounId, {
value: RESERVE_PRICE,
});
await nounsToken.setMinter(constants.AddressZero);
await ethers.provider.send('evm_increaseTime', [60 * 60 * 25]); // Add 25 hours
const settleTx = nounsAuctionHouse.connect(bidderA).settleCurrentAndCreateNewAuction();
await expect(settleTx)
.to.emit(nounsAuctionHouse, 'AuctionSettled')
.withArgs(nounId, bidderA.address, RESERVE_PRICE);
const paused = await nounsAuctionHouse.paused();
expect(paused).to.equal(true);
});
it('should burn a Noun on auction settlement if no bids are received', async () => {
await (await nounsAuctionHouse.unpause()).wait();
const { nounId } = await nounsAuctionHouse.auction();
await ethers.provider.send('evm_increaseTime', [60 * 60 * 25]); // Add 25 hours
const tx = nounsAuctionHouse.connect(bidderA).settleCurrentAndCreateNewAuction();
await expect(tx)
.to.emit(nounsAuctionHouse, 'AuctionSettled')
.withArgs(nounId, '0x0000000000000000000000000000000000000000', 0);
});
});
Example #9
Source File: end2end.test.ts From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
async function deploy() {
[deployer, bidderA, wethDeployer, noundersDAO] = await ethers.getSigners();
// Deployed by another account to simulate real network
weth = await deployWeth(wethDeployer);
// nonce 2: Deploy AuctionHouse
// nonce 3: Deploy nftDescriptorLibraryFactory
// nonce 4: Deploy NounsDescriptor
// nonce 5: Deploy NounsSeeder
// nonce 6: Deploy NounsToken
// nonce 0: Deploy NounsDAOExecutor
// nonce 1: Deploy NounsDAOLogicV1
// nonce 7: Deploy NounsDAOProxy
// nonce ++: populate Descriptor
// nonce ++: set ownable contracts owner to timelock
// 1. DEPLOY Nouns token
nounsToken = await deployNounsToken(
deployer,
noundersDAO.address,
deployer.address, // do not know minter/auction house yet
);
// 2a. DEPLOY AuctionHouse
const auctionHouseFactory = await ethers.getContractFactory('NounsAuctionHouse', deployer);
const nounsAuctionHouseProxy = await upgrades.deployProxy(auctionHouseFactory, [
nounsToken.address,
weth.address,
TIME_BUFFER,
RESERVE_PRICE,
MIN_INCREMENT_BID_PERCENTAGE,
DURATION,
]);
// 2b. CAST proxy as AuctionHouse
nounsAuctionHouse = NounsAuctionHouseFactory.connect(nounsAuctionHouseProxy.address, deployer);
// 3. SET MINTER
await nounsToken.setMinter(nounsAuctionHouse.address);
// 4. POPULATE body parts
descriptor = NounsDescriptorFactory.connect(await nounsToken.descriptor(), deployer);
await populateDescriptor(descriptor);
// 5a. CALCULATE Gov Delegate, takes place after 2 transactions
const calculatedGovDelegatorAddress = ethers.utils.getContractAddress({
from: deployer.address,
nonce: (await deployer.getTransactionCount()) + 2,
});
// 5b. DEPLOY NounsDAOExecutor with pre-computed Delegator address
timelock = await new NounsDaoExecutorFactory(deployer).deploy(
calculatedGovDelegatorAddress,
TIME_LOCK_DELAY,
);
// 6. DEPLOY Delegate
const govDelegate = await new NounsDaoLogicV1Factory(deployer).deploy();
// 7a. DEPLOY Delegator
const nounsDAOProxy = await new NounsDaoProxyFactory(deployer).deploy(
timelock.address,
nounsToken.address,
noundersDAO.address, // NoundersDAO is vetoer
timelock.address,
govDelegate.address,
VOTING_PERIOD,
VOTING_DELAY,
PROPOSAL_THRESHOLD_BPS,
QUORUM_VOTES_BPS,
);
expect(calculatedGovDelegatorAddress).to.equal(nounsDAOProxy.address);
// 7b. CAST Delegator as Delegate
gov = NounsDaoLogicV1Factory.connect(nounsDAOProxy.address, deployer);
// 8. SET Nouns owner to NounsDAOExecutor
await nounsToken.transferOwnership(timelock.address);
// 9. SET Descriptor owner to NounsDAOExecutor
await descriptor.transferOwnership(timelock.address);
// 10. UNPAUSE auction and kick off first mint
await nounsAuctionHouse.unpause();
// 11. SET Auction House owner to NounsDAOExecutor
await nounsAuctionHouse.transferOwnership(timelock.address);
}
Example #10
Source File: 0001-layer2-clearingHouse.ts From perpetual-protocol with GNU General Public License v3.0 | 4 votes |
migration: MigrationDefinition = {
configPath: "hardhat.flatten.clearinghouse.config.ts",
// deploy the flattened clearingHouse and init it just in case
getTasks: (context: MigrationContext) => {
let arbitrageur: string
let initMarginRatio: string
let oldImpAddr: string
let insuranceFund: string
let BTC: string
let arbitrageurBTCPositionSize: string
let openInterestNotional: string
let newImplContractAddr: string
return [
async (): Promise<void> => {
console.log("verifying state variables...")
// have to first flatten contracts for creating instances
const filename = `${ContractName.ClearingHouse}.sol`
await flatten(SRC_DIR, hre.config.paths.sources, filename)
// after flatten sol file we must re-compile again
await hre.run(TASK_COMPILE)
const clearingHouseContract = await context.factory
.create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
.instance()
oldImpAddr = await getImplementation(clearingHouseContract.address)
initMarginRatio = (await clearingHouseContract.initMarginRatio()).toString()
insuranceFund = await clearingHouseContract.insuranceFund()
BTC = context.systemMetadataDao.getContractMetadata(context.layer, AmmInstanceName.BTCUSDC).address
openInterestNotional = (await clearingHouseContract.openInterestNotionalMap(BTC)).toString()
arbitrageur = context.externalContract.arbitrageur!
arbitrageurBTCPositionSize = (
await clearingHouseContract.getUnadjustedPosition(BTC, arbitrageur)
).size.toString()
},
async (): Promise<void> => {
console.log("prepare upgrading...")
// deploy clearing house implementation
const clearingHouseContract = await context.factory.create<ClearingHouse>(
ContractFullyQualifiedName.FlattenClearingHouse,
)
newImplContractAddr = await clearingHouseContract.prepareUpgradeContractLegacy()
},
async (): Promise<void> => {
console.info("do upgrade")
// create an impersonated signer
const govAddr = context.externalContract.foundationGovernance
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [govAddr],
})
const govSigner = ethers.provider.getSigner(govAddr)
// prepare information for upgrading
const contractName = ContractFullyQualifiedName.FlattenClearingHouse
const proxyAddr = context.factory.create<ClearingHouse>(contractName).address!
const proxyAdmin = await upgrades.admin.getInstance()
await proxyAdmin.connect(govSigner).upgrade(proxyAddr, newImplContractAddr)
console.log(
`upgrade: contractFullyQualifiedName=${contractName}, proxy=${proxyAddr}, implementation=${newImplContractAddr}`,
)
},
async (): Promise<void> => {
const clearingHouseContract = await context.factory
.create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
.instance()
// for comparing with the new implementation address
console.log("old implementation address: ", oldImpAddr)
console.log("new implementation address: ", await getImplementation(clearingHouseContract.address))
const newInsuranceFund = await clearingHouseContract.insuranceFund()
console.log("insuranceFund address (shouldn't be zero address): ", newInsuranceFund)
expect(newInsuranceFund).to.eq(insuranceFund)
console.log("insuranceFund verified!")
expect((await clearingHouseContract.initMarginRatio()).toString()).to.eq(initMarginRatio)
console.log("initMarginRatio verified!")
expect((await clearingHouseContract.openInterestNotionalMap(BTC)).toString()).to.eq(
openInterestNotional,
)
console.log("openInterestNotional verified!")
expect((await clearingHouseContract.getUnadjustedPosition(BTC, arbitrageur)).size.toString()).to.eq(
arbitrageurBTCPositionSize,
)
console.log("arbitrageurBTCPositionSize verified!")
},
]
},
}
Example #11
Source File: 0004-layer2-clearingHouse-amm-openPos.ts From perpetual-protocol with GNU General Public License v3.0 | 4 votes |
migration: MigrationDefinition = {
configPath: "hardhat.flatten.clearinghouse.config.ts",
// deploy the flattened clearingHouse and init it just in case
getTasks: (context: MigrationContext) => {
let arbitrageur: string
let oldClearingHouseImpAddr: string
let oldAmmImpAddr: string
let newClearingHouseImplAddr: string
let newAmmImplAddr: string
let ammETHAddr: string
let arbitrageurPosition: any
let oldQuoteAssetReserve: BigNumber
let quoteAssetAddr: string
return [
async (): Promise<void> => {
console.log("get state variables for verification later...")
// flat clearingHouse
const fileClearingHouse = `${ContractName.ClearingHouse}.sol`
await flatten(SRC_DIR, hre.config.paths.sources, fileClearingHouse)
await hre.run(TASK_COMPILE)
// flat Amm
const fileAmm = `${ContractName.Amm}.sol`
await flatten(SRC_DIR, hre.config.paths.sources, fileAmm)
await hre.run(TASK_COMPILE)
const clearingHouseContract = await context.factory
.create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
.instance()
const ammContract = await context.factory
.createAmm(AmmInstanceName.ETHUSDC, ContractFullyQualifiedName.FlattenAmmUnderClearingHouse)
.instance()
oldClearingHouseImpAddr = await getImplementation(clearingHouseContract.address)
oldAmmImpAddr = await getImplementation(ammContract.address)
ammETHAddr = ammContract.address
arbitrageur = context.externalContract.arbitrageur!
arbitrageurPosition = await clearingHouseContract.getPosition(ammETHAddr, arbitrageur)
oldQuoteAssetReserve = await ammContract.quoteAssetReserve()
quoteAssetAddr = await ammContract.quoteAsset()
},
async (): Promise<void> => {
console.log("prepare upgrading...")
// deploy clearingHouse implementation
const clearingHouseContract = await context.factory.create<ClearingHouse>(
ContractFullyQualifiedName.FlattenClearingHouse,
)
newClearingHouseImplAddr = await clearingHouseContract.prepareUpgradeContractLegacy()
// deploy Amm implementation
const ammContract = await context.factory.createAmm(
AmmInstanceName.ETHUSDC,
ContractFullyQualifiedName.FlattenAmmUnderClearingHouse,
)
newAmmImplAddr = await ammContract.prepareUpgradeContractLegacy()
},
async (): Promise<void> => {
console.info("upgrading...")
// create an impersonated signer
const govSigner = await impersonateAccount(context.externalContract.foundationGovernance!)
// prepare information for upgrading
const contractNameClearingHouse = ContractFullyQualifiedName.FlattenClearingHouse
const proxyClearingHouseAddr = context.factory.create<ClearingHouse>(contractNameClearingHouse).address!
const proxyAdmin = await upgrades.admin.getInstance()
await proxyAdmin.connect(govSigner).upgrade(proxyClearingHouseAddr, newClearingHouseImplAddr)
console.log(
`upgrade: contractFullyQualifiedName=${contractNameClearingHouse}, proxy=${proxyClearingHouseAddr}, implementation=${newClearingHouseImplAddr}`,
)
await proxyAdmin.connect(govSigner).upgrade(ammETHAddr, newAmmImplAddr)
console.log(
`upgrade: contractFullyQualifiedName=${contractNameClearingHouse}, proxy=${ammETHAddr}, implementation=${newAmmImplAddr}`,
)
},
// verify can openPosition
async (): Promise<void> => {
const clearingHouseContract = await context.factory
.create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
.instance()
const gov = context.externalContract.foundationGovernance!
const govSigner = await impersonateAccount(gov)
const arbitrageurSigner = await impersonateAccount(arbitrageur)
const usdcAddr = context.externalContract.usdc!
const USDCArtifact = await artifacts.readArtifact(ContractFullyQualifiedName.FlattenIERC20)
const usdcInstance = (await ethers.getContractAt(USDCArtifact.abi, usdcAddr)) as ERC20
// send eth and usdc to gov account
const txETH = await arbitrageurSigner.sendTransaction({
to: gov,
value: ethers.utils.parseEther("0.1"),
})
await txETH.wait()
const txUSDC = await usdcInstance.connect(arbitrageurSigner).transfer(gov, parseUnits("1000", 6))
await txUSDC.wait()
const txApprove = await usdcInstance
.connect(govSigner)
.approve(clearingHouseContract.address, parseUnits("1000", 6))
await txApprove.wait()
// open position
const receipt = await clearingHouseContract
.connect(govSigner)
.openPosition(
ammETHAddr,
Side.BUY,
{ d: parseEther("600") },
{ d: parseEther("1") },
{ d: parseEther("0") },
)
const ownerPosition = await clearingHouseContract.getPosition(ammETHAddr, gov)
expect(ownerPosition.margin.d).to.eq(parseEther("600"))
expect(ownerPosition.blockNumber).to.eq(receipt.blockNumber)
},
// verify arbitrageur's position on ETH market and Amm's reserve
async (): Promise<void> => {
const clearingHouseContract = await context.factory
.create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
.instance()
const ammContract = await context.factory
.createAmm(AmmInstanceName.ETHUSDC, ContractFullyQualifiedName.FlattenAmmUnderClearingHouse)
.instance()
// for comparing with the new implementation address
console.log("old implementation address of ClearingHouse: ", oldClearingHouseImpAddr)
console.log(
"new implementation address of ClearingHouse: ",
await getImplementation(clearingHouseContract.address),
)
console.log("old implementation address of Amm: ", oldAmmImpAddr)
console.log("new implementation address of Amm: ", await getImplementation(ammContract.address))
console.log("arbitrageur position: ")
const arbitrageurPositionNow = await clearingHouseContract.getPosition(ammETHAddr, arbitrageur)
console.log("size: ", arbitrageurPositionNow.size.d.toString())
console.log("margin: ", arbitrageurPositionNow.margin.d.toString())
console.log("last updated blockNumber: ", arbitrageurPositionNow.blockNumber.toString())
expect(arbitrageurPosition.size.d).to.eq(arbitrageurPositionNow.size.d)
expect(arbitrageurPosition.margin.d).to.eq(arbitrageurPositionNow.margin.d)
expect(arbitrageurPosition.blockNumber).to.eq(arbitrageurPositionNow.blockNumber)
console.log("amm states: ")
const newQuoteAssetReserve = await ammContract.quoteAssetReserve()
console.log("quote asset reserve: ", oldQuoteAssetReserve.toString())
console.log("USDC addr: ", quoteAssetAddr.toString())
expect(newQuoteAssetReserve).to.eq(oldQuoteAssetReserve.add(parseEther("600")))
expect(await ammContract.quoteAsset()).to.eq(quoteAssetAddr)
},
]
},
}
Example #12
Source File: OzContractDeployerSpec.ts From perpetual-protocol with GNU General Public License v3.0 | 4 votes |
// conflict with hardhat-gas-reporter without proxyResolver
describe("OzContractDeployer Spec", () => {
const [wallet] = new MockProvider().getWallets()
const ozContractDeployer: OzContractDeployer = new OzContractDeployer()
const contractNameV1 = "src/mock/UpgradableContractV1.sol:UpgradableContractV1"
const contractNameV2 = "src/mock/UpgradableContractV2.sol:UpgradableContractV2"
// the following two are proxys
let v1: UpgradableContractV1
let v2: UpgradableContractV2
let factoryV2: ContractFactory
let proxyAddr: string
async function getImplementation(proxyAddr: string) {
const proxyAdmin = await upgrades.admin.getInstance()
return proxyAdmin.getProxyImplementation(proxyAddr)
}
beforeEach(async () => {
factoryV2 = await ethers.getContractFactory(contractNameV2)
proxyAddr = await ozContractDeployer.deploy(contractNameV1, [])
v1 = (await ethers.getContractAt(contractNameV1, proxyAddr)) as UpgradableContractV1
})
it("retrieve version that's initialized", async () => {
expect((await v1.version()).toString()).eq("1")
})
it("doesn't have increaseVersion function", async () => {
const wrongV2 = factoryV2.attach(proxyAddr) as UpgradableContractV2
await expect(wrongV2.increaseVersion()).to.be.reverted
})
it("force error, initialization is included in ozContractDeployer.deploy()", async () => {
const v1ImplAddr = await getImplementation(proxyAddr)
const v1Impl = (await ethers.getContractAt(contractNameV1, v1ImplAddr)) as UpgradableContractV1
await expectRevert(v1Impl.initialize(), "Contract instance has already been initialized")
})
describe("upgrade to v2", () => {
beforeEach(async () => {
await ozContractDeployer.upgrade(proxyAddr, contractNameV2, [])
v2 = (await ethers.getContractAt(contractNameV2, proxyAddr)) as UpgradableContractV2
})
it("won't change the proxy address", async () => {
expect(v2.address).eq(proxyAddr)
})
it("won't change state", async () => {
expect((await v2.version()).toString()).eq("1")
})
it("has a new function", async () => {
await v2.increaseVersion()
expect((await v1.version()).toString()).eq("2")
})
it("force error, initialization is included in ozContractDeployer.upgrade()", async () => {
const v2ImplAddr = await getImplementation(v2.address)
const v2Impl = (await ethers.getContractAt(contractNameV2, v2ImplAddr)) as UpgradableContractV2
await expectRevert(v2Impl.initialize(), "Contract instance has already been initialized")
})
})
describe("prepareUpgrade to v2", () => {
let v2ImplAddr: string
beforeEach(async () => {
v2ImplAddr = await ozContractDeployer.prepareUpgrade(proxyAddr, contractNameV2, [])
})
it("ozContractDeployer.prepareUpgrade() returns the implementation address; will be different from proxy address", async () => {
expect(v2ImplAddr).not.eq(proxyAddr)
})
it("won't change state", async () => {
expect((await v1.version()).toString()).eq("1")
})
it("proxy still has no new function", async () => {
const wrongV2 = factoryV2.attach(proxyAddr) as UpgradableContractV2
await expect(wrongV2.increaseVersion()).to.be.reverted
})
it("force error, initialization is included in ozContractDeployer.prepareUpgrade()", async () => {
const v2Impl = (await ethers.getContractAt(contractNameV2, v2ImplAddr)) as UpgradableContractV2
await expectRevert(v2Impl.initialize(), "Contract instance has already been initialized")
})
})
describe("transferProxyAdminOwnership to others", () => {
it("can't transfer to empty address", async () => {
await expect(OzContractDeployer.transferProxyAdminOwnership("0x0000000000000000000000000000000000000000"))
.to.be.reverted
})
it("can't transfer and upgrade once transfer admin to others, but can deploy new and prepareUpgrade", async () => {
await OzContractDeployer.transferProxyAdminOwnership(wallet.address)
await expect(OzContractDeployer.transferProxyAdminOwnership(wallet.address)).to.be.reverted
await expect(ozContractDeployer.upgrade(proxyAddr, contractNameV2, [])).to.be.reverted
await upgrades.prepareUpgrade(v1.address, factoryV2)
const newProxy = await ozContractDeployer.deploy(contractNameV2, [])
await expect(ozContractDeployer.upgrade(newProxy, contractNameV1, [])).to.be.reverted
})
// once transferProxyAdminOwnership has been called, every admin-only tx won't be able to test
})
})