From 51c1653a06bfbbaedca8035f03bf9fec7a1e93bd Mon Sep 17 00:00:00 2001 From: Augustus Chang Date: Thu, 11 May 2023 09:42:59 -0400 Subject: [PATCH 1/2] add fixes --- contracts/package.json | 1 + contracts/src/libraries.cairo | 1 + contracts/src/libraries/mocks.cairo | 2 + .../mocks/mock_non_upgradeable.cairo | 11 +++ .../libraries/mocks/mock_upgradeable.cairo | 21 ++++++ contracts/test/upgradeable.test.ts | 72 +++++++++++++++++++ 6 files changed, 108 insertions(+) create mode 100644 contracts/src/libraries/mocks.cairo create mode 100644 contracts/src/libraries/mocks/mock_non_upgradeable.cairo create mode 100644 contracts/src/libraries/mocks/mock_upgradeable.cairo create mode 100644 contracts/test/upgradeable.test.ts diff --git a/contracts/package.json b/contracts/package.json index 131fc7d74..228680265 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -4,6 +4,7 @@ "description": "", "main": "index.js", "scripts": { + "compile:cairo": "scarb --profile release build", "compile:solidity": "hardhat compile", "test": "hardhat --network localhost test" }, diff --git a/contracts/src/libraries.cairo b/contracts/src/libraries.cairo index 56b6fa85a..2fd5588af 100644 --- a/contracts/src/libraries.cairo +++ b/contracts/src/libraries.cairo @@ -2,3 +2,4 @@ mod ownable; mod access_control; mod token; mod upgradeable; +mod mocks; diff --git a/contracts/src/libraries/mocks.cairo b/contracts/src/libraries/mocks.cairo new file mode 100644 index 000000000..9a58bab7d --- /dev/null +++ b/contracts/src/libraries/mocks.cairo @@ -0,0 +1,2 @@ +mod mock_upgradeable; +mod mock_non_upgradeable; diff --git a/contracts/src/libraries/mocks/mock_non_upgradeable.cairo b/contracts/src/libraries/mocks/mock_non_upgradeable.cairo new file mode 100644 index 000000000..c1724b22a --- /dev/null +++ b/contracts/src/libraries/mocks/mock_non_upgradeable.cairo @@ -0,0 +1,11 @@ +#[contract] +mod MockNonUpgradeable { + + #[constructor] + fn constructor() {} + + #[view] + fn bar() -> bool { + true + } +} diff --git a/contracts/src/libraries/mocks/mock_upgradeable.cairo b/contracts/src/libraries/mocks/mock_upgradeable.cairo new file mode 100644 index 000000000..eb6da21d9 --- /dev/null +++ b/contracts/src/libraries/mocks/mock_upgradeable.cairo @@ -0,0 +1,21 @@ +use starknet::class_hash::ClassHash; + +#[contract] +mod MockUpgradeable { + use starknet::class_hash::ClassHash; + + use chainlink::libraries::upgradeable::Upgradeable; + + #[constructor] + fn constructor() {} + + #[view] + fn foo() -> bool { + true + } + + #[external] + fn upgrade(new_impl: ClassHash) { + Upgradeable::upgrade(new_impl) + } +} diff --git a/contracts/test/upgradeable.test.ts b/contracts/test/upgradeable.test.ts new file mode 100644 index 000000000..9b576b0eb --- /dev/null +++ b/contracts/test/upgradeable.test.ts @@ -0,0 +1,72 @@ +import { expect } from 'chai' +import { starknet } from 'hardhat' +import { StarknetContract, Account, StarknetContractFactory } from 'hardhat/types/runtime' +import { account, expectInvokeError, expectSuccessOrDeclared, expectCallError } from '@chainlink/starknet' +import { ec } from 'starknet' + +describe('upgradeable', function () { + this.timeout(1_000_000) + + let owner: Account + let nonOwner: Account + const opts = account.makeFunderOptsFromEnv() + const funder = new account.Funder(opts) + let upgradeableFactory: StarknetContractFactory + let nonUpgradeableFactory: StarknetContractFactory + let upgradeableContract: StarknetContract + + + // should be beforeeach, but that'd be horribly slow. Just remember that the tests are not idempotent + before(async function () { + owner = await starknet.OpenZeppelinAccount.createAccount() + + await funder.fund([ + { account: owner.address, amount: 1e21 }, + ]) + await owner.deployAccount() + + upgradeableFactory = await starknet.getContractFactory('mock_upgradeable') + await expectSuccessOrDeclared(owner.declare(upgradeableFactory, { maxFee: 1e20 })) + + nonUpgradeableFactory = await starknet.getContractFactory('mock_non_upgradeable') + await expectSuccessOrDeclared(owner.declare(nonUpgradeableFactory, { maxFee: 1e20 })) + }) + + describe('Upgrade contract', () => { + + beforeEach(async () => { + upgradeableContract = await owner.deploy(upgradeableFactory) + }) + + it('succeeds if class hash exists', async () => { + expect((await upgradeableContract.call('foo')).response).to.equal(true) + + const newClassHash = await nonUpgradeableFactory.getClassHash() + + await owner.invoke( + upgradeableContract, + 'upgrade', + { + new_impl: newClassHash + } + ) + + // should error because the contract has upgraded and no longer has foo function + await expectCallError(upgradeableContract.call('foo')) + + const afterUpgradeContract = nonUpgradeableFactory.getContractAt(upgradeableContract.address) + expect((await afterUpgradeContract.call('bar')).response).to.equal(true) + }) + + it('reverts if new implementation class hash does not exist', async () => { + expect((await upgradeableContract.call('foo')).response).to.equal(true) + const nonExistentClassHash = ec.starkCurve.pedersen(777, 777) + + // class hash is not declared: StarknetErrorCode.UNDECLARED_CLASS + await expectInvokeError( + owner.invoke(upgradeableContract, 'upgrade', { new_impl: nonExistentClassHash }) + ) + + }) + }) +}) From ac3de338eede7608e29d1a295184db03aadffb4c Mon Sep 17 00:00:00 2001 From: Augustus <14297860+augustbleeds@users.noreply.github.com> Date: Fri, 12 May 2023 10:13:30 -0400 Subject: [PATCH 2/2] Update package.json --- contracts/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/package.json b/contracts/package.json index 228680265..131fc7d74 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -4,7 +4,6 @@ "description": "", "main": "index.js", "scripts": { - "compile:cairo": "scarb --profile release build", "compile:solidity": "hardhat compile", "test": "hardhat --network localhost test" },