Skip to content

Commit

Permalink
refactor upgrade example
Browse files Browse the repository at this point in the history
  • Loading branch information
zoeyTM committed May 7, 2024
1 parent 8c83224 commit 759edd5
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 42 deletions.
3 changes: 3 additions & 0 deletions examples/upgradeable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ To run the Ignition deploy against the ephemeral hardhat network:

```shell
npx hardhat ignition deploy ./ignition/modules/ProxyModule.js

# or to deploy the upgrade module
npx hardhat ignition deploy ./ignition/modules/UpgradeModule.js
```

## Test
Expand Down
56 changes: 15 additions & 41 deletions examples/upgradeable/ignition/modules/ProxyModule.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// ./ignition/LockModule.js
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");

/**
Expand All @@ -23,63 +22,38 @@ const proxyModule = buildModule("ProxyModule", (m) => {
"0x",
]);

// We need to get the address of the ProxyAdmin contrac that was created by the TransparentUpgradeableProxy
// We need to get the address of the ProxyAdmin contract that was created by the TransparentUpgradeableProxy
// so that we can use it to upgrade the proxy later.
const proxyAdminAddress = m.readEventArgument(
proxy,
"AdminChanged",
"newAdmin"
);

// Here we use m.contractAt(...) to create a contract instance for the ProxyAdmin that we can interact with.
// Here we use m.contractAt(...) to create a contract instance for the ProxyAdmin that we can interact with later to upgrade the proxy.
const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress);

// Return the proxy and proxy admin so that they can be used by other modules.
return { proxyAdmin, proxy };
});

/**
* This is the second module that will be run. It upgrades the proxy to a new
* version of the Demo contract.
* This is the second module that will be run, and it is also the only module exported from this file.
* It creates a contract instance for the Demo contract using the proxy from the previous module.
*/
const upgradeModule = buildModule("UpgradeModule", (m) => {
// Make sure we're account that owns the ProxyAdmin contract.
const proxyAdminOwner = m.getAccount(0);

const demoModule = buildModule("DemoModule", (m) => {
// Get the proxy and proxy admin from the previous module.
const { proxyAdmin, proxy } = m.useModule(proxyModule);

// This is the new version of the Demo contract that we want to upgrade to.
const demoV2 = m.contract("DemoV2");

// Upgrade the proxy to the new version of the Demo contract.
// This function also accepts a data parameter, which can be used to call a function,
// but we don't need it here so we pass an empty hex string ("0x").
m.call(proxyAdmin, "upgradeAndCall", [proxy, demoV2, "0x"], {
from: proxyAdminOwner,
});

// Return the proxy and proxy admin so that they can be used by other modules.
return { proxyAdmin, proxy };
});

/**
* This is the third and final module that will be run.
*
* It takes the proxy from the previous module and uses it to create a local contract instance
* for the DemoV2 contract. This allows us to interact with the DemoV2 contract via the proxy.
*/
const interactableModule = buildModule("InteractableModule", (m) => {
// Get the proxy from the previous module.
const { proxy } = m.useModule(upgradeModule);
const { proxy, proxyAdmin } = m.useModule(proxyModule);

// Create a local contract instance for the DemoV2 contract.
// This line tells Hardhat Ignition to treat the contract at the proxy address as an DemoV2 contract.
// This allows us to call functions on the DemoV2 contract via the proxy.
const demo = m.contractAt("DemoV2", proxy);
// Here we're using m.contractAt(...) a bit differently than we did above.
// While we're still using it to create a contract instance, we're now telling Hardhat Ignition
// to treat the contract at the proxy address as an instance of the Demo contract.
// This allows us to interact with the underlying Demo contract via the proxy from within tests and scripts.
const demo = m.contractAt("Demo", proxy);

// Return the contract instance so that it can be used by other modules or in tests.
return { demo };
// Return the contract instance, along with the original proxy and proxyAdmin contracts
// so that they can be used by other modules, or in tests and scripts.
return { demo, proxy, proxyAdmin };
});

module.exports = interactableModule;
module.exports = demoModule;
48 changes: 48 additions & 0 deletions examples/upgradeable/ignition/modules/UpgradeModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");

const ProxyModule = require("./ProxyModule");

/**
* This module upgrades the proxy to a new version of the Demo contract.
*/
const upgradeModule = buildModule("UpgradeModule", (m) => {
// Make sure we're using the account that owns the ProxyAdmin contract.
const proxyAdminOwner = m.getAccount(0);

// Get the proxy and proxy admin from the previous module.
const { proxyAdmin, proxy } = m.useModule(ProxyModule);

// This is the new version of the Demo contract that we want to upgrade to.
const demoV2 = m.contract("DemoV2");

// Upgrade the proxy to the new version of the Demo contract.
// This function also accepts a data parameter, which can be used to call a function,
// but we don't need it here so we pass an empty hex string ("0x").
m.call(proxyAdmin, "upgradeAndCall", [proxy, demoV2, "0x"], {
from: proxyAdminOwner,
});

// Return the proxy and proxy admin so that they can be used by other modules.
return { proxyAdmin, proxy };
});

/**
* This is the final module that will be run.
*
* It takes the proxy from the previous module and uses it to create a local contract instance
* for the DemoV2 contract. This allows us to interact with the DemoV2 contract via the proxy.
*/
const demoV2Module = buildModule("DemoV2Module", (m) => {
// Get the proxy from the previous module.
const { proxy } = m.useModule(upgradeModule);

// Create a local contract instance for the DemoV2 contract.
// This line tells Hardhat Ignition to use the DemoV2 ABI for the contract at the proxy address.
// This allows us to call functions on the DemoV2 contract via the proxy.
const demo = m.contractAt("DemoV2", proxy);

// Return the contract instance so that it can be used by other modules or in tests.
return { demo };
});

module.exports = demoV2Module;
13 changes: 12 additions & 1 deletion examples/upgradeable/test/ProxyDemo.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
const { expect } = require("chai");

const ProxyModule = require("../ignition/modules/ProxyModule");
const UpgradeModule = require("../ignition/modules/UpgradeModule");

describe("Demo Proxy", function () {
describe("Proxy interaction", async function () {
it("Should be interactable via proxy", async function () {
const [owner, otherAccount] = await ethers.getSigners();

const { demo } = await ignition.deploy(ProxyModule);

expect(await demo.connect(otherAccount).version()).to.equal("1.0.0");
});
});

describe("Upgrading", function () {
it("Should have upgraded the proxy to DemoV2", async function () {
const [owner, otherAccount] = await ethers.getSigners();

const { demo } = await ignition.deploy(ProxyModule);
const { demo } = await ignition.deploy(UpgradeModule);

expect(await demo.connect(otherAccount).version()).to.equal("2.0.0");
});
Expand Down

0 comments on commit 759edd5

Please sign in to comment.