Skip to content

Commit a6fc96f

Browse files
committed
tests: add more tests around upgrades
1 parent 7cd2974 commit a6fc96f

File tree

4 files changed

+206
-6
lines changed

4 files changed

+206
-6
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"@nomiclabs/hardhat-ethers": "^2.0.0",
2323
"@nomiclabs/hardhat-etherscan": "^2.0.0",
2424
"@nomiclabs/hardhat-waffle": "^2.0.0",
25-
"@openzeppelin/contracts": "^3.2.1-solc-0.7",
25+
"@openzeppelin/contracts": "^3.3.0-solc-0.7",
2626
"@statechannels/nitro-protocol": "^0.11.2",
2727
"@tenderly/hardhat-tenderly": "^1.0.3",
2828
"@typechain/ethers-v5": "^1.0.0",

test/lib/fixtures.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,9 @@ export class NetworkFixture {
6161
await staking.connect(deployer).setSlasher(slasherAddress, true)
6262
await grt.connect(deployer).addMinter(rewardsManager.address)
6363
await gns.connect(deployer).approveAll()
64-
await rewardsManager.connect(deployer).setIssuanceRate(deployment.defaults.rewards.issuanceRate)
6564

6665
// Unpause the protocol
6766
await controller.connect(deployer).setPaused(false)
68-
await controller.connect(deployer).setPartialPaused(false)
6967

7068
return {
7169
controller,
@@ -77,6 +75,7 @@ export class NetworkFixture {
7775
staking,
7876
rewardsManager,
7977
serviceRegistry,
78+
proxyAdmin,
8079
}
8180
}
8281

test/upgrade/admin.test.ts

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { expect } from 'chai'
2+
import hre, { network } from 'hardhat'
3+
import '@nomiclabs/hardhat-ethers'
4+
5+
import { GraphProxy } from '../../build/typechain/contracts/GraphProxy'
6+
import { Curation } from '../../build/typechain/contracts/Curation'
7+
import { GraphProxyAdmin } from '../../build/typechain/contracts/GraphProxyAdmin'
8+
import { Staking } from '../../build/typechain/contracts/Staking'
9+
10+
import * as deployment from '../lib/deployment'
11+
import { NetworkFixture } from '../lib/fixtures'
12+
import { getAccounts, Account } from '../lib/testHelpers'
13+
14+
import { getContractAt } from '../../cli/network'
15+
16+
const { ethers } = hre
17+
const { AddressZero } = ethers.constants
18+
19+
describe('Upgrades', () => {
20+
let me: Account
21+
let governor: Account
22+
23+
let fixture: NetworkFixture
24+
25+
let proxyAdmin: GraphProxyAdmin
26+
let curation: Curation
27+
let staking: Staking
28+
let stakingProxy: GraphProxy
29+
30+
before(async function () {
31+
;[me, governor] = await getAccounts()
32+
33+
fixture = new NetworkFixture()
34+
;({ proxyAdmin, staking, curation } = await fixture.load(governor.signer))
35+
stakingProxy = getContractAt('GraphProxy', staking.address, governor.signer) as GraphProxy
36+
37+
// Give some funds to the indexer and approve staking contract to use funds on indexer behalf
38+
})
39+
40+
beforeEach(async function () {
41+
await fixture.setUp()
42+
})
43+
44+
afterEach(async function () {
45+
await fixture.tearDown()
46+
})
47+
48+
describe('GraphProxyAdmin & GraphProxy', function () {
49+
describe('getters', function () {
50+
describe('admin()', function () {
51+
it('should get the proxy admin of a proxy contract', async function () {
52+
const proxyAdminAddress = await proxyAdmin.getProxyAdmin(staking.address)
53+
expect(proxyAdmin.address).eq(proxyAdminAddress)
54+
})
55+
56+
it('reject get admin from other than the ProxyAdmin', async function () {
57+
await expect(stakingProxy.admin()).revertedWith(
58+
"function selector was not recognized and there's no fallback function",
59+
)
60+
})
61+
})
62+
63+
describe('implementation()', function () {
64+
it('should get implementation only from ProxyAdmin', async function () {
65+
const implementationAddress = await proxyAdmin.getProxyImplementation(staking.address)
66+
expect(implementationAddress).not.eq(AddressZero)
67+
})
68+
69+
it('reject get implementation from other than the ProxyAdmin', async function () {
70+
await expect(stakingProxy.implementation()).revertedWith(
71+
"function selector was not recognized and there's no fallback function",
72+
)
73+
})
74+
})
75+
76+
describe('pendingImplementation()', function () {
77+
it('should get pending implementation only from ProxyAdmin', async function () {
78+
const pendingImplementationAddress = await proxyAdmin.getProxyPendingImplementation(
79+
staking.address,
80+
)
81+
expect(pendingImplementationAddress).eq(AddressZero)
82+
})
83+
84+
it('reject get pending implementation from other than the ProxyAdmin', async function () {
85+
await expect(stakingProxy.pendingImplementation()).revertedWith(
86+
"function selector was not recognized and there's no fallback function",
87+
)
88+
})
89+
})
90+
})
91+
92+
describe('upgrade', function () {
93+
it('should be able to upgrade contract', async function () {
94+
// Get some other implementation to use just for the purpose of the test
95+
const oldImplementationAddress = await proxyAdmin.getProxyImplementation(staking.address)
96+
const newImplementationAddress = await proxyAdmin.getProxyImplementation(curation.address)
97+
98+
const stakingProxy = getContractAt('GraphProxy', staking.address, governor.signer)
99+
100+
// Upgrade the Staking:Proxy to a new implementation
101+
const tx1 = proxyAdmin
102+
.connect(governor.signer)
103+
.upgrade(staking.address, newImplementationAddress)
104+
await expect(tx1)
105+
.emit(stakingProxy, 'PendingImplementationUpdated')
106+
.withArgs(AddressZero, newImplementationAddress)
107+
108+
const tx2 = proxyAdmin
109+
.connect(governor.signer)
110+
.acceptProxy(newImplementationAddress, staking.address)
111+
await expect(tx2)
112+
.emit(stakingProxy, 'ImplementationUpdated')
113+
.withArgs(oldImplementationAddress, newImplementationAddress)
114+
115+
// Implementation should be the new one
116+
expect(await proxyAdmin.getProxyImplementation(curation.address)).eq(
117+
newImplementationAddress,
118+
)
119+
})
120+
121+
it('reject upgrade if not the governor of the ProxyAdmin', async function () {
122+
const newImplementationAddress = await proxyAdmin.getProxyImplementation(curation.address)
123+
124+
// Upgrade the Staking:Proxy to a new implementation
125+
const tx = proxyAdmin.connect(me.signer).upgrade(staking.address, newImplementationAddress)
126+
await expect(tx).revertedWith('Only Governor can call')
127+
})
128+
129+
it('reject upgrade if not using the ProxyAdmin', async function () {
130+
const newImplementationAddress = await proxyAdmin.getProxyImplementation(curation.address)
131+
132+
// Due to the transparent proxy we should not be able to upgrade from other than the proxy admin
133+
const tx = stakingProxy.connect(governor.signer).upgradeTo(newImplementationAddress)
134+
await expect(tx).revertedWith(
135+
"function selector was not recognized and there's no fallback function",
136+
)
137+
})
138+
})
139+
140+
describe('acceptUpgrade', function () {
141+
it('reject accept upgrade if not using the ProxyAdmin', async function () {
142+
// Due to the transparent proxy we should not be able to accept upgrades from other than the proxy admin
143+
const tx = stakingProxy.connect(governor.signer).acceptUpgrade()
144+
await expect(tx).revertedWith(
145+
"function selector was not recognized and there's no fallback function",
146+
)
147+
})
148+
})
149+
150+
describe('acceptProxy', function () {
151+
it('reject accept proxy if not using the ProxyAdmin', async function () {
152+
const newImplementationAddress = await proxyAdmin.getProxyImplementation(curation.address)
153+
const implementation = getContractAt('Curation', newImplementationAddress, governor.signer)
154+
155+
// Start an upgrade to a new implementation
156+
await proxyAdmin.connect(governor.signer).upgrade(staking.address, newImplementationAddress)
157+
158+
// Try to accept the proxy directly from the implementation, this should not work, only from the ProxyAdmin
159+
const tx = implementation.connect(governor.signer).acceptProxy(staking.address)
160+
await expect(tx).revertedWith('Caller must be the proxy admin')
161+
})
162+
})
163+
164+
describe('changeProxyAdmin', function () {
165+
it('should set the proxy admin of a proxy', async function () {
166+
const otherProxyAdmin = await deployment.deployProxyAdmin(governor.signer)
167+
168+
await proxyAdmin
169+
.connect(governor.signer)
170+
.changeProxyAdmin(staking.address, otherProxyAdmin.address)
171+
expect(await otherProxyAdmin.getProxyAdmin(staking.address)).eq(otherProxyAdmin.address)
172+
173+
// Should not find the change admin function in the proxy due to transparent proxy
174+
// as this ProxyAdmin is not longer the owner
175+
const tx = proxyAdmin
176+
.connect(governor.signer)
177+
.changeProxyAdmin(staking.address, otherProxyAdmin.address)
178+
await expect(tx).revertedWith(
179+
"function selector was not recognized and there's no fallback function",
180+
)
181+
})
182+
183+
it('reject change admin if not the governor of the ProxyAdmin', async function () {
184+
const otherProxyAdmin = await deployment.deployProxyAdmin(governor.signer)
185+
186+
const tx = proxyAdmin
187+
.connect(me.signer)
188+
.changeProxyAdmin(staking.address, otherProxyAdmin.address)
189+
await expect(tx).revertedWith('Only Governor can call')
190+
})
191+
192+
it('reject change admin if not using the ProxyAdmin', async function () {
193+
// Due to the transparent proxy we should not be able to set admin from other than the proxy admin
194+
const tx = stakingProxy.connect(governor.signer).setAdmin(me.address)
195+
await expect(tx).revertedWith(
196+
"function selector was not recognized and there's no fallback function",
197+
)
198+
})
199+
})
200+
})
201+
})

0 commit comments

Comments
 (0)