Skip to content

Commit 0867ed6

Browse files
authored
Add AnlogTokenV2 [pre|post]upgrade tests (#16)
* add UpgradeV1V2Test: fails * annotate V2 as an upgrade to V1 * chore: uppercase constants in test * upgrade v1 v2 fails: InvalidInitialization * UpgradeV1V2 works * add test_postUpgrade basic steps * all AnlogTokenV2 tests also to run after upgrade * add script/01_Upgrade.V1.V2.s.sol * neat readme * better title
1 parent ce43b56 commit 0867ed6

File tree

9 files changed

+265
-187
lines changed

9 files changed

+265
-187
lines changed

.env.anvil

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
export ANVIL_RPC_URL=http://localhost:9545
1+
export ANVIL_PORT=9545
2+
export ANVIL_RPC_URL=http://localhost:$ANVIL_PORT
23
# well-known (thus key compromised) Anvil default account(0)
34
export DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
45
# well-known (thus key compromised) Anvil default account(1)
@@ -9,7 +10,9 @@ export UPGRADER=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
910
export PAUSER=0x90F79bf6EB2c4f870365E785982E1f101E93b906
1011
# well-known (thus key compromised) Anvil default account(4)
1112
export UNPAUSER=0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65
13+
# Already deployed proxy
14+
export PROXY=0xF289621eD23Dd4051362FFddC57C237e3c00018D
1215
# Teleport-specific
13-
export GATEWAY=0x49877F1e26d523e716d941a424af46B86EcaF09E
16+
export GATEWAY=0xEb73D0D236DE8F8D09dc6A52916e5849ff1E8dfA
1417
export TIMECHAIN_ROUTE_ID=1000
1518
export MINIMAL_TELEPORT_VALUE=1000000000000

README.md

Lines changed: 47 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
> While this upgradeable token smart contract is built using [audited OZ libraries](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/tree/v5.2.0/audits),
44
> **this particular implementation has not yet underwent any security audits. Use at your own risk.**
55
6-
# $ANLOG ERC20 Token
6+
# `$WANLOG`: Wrapped `ANLOG` ERC20 Token
77

88
## Initial Requirements
99

@@ -49,16 +49,8 @@ This project is built with the [Foundry](https://book.getfoundry.sh/) framework.
4949

5050
## Testing
5151

52-
If changed some contract, run this first:
53-
54-
``` sh
55-
forge fmt && forge clean && forge build
56-
```
57-
58-
Then run tests:
59-
6052
``` sh
61-
forge test
53+
forge fmt && forge clean && forge tests
6254
```
6355

6456
## Deployment
@@ -68,54 +60,37 @@ forge test
6860
<details>
6961
<summary>Expand me</summary>
7062

71-
Spin out a default [Anvil](https://book.getfoundry.sh/anvil/) node:
63+
Load environment variables:
7264

7365
``` sh
74-
anvil -p 9545
66+
source .env.anvil
7567
```
7668

77-
Load environment variables and run the deployment script:
69+
Spin out an [Anvil](https://book.getfoundry.sh/anvil/) fork of Sepolia:
70+
71+
``` sh
72+
anvil -f $SEPOLIA_RPC_URL -p $ANVIL_PORT
73+
```
74+
75+
Run the deployment script:
7876

7977
``` sh
80-
source .env.anvil
8178
forge script script/00_Deploy.V1.s.sol --rpc-url $ANVIL_RPC_URL --broadcast -i 1
8279
```
8380

8481
It will ask you to enter the private key. As we're using Anvil's default `account (0)` as the deployer (specified in the [`.env.anvil`](./.env.anvil)), use its (**!well-known!**) key here (can be found in Anvil logs).
8582

8683
</details>
8784

88-
### To Sepolia testnet
85+
### To Sepolia
8986

90-
> [!IMPORTANT]
91-
> You need to setup environment first, see [`.env.sepolia.example`](./.env.sepolia.example)
92-
93-
#### Dry-run on Fork
94-
95-
<details>
96-
<summary>Expand me</summary>
97-
98-
Spin out an Anvil fork of Sepolia network:
87+
Load environment variables:
9988

10089
``` sh
10190
source .env.sepolia
102-
anvil -f $SEPOLIA_RPC_URL -p 9545
10391
```
10492

105-
Deploy:
106-
107-
``` sh
108-
source .env.sepolia
109-
forge script script/00_Deploy.V1.s.sol --rpc-url $ANVIL_RPC_URL --broadcast -i 1
110-
```
111-
112-
Make sure to provide the private key of the `DEPLOYER` account upon script's interactive prompt.
113-
</details>
114-
115-
116-
#### Real run on Sepolia
117-
118-
##### Deploy
93+
#### Deploy
11994

12095
Once steps described above taken and succeed, deploy to Sepolia with:
12196

@@ -129,7 +104,7 @@ forge script script/00_Deploy.V1.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast -i
129104
> forge script script/00_Deploy.V1.s.sol --rpc-url $ANVIL_RPC_URL --broadcast -l
130105
> ```
131106
132-
##### Verify
107+
#### Verify
133108
134109
Figure out `solc` version used to compile the contracts:
135110
@@ -153,37 +128,46 @@ forge v --verifier etherscan --compiler-version=<solc_version> \
153128
src/AnlogTokenV1.sol:AnlogTokenV1
154129
```
155130

156-
### Upgrade instructions
131+
## Upgrade
157132

158133
> [!NOTE]
159-
> Commands below are given for the setting when you upgrade from `V0` to `V1Upgrade`. See [`Upgrade.V0.V1.t.sol`](test/Upgrade.V0.V1.t.sol) for the reference.
134+
> Commands below are given for the setting when you upgrade from `V1` to `V2`.
135+
> Also see [`Upgrade.V1.V2.t.sol` test](test/Upgrade.V0.V1.t.sol) for the reference.
136+
137+
*Prerequisites*: you have `V1` deployed.
138+
139+
### Locally to Anvil
140+
141+
<details>
142+
<summary>Expand me</summary>
160143

144+
Load environment variables:
161145

162-
Let say you have deployed proxy and `V0` implementation contract for it.
163-
To upgrade the implementation contract to `V1Upgrade`:
146+
``` sh
147+
source .env.anvil
148+
```
149+
150+
First spin out a local anvil and deploy `V1` to it by following instructions above.
151+
152+
Then set `PROXY` environment variable to the address of the deployed proxy.
164153

165-
1. Deploy `V1Upgrade`:
154+
Then run `V1.V2` upgrade script:
166155

167-
``` sh
168-
source .env.sepolia
169-
forge create --constructor-args=0x,0x,0x,0x \
170-
--rpc-url $SEPOLIA_RPC_URL \
171-
--from $DEPLOYER \
172-
test/mock/AnlogTokenV1Upgrade.sol:AnlogTokenV1Upgrade -i --broadcast
173-
```
156+
``` sh
157+
forge script script/01_Upgrade.V1.V2.s.sol --rpc-url $ANVIL_RPC_URL --broadcast -i 1
158+
```
159+
160+
It will ask you to enter the private key. As we're using Anvil's default `account (2)` as the `UPGRADER` (specified in the [`.env.anvil`](./.env.anvil)), use its (**!well-known!**) key here (can be found in Anvil logs).
161+
162+
</details>
174163

175-
**NOTE** that we provided `0x,0x,0x,0x` to the constructor args. That is because we don't care on the values of the implementation contract storage. The proxy storage will be updated in the following `initialize()` call right after the upgrade.
176164

177-
2. Prepare `calldata` for `V1Upgrade` initializer:
165+
### To Sepolia
178166

179-
```sh
180-
source .env.sepolia
181-
cast cd "initialize(address,address,address,address)()" $MINTER $UPGRADER $PAUSER $UNPAUSER
182-
```
183-
You should get hex-encoded `calldata` as the result.
167+
In a live network, most probably a multisig is used for the `UPGRADER` account. Thus for doing an upgrade,
184168

185-
3. Dispatch the call to [`upgradeToAndCall(address,bytes)`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/332bcb5f4d9cf0ae0f98fe91c77d9c1fb9951506/contracts/proxy/ERC1967/ERC1967Utils.sol#L67), providing the following args:
169+
dispatch the call to [`upgradeToAndCall(address,bytes)`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/332bcb5f4d9cf0ae0f98fe91c77d9c1fb9951506/contracts/proxy/ERC1967/ERC1967Utils.sol#L67), providing the following args:
186170

187-
- **address**: current (`V0`) implementation contract address;
188-
- **bytes**: `calldata` for the `V1Upgrade` implementation contract initializer
189-
(the one you've got on the previous step).
171+
- **address**: current (`V1`) implementation contract address;
172+
- **bytes**: (empty) (this is `calldata` for the `V2` implementation contract initializer, which we don't need for this upgrade).
173+
**note** for the cases when you need it, look at this [commit](https://github.com/Analog-Labs/erc20-token/blob/2c6025e8099966194ed006c25f1a79b5cabfa0df/README.md#upgrade-instructions) for the instructions of how to calculate it.

script/00_Deploy.V1.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {Script, console} from "forge-std/Script.sol";
55
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
66
import {AnlogTokenV1} from "../src/AnlogTokenV1.sol";
77

8-
contract AnlogTokenScript is Script {
8+
contract AnlogTokenV1DeploymentScript is Script {
99
AnlogTokenV1 public token;
1010

1111
function setUp() public {}

script/01_Deploy.V2.s.sol

Lines changed: 0 additions & 44 deletions
This file was deleted.

script/01_Upgrade.V1.V2.s.sol

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.13;
3+
4+
import {Script, console} from "forge-std/Script.sol";
5+
import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol";
6+
import {AnlogTokenV1} from "../src/AnlogTokenV1.sol";
7+
8+
contract AnlogTokenV1V2UpgradeScript is Script {
9+
AnlogTokenV1 public token;
10+
11+
function setUp() public {}
12+
13+
function run() public {
14+
address proxy = vm.envAddress("PROXY");
15+
address upgrader = vm.envAddress("UPGRADER");
16+
17+
// Teleport-related
18+
address gateway = vm.envAddress("GATEWAY");
19+
uint16 timechainId = uint16(vm.envUint("TIMECHAIN_ROUTE_ID"));
20+
uint256 minimalTeleport = vm.envUint("MINIMAL_TELEPORT_VALUE");
21+
22+
// We don't need initializer to be called for this upgrade,
23+
// as all the initial V1 token config stays the same.
24+
// Thus `data` is empty.
25+
bytes memory emptyData;
26+
27+
Options memory opts;
28+
opts.constructorData = abi.encode(gateway, timechainId, minimalTeleport);
29+
30+
vm.startBroadcast(upgrader);
31+
Upgrades.upgradeProxy(proxy, "AnlogTokenV2.sol", emptyData, opts);
32+
vm.stopBroadcast();
33+
34+
console.log("[dry-run] Upgraded to AnlogTokenV2.sol implentation to proxy address: ", proxy);
35+
console.log(" TELEPORT SETTINGS:");
36+
console.log(" GATEWAY: ", gateway);
37+
console.log(" TIMECHAIN_ROUTE_ID: ", timechainId);
38+
console.log(" MINIMAL_TELEPORT_VALUE: ", minimalTeleport);
39+
}
40+
}

src/AnlogTokenV2.sol

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
1414
import {IGmpReceiver} from "@analog-gmp/interfaces/IGmpReceiver.sol";
1515
import {IGateway} from "@analog-gmp/interfaces/IGateway.sol";
1616

17-
/// @notice V1: Roles Model implementation of upgradable ERC20 token.
18-
/// This to be used as the initial implementation of UUPS proxy.
19-
/// If an upgrade from V0 to V1 is needed,
20-
/// AnlogTokenV2Upgrade should be used instead.
17+
/// @notice V2: Teleportable Wrapped Analog ERC20 token.
18+
/// @custom:oz-upgrades-from AnlogTokenV1
2119
contract AnlogTokenV2 is
2220
Initializable,
2321
ERC20Upgradeable,

0 commit comments

Comments
 (0)