Skip to content

Commit

Permalink
transitory upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx committed May 13, 2024
1 parent 2bc8040 commit ea128e2
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 5 deletions.
60 changes: 60 additions & 0 deletions contracts/vesting/VestingWalletRecoveryLight.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/StorageSlot.sol";

/**
* This contract is designed for recovering the in case the beneficiary was lost.
*/
contract VestingWalletRecoveryLight {
/// Storage
// Initializable
uint8 private _initialized;
bool private _initializing;
// ContextUpgradeable
uint256[50] private __gap_1;
// OwnableUpgradeable
address private _owner;
uint256[49] private __gap_2;
// UUPSUpgradeable
uint256[50] private __gap_3;
// ERC1967UpgradeUpgradeable
uint256[50] private __gap_4;
// VestingWallerV1
mapping (address => uint256) private _released;
address private _beneficiary;
uint256 private _start;
uint256 private _cliff;
uint256 private _duration;

/// Constants and Events
// ERC1967UpgradeUpgradeable
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
event Upgraded(address indexed implementation);

function changeOwnerAndUpgrade(address newBeneficiary, address newImplementation) external {
// change ownership
_beneficiary = newBeneficiary;

// ERC1967Upgrade._setImplementation
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
emit Upgraded(newImplementation);
}

function proxiableUUID() external pure returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}


function upgradeTo(address) external pure {
revert();
}

function upgradeToAndCall(address, bytes memory) external pure {
revert();
}
}
22 changes: 17 additions & 5 deletions test/vesting/VestingWallet.recovery.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const hre = require('hardhat');
const { ethers } = hre;
const { expect } = require('chai');
const { prepare, deployUpgradeable, performUpgrade, deploy, attach } = require('../fixture');
const { prepare, deployUpgradeable, performUpgrade } = require('../fixture');
const utils = require('../../scripts/utils');

const allocation = {
Expand All @@ -17,6 +17,7 @@ describe('VestingWallet ', function () {
describe('vesting with admin', function () {
beforeEach(async function () {
allocation.beneficiary = this.accounts.user1.address;
allocation.newBeneficiary = this.accounts.user2.address;
allocation.owner = this.accounts.admin.address;

this.vesting = await deployUpgradeable(
Expand All @@ -37,7 +38,7 @@ describe('VestingWallet ', function () {
);
});

it('perform recovery', async function () {
it('perform recovery (full upgrade)', async function () {
this.vesting = await performUpgrade(hre, this.vesting, 'VestingWalletRecovery', {
unsafeAllow: 'delegatecall',
});
Expand All @@ -47,15 +48,26 @@ describe('VestingWallet ', function () {
.to.be.revertedWith(`Ownable: caller is not the owner`);

// authorized
await expect(this.vesting.connect(this.accounts.admin).updateBeneficiary(this.accounts.user2.address))
.to.emit(this.vesting, 'BeneficiaryUpdate').withArgs(this.accounts.user2.address);
await expect(this.vesting.connect(this.accounts.admin).updateBeneficiary(allocation.newBeneficiary))
.to.emit(this.vesting, 'BeneficiaryUpdate').withArgs(allocation.newBeneficiary);
});

it('perform recovery (transitory upgrade)', async function () {
const implementation = await hre.upgrades.erc1967.getImplementationAddress(this.vesting.address);

await performUpgrade(hre, this.vesting, 'VestingWalletRecoveryLight', {
call: { fn: 'changeOwnerAndUpgrade', args: [allocation.newBeneficiary, implementation] },
unsafeAllow: 'delegatecall'
});
});

afterEach(async function () {
await Promise.all([this.vesting.start(), this.vesting.cliff(), this.vesting.duration(), this.vesting.beneficiary(), this.vesting.owner()]).then(
([start, cliff, duration, beneficiary, owner]) => {
expect(start).to.be.equal(allocation.start);
expect(cliff).to.be.equal(allocation.cliff);
expect(duration).to.be.equal(allocation.duration);
expect(beneficiary).to.be.equal(this.accounts.user2.address);
expect(beneficiary).to.be.equal(allocation.newBeneficiary);
expect(owner).to.be.equal(allocation.owner);
}
);
Expand Down

0 comments on commit ea128e2

Please sign in to comment.