Skip to content

Commit c5d1c58

Browse files
committed
update the guide for the new example
1 parent 515e73c commit c5d1c58

File tree

1 file changed

+82
-19
lines changed

1 file changed

+82
-19
lines changed

docs/src/content/ignition/docs/guides/upgradeable-proxies.md

+82-19
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Because we're using the OpenZeppelin proxy contracts, we need to import them her
6060

6161
## Writing our Ignition modules
6262

63-
Inside our `ignition` directory, we'll create a directory called `modules`, if one doesn't already exist. Inside this directory, we'll create a file called `ProxyModule.js` (or `ProxyModule.ts` if you're using TypeScript). Inside this file, we'll break up our Ignition module into three parts.
63+
Inside our `ignition` directory, we'll create a directory called `modules`, if one doesn't already exist. Inside this directory, we'll create a file called `ProxyModule.js` (or `ProxyModule.ts` if you're using TypeScript). Inside this file, we'll break up our first Ignition module into two parts.
6464

6565
### Part 1: Deploying our proxies
6666

@@ -140,11 +140,53 @@ Then, we deploy our `TransparentUpgradeableProxy` contract. This contract will b
140140

141141
When we deploy the proxy, it will automatically create a new `ProxyAdmin` contract within its constructor. We'll need to get the address of this contract so that we can interact with it later. To do this, we'll use the `m.readEventArgument(...)` method to read the `newAdmin` argument from the `AdminChanged` event that is emitted when the proxy is deployed.
142142

143-
Finally, we'll use the `m.contractAt(...)` method to get the `ProxyAdmin` contract at the address we retrieved from the event and return it along with the proxy.
143+
Finally, we'll use the `m.contractAt(...)` method to tell Ignition to use the `ProxyAdmin` ABI for the contract at the address we just retrieved. This will allow us to interact with the `ProxyAdmin` contract when we upgrade our proxy.
144144

145-
### Part 2: Upgrading our proxy
145+
### Part 2: Interacting with our proxy
146146

147-
Now that we have our proxy deployed, we want to upgrade it. To do this, within the same file, we'll create a new module called `UpgradeModule`:
147+
Now that we have our proxy deployed, we're ready to interact with it. To do this, we'll create a new module called `DemoModule`:
148+
149+
```js
150+
const demoModule = buildModule("DemoModule", (m) => {
151+
const { proxy, proxyAdmin } = m.useModule(upgradeModule);
152+
153+
const demo = m.contractAt("Demo", proxy);
154+
155+
return { demo, proxy, proxyAdmin };
156+
});
157+
```
158+
159+
First, we use the `m.useModule(...)` method to get the proxy contract from the previous module. This will ensure that the proxy is deployed before we try to upgrade it.
160+
161+
Then, similar to what we did with our `ProxyAdmin` above, we use `m.contractAt("Demo", proxy)` to tell Ignition to use the `Demo` ABI for the contract at the address of the proxy. This will allow us to interact with the `Demo` contract through the proxy when we use it in tests or scripts.
162+
163+
Finally, we return the `Demo` contract instance so that we can use it in other modules, or tests and scripts. We also return the `proxy` and `proxyAdmin` contracts so that we can use them to upgrade our proxy in the next module.
164+
165+
As a last step, we'll export `demoModule` from our file so that we can deploy it and use it in our tests or scripts:
166+
167+
::::tabsgroup{options="TypeScript,JavaScript"}
168+
169+
:::tab{value="TypeScript"}
170+
171+
```typescript
172+
export default demoModule;
173+
```
174+
175+
:::
176+
177+
:::tab{value="JavaScript"}
178+
179+
```javascript
180+
module.exports = demoModule;
181+
```
182+
183+
:::
184+
185+
::::
186+
187+
### Part 3: Upgrading our proxy
188+
189+
Next it's time to upgrade our proxy to a new version. To do this, we'll create a new file within our `ignition/modules` directory called `UpgradeModule.js` (or `UpgradeModule.ts` if you're using TypeScript). Inside this file, we'll again break up our module into two parts. To start, we'll write our `UpgradeModule`:
148190

149191
```js
150192
const upgradeModule = buildModule("UpgradeModule", (m) => {
@@ -162,22 +204,23 @@ const upgradeModule = buildModule("UpgradeModule", (m) => {
162204
});
163205
```
164206

165-
This module begins the same way as the previous one, by getting the account that owns the `ProxyAdmin` contract. We'll use this in a moment to upgrade the proxy.
207+
This module begins the same way as `ProxyModule`, by getting the account that owns the `ProxyAdmin` contract. We'll use this in a moment to upgrade the proxy.
166208

167-
Next, we use the `m.useModule(...)` method to get the `ProxyAdmin` and proxy contracts from the previous module. This will ensure that the proxy is deployed before we try to upgrade it.
209+
Next, we use the `m.useModule(...)` method to get the `ProxyAdmin` and proxy contracts from the previous module.
168210

169211
Then, we deploy our `DemoV2` contract. This will be the contract that we'll upgrade our proxy to.
170212

171213
Finally, we call the `upgradeAndCall` method on the `ProxyAdmin` contract. This method takes three arguments: the proxy contract, the new implementation contract, and a data parameter that can be used to call an additional function. We don't need it right now, so we'll leave it blank by setting it to an empty hex string (`"0x"`). We also provide the `from` option to ensure that the upgrade is called from the owner of the `ProxyAdmin` contract.
172214

173215
Lastly, we again return the `ProxyAdmin` and proxy contracts so that we can use them in our next module.
174216

175-
### Part 3: Interacting with our proxy
217+
### Part 4: Interacting with our upgraded proxy
176218

177-
Now that we have our proxy deployed and upgraded, we're ready to interact with it. To do this, we'll create a new module called `InteractableModule`:
219+
Finally, in the same file, we'll create our module called `DemoV2Module`:
178220

179221
```js
180-
const interactableModule = buildModule("InteractableModule", (m) => {
222+
223+
const demoV2Module = buildModule("DemoV2Module", (m) => {
181224
const { proxy } = m.useModule(upgradeModule);
182225

183226
const demo = m.contractAt("DemoV2", proxy);
@@ -186,28 +229,26 @@ const interactableModule = buildModule("InteractableModule", (m) => {
186229
});
187230
```
188231

189-
First, as before, we use the `m.useModule(...)` method to get the proxy contract from the previous module.
190-
191-
Then, we use `m.contractAt("DemoV2", proxy)` to get the `DemoV2` contract at the address of the proxy. This will allow us to interact with the proxy as if it were the `DemoV2` contract itself.
232+
This module is similar to `DemoModule`, but instead of using the `Demo` contract, we use the `DemoV2` contract. Though the `Demo` contracts are contrived for this example and don't actually change the ABI between upgrades, this module demonstrates how you can interact with different versions of your contract ABI through the same proxy.
192233

193-
Finally, we return the `DemoV2` contract instance so that we can use it in other modules or tests.
234+
As before, we return the `DemoV2` contract instance so that we can use it in other modules, or tests and scripts. We could also return the `proxy` and `proxyAdmin` contracts if we needed to interact with them further, but for the purposes of this example, we left them out.
194235

195-
As a final step, we'll export `interactableModule` from our file so that we can deploy it and use it in our tests:
236+
As a last step, we'll export `demoV2Module` from our file so that we can deploy it and use it in our tests or scripts:
196237

197238
::::tabsgroup{options="TypeScript,JavaScript"}
198239

199240
:::tab{value="TypeScript"}
200241

201242
```typescript
202-
export default interactableModule;
243+
export default demoV2Module;
203244
```
204245

205246
:::
206247

207248
:::tab{value="JavaScript"}
208249

209250
```javascript
210-
module.exports = interactableModule;
251+
module.exports = demoV2Module;
211252
```
212253

213254
:::
@@ -216,7 +257,7 @@ module.exports = interactableModule;
216257

217258
## Testing our Ignition modules
218259

219-
Now that we've written our Ignition modules for deploying and interacting with our proxy, let's write a simple test to make sure everything works as expected.
260+
Now that we've written our Ignition modules for deploying and interacting with our proxy, let's write a couple of simple tests to make sure everything works as expected.
220261

221262
Inside our `test` directory, we'll create a file called `ProxyDemo.js` (or `ProxyDemo.ts` if you're using TypeScript):
222263

@@ -228,8 +269,19 @@ Inside our `test` directory, we'll create a file called `ProxyDemo.js` (or `Prox
228269
import { expect } from "chai";
229270

230271
import ProxyModule from "../ignition/modules/ProxyModule";
272+
import UpgradeModule from "../ignition/modules/UpgradeModule";
231273

232274
describe("Demo Proxy", function () {
275+
describe("Proxy interaction", async function () {
276+
it("Should be interactable via proxy", async function () {
277+
const [owner, otherAccount] = await ethers.getSigners();
278+
279+
const { demo } = await ignition.deploy(ProxyModule);
280+
281+
expect(await demo.connect(otherAccount).version()).to.equal("1.0.0");
282+
});
283+
});
284+
233285
describe("Upgrading", function () {
234286
it("Should have upgraded the proxy to DemoV2", async function () {
235287
const [owner, otherAccount] = await ethers.getSigners();
@@ -250,13 +302,24 @@ describe("Demo Proxy", function () {
250302
const { expect } = require("chai");
251303

252304
const ProxyModule = require("../ignition/modules/ProxyModule");
305+
const UpgradeModule = require("../ignition/modules/UpgradeModule");
253306

254307
describe("Demo Proxy", function () {
308+
describe("Proxy interaction", async function () {
309+
it("Should be interactable via proxy", async function () {
310+
const [owner, otherAccount] = await ethers.getSigners();
311+
312+
const { demo } = await ignition.deploy(ProxyModule);
313+
314+
expect(await demo.connect(otherAccount).version()).to.equal("1.0.0");
315+
});
316+
});
317+
255318
describe("Upgrading", function () {
256319
it("Should have upgraded the proxy to DemoV2", async function () {
257320
const [owner, otherAccount] = await ethers.getSigners();
258321

259-
const { demo } = await ignition.deploy(ProxyModule);
322+
const { demo } = await ignition.deploy(UpgradeModule);
260323

261324
expect(await demo.connect(otherAccount).version()).to.equal("2.0.0");
262325
});
@@ -268,7 +331,7 @@ describe("Demo Proxy", function () {
268331

269332
::::
270333

271-
Here we use Hardhat Ignition to deploy our imported module. Then, we use the `demo` contract instance that is returned to call the `version` method and make sure that it returns the correct version string.
334+
Here we use Hardhat Ignition to deploy our imported modules. First, we deploy our base `ProxyModule` that returns the first version of our `Demo` contract and test it to ensure the proxy worked and retrieves the appropriate version string. Then, we deploy our `UpgradeModule` that returns the second version of our `Demo` contract and test it to ensure the proxy now returns the updated version string.
272335

273336
## Further reading
274337

0 commit comments

Comments
 (0)