Skip to content

Commit

Permalink
feat: add migration to upgrade contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
fedealconada committed Jan 23, 2024
1 parent d858d95 commit 4393e6a
Show file tree
Hide file tree
Showing 15 changed files with 310 additions and 37 deletions.
2 changes: 1 addition & 1 deletion script/deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "../src/viewers/KYCViewer.sol";
import "../src/interfaces/IKintoWallet.sol";
import "../src/wallet/KintoWalletFactory.sol";
import "../src/paymasters/SponsorPaymaster.sol";
import {KintoWalletV3 as KintoWallet} from "../src/wallet/KintoWallet.sol";
import "../src/wallet/KintoWallet.sol";

import "../test/helpers/Create2Helper.sol";
import "../test/helpers/ArtifactsReader.sol";
Expand Down
6 changes: 6 additions & 0 deletions script/migrations/10-upgrade_wallet_v3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ contract KintoMigration10DeployScript is Create2Helper, ArtifactsReader {
console.log(string.concat('"KintoWalletV3-impl": "', vm.toString(address(_kintoWalletImpl)), '"'));
}
}

contract KintoWalletV3 is KintoWallet {
constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _appRegistry)
KintoWallet(_entryPoint, _kintoID, _appRegistry)
{}
}
4 changes: 4 additions & 0 deletions script/migrations/11-upgrade_paymasterv2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@ contract KintoMigration11DeployScript is Create2Helper, ArtifactsReader {
console.log(string.concat('"SponsorPaymasterV2-impl": "', vm.toString(address(_paymasterImpl)), '"'));
}
}

contract SponsorPaymasterV2 is SponsorPaymaster {
constructor(IEntryPoint __entryPoint) SponsorPaymaster(__entryPoint) {}
}
4 changes: 4 additions & 0 deletions script/migrations/13-upgrade_id_paymasterv3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,7 @@ contract KintoMigration13DeployScript is Create2Helper, ArtifactsReader {
console.log(string.concat('"KintoIDV3-impl": "', vm.toString(address(_kintoIDImpl)), '"'));
}
}

contract SponsorPaymasterV3 is SponsorPaymaster {
constructor(IEntryPoint __entryPoint) SponsorPaymaster(__entryPoint) {}
}
10 changes: 10 additions & 0 deletions script/migrations/20-faucet_fund_fix.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,13 @@ contract KintoMigration20DeployScript is Create2Helper, ArtifactsReader {
console.log(string.concat('"KintoWalletFactoryV6-impl": "', vm.toString(address(_factoryImpl)), '"'));
}
}

contract KintoWalletFactoryV6 is KintoWalletFactory {
constructor(IKintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {}
}

contract KintoWalletV3 is KintoWallet {
constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _appRegistry)
KintoWallet(_entryPoint, _kintoID, _appRegistry)
{}
}
209 changes: 209 additions & 0 deletions script/migrations/21-multiple_upgrade.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "../../src/wallet/KintoWalletFactory.sol";
import "../../src/wallet/KintoWallet.sol";
import "../../src/apps/KintoAppRegistry.sol";
import "../../src/paymasters/SponsorPaymaster.sol";
import "../../src/viewers/KYCViewer.sol";
import "../../src/KintoID.sol";

import "../../test/helpers/Create2Helper.sol";
import "../../test/helpers/ArtifactsReader.sol";
import "../../test/helpers/UUPSProxy.sol";
import "../../test/helpers/UserOp.sol";

import "forge-std/Script.sol";
import "forge-std/console.sol";

contract KintoMigration21DeployScript is Create2Helper, ArtifactsReader, UserOp {
using ECDSAUpgradeable for bytes32;

KintoWalletFactory _walletFactory;
uint256 deployerPrivateKey;

// NOTE: this migration must be run from the ledger admin
function run() public {
console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid));

deployerPrivateKey = vm.envUint("PRIVATE_KEY");

// execute this script with the with the ledger
console.log("Executing from address", msg.sender);

// set wallet factory
_walletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory")));

// deploy contracts
// address _paymasterImpl = upgradePaymaster();
// console.log(string.concat("SponsorPaymasterV4-impl: ", vm.toString(_paymasterImpl)));

// address _registryImpl = upgradeRegistry();
// console.log(string.concat("KintoAppRegistryV3-impl: ", vm.toString(_registryImpl)));

// address _walletImpl = upgradeWallet();
// console.log(string.concat("KintoWalletV4-impl: ", vm.toString(_walletImpl)));

// address _factoryImpl = upgradeFactory();
// console.log(string.concat("KintoWalletFactoryV7-impl: ", vm.toString(_factoryImpl)));

(address _kycViewerImpl, address _kycViewerProxy) = upgradeKYCViewer();
console.log(string.concat("KYCViewerV2-impl: ", vm.toString(_kycViewerImpl)));
console.log(string.concat("KYCViewerV2: ", vm.toString(_kycViewerProxy)));

// writes the addresses to a file
// console.log("TODO: Manually add these new addresses to the artifacts file");
// console.log(string.concat("SponsorPaymasterV4-impl: ", vm.toString(address(_paymasterImpl))));
// console.log(string.concat("KintoAppRegistryV3-impl: ", vm.toString(address(_registryImpl))));
// console.log(string.concat("KintoWalletV4-impl: ", vm.toString(address(_walletImpl))));
// console.log(string.concat("KintoWalletFactoryV7-impl: ", vm.toString(address(_factoryImpl))));
// console.log(string.concat("KYCViewerV2-impl: ", vm.toString(address(_kycViewerImpl))));
// console.log(string.concat("KYCViewerV2: ", vm.toString(address(_kycViewerProxy))));
}

function upgradePaymaster() public returns (address _paymasterImpl) {
// (1). deploy new paymaster implementation via wallet factory
address paymasterProxy = _getChainDeployment("SponsorPaymaster");
require(paymasterProxy != address(0), "Need to execute main deploy script first");

bytes memory bytecode =
abi.encodePacked(type(SponsorPaymasterV4).creationCode, abi.encode(_getChainDeployment("EntryPoint")));

vm.broadcast(deployerPrivateKey);
_paymasterImpl = _walletFactory.deployContract(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32(0));

// (3). upgrade paymaster to new implementation
// vm.broadcast(); // requires LEDGER_ADMIN
vm.prank(vm.envAddress("LEDGER_ADMIN"));
SponsorPaymaster(payable(paymasterProxy)).upgradeTo(address(_paymasterImpl));
}

function upgradeRegistry() public returns (address _registryImpl) {
// (1). deploy new kinto registry implementation via wallet factory
address registryProxy = _getChainDeployment("KintoAppRegistry");
require(registryProxy != address(0), "Need to execute main deploy script first");

bytes memory bytecode =
abi.encodePacked(type(KintoAppRegistryV3).creationCode, abi.encode(address(_walletFactory)));

vm.broadcast(deployerPrivateKey);
_registryImpl =
_walletFactory.deployContract{value: 0}(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32("1"));

// (2). upgrade registry to new implementation
_upgradeTo(payable(registryProxy), _registryImpl, deployerPrivateKey);
}

function upgradeWallet() public returns (address _walletImpl) {
// (1). deploy new kinto wallet implementation via wallet factory
bytes memory bytecode = abi.encodePacked(
type(KintoWalletV4).creationCode,
abi.encode(
_getChainDeployment("EntryPoint"),
IKintoID(_getChainDeployment("KintoID")),
IKintoAppRegistry(_getChainDeployment("KintoAppRegistry"))
)
);
vm.broadcast(deployerPrivateKey);
_walletImpl = _walletFactory.deployContract(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32(0));

// (2). upgrade all implementations
// vm.broadcast(); // requires LEDGER_ADMIN
vm.prank(vm.envAddress("LEDGER_ADMIN"));
_walletFactory.upgradeAllWalletImplementations(IKintoWallet(_walletImpl));
}

function upgradeFactory() public returns (address _factoryImpl) {
// (1). deploy new kinto factory
address factoryProxy = _getChainDeployment("KintoWalletFactory");
require(factoryProxy != address(0), "Need to execute main deploy script first");

address _walletImpl = _getChainDeployment("KintoWalletV3-impl");
require(_walletImpl != address(0), "Need to deploy the new wallet first");

bytes memory bytecode = abi.encodePacked(
type(KintoWalletFactoryV7).creationCode,
abi.encode(_walletImpl) // Encoded constructor arguments
);

vm.broadcast(deployerPrivateKey);
_factoryImpl = _walletFactory.deployContract(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32(0));

// (2). upgrade factory to new implementation
// vm.broadcast(); // requires LEDGER_ADMIN
vm.prank(vm.envAddress("LEDGER_ADMIN"));
KintoWalletFactory(payable(factoryProxy)).upgradeTo(address(_factoryImpl));
}

function upgradeKYCViewer() public returns (address _kycViewerImpl, address _proxy) {
// make sure kyc viewer proxy is not already deployed
address viewerProxy = payable(_getChainDeployment("KYCViewer"));
require(viewerProxy == address(0), "KYCViewer proxy is already deployed");

// (1). deploy KYCViewerV2 implementation
bytes memory bytecode = abi.encodePacked(type(KYCViewerV2).creationCode, abi.encode(_walletFactory));

vm.broadcast(deployerPrivateKey);
_kycViewerImpl = _walletFactory.deployContract(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32(0));

// (2). deploy KYCViewerV2 proxy
bytecode = abi.encodePacked(type(UUPSProxy).creationCode, abi.encode(address(_kycViewerImpl), ""));

vm.broadcast(deployerPrivateKey);
_proxy = _walletFactory.deployContract(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32(0));

_initialize(_proxy, deployerPrivateKey);
}

function _upgradeTo(address _proxy, address _newImpl, uint256 _signerPk) internal {
address payable _from = payable(_getChainDeployment("KintoWallet-admin"));

// prep upgradeTo user op
uint256 nonce = IKintoWallet(_from).getNonce();
uint256[] memory privateKeys = new uint256[](1);
privateKeys[0] = _signerPk;
UserOperation[] memory userOps = new UserOperation[](1);
userOps[0] = _createUserOperation(
block.chainid,
_from,
_proxy,
0,
nonce,
privateKeys,
abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, address(_newImpl)),
_getChainDeployment("SponsorPaymaster")
);

vm.broadcast(deployerPrivateKey);
IEntryPoint(_getChainDeployment("EntryPoint")).handleOps(userOps, payable(vm.addr(_signerPk)));
}

function _initialize(address _proxy, uint256 _signerPk) internal {
address payable _from = payable(_getChainDeployment("KintoWallet-admin"));

// fund _proxy in the paymaster
ISponsorPaymaster _paymaster = ISponsorPaymaster(_getChainDeployment("SponsorPaymaster"));
vm.broadcast(deployerPrivateKey);
_paymaster.addDepositFor{value: 0.00000001 ether}(_proxy);
assertEq(_paymaster.balances(_proxy), 0.00000001 ether);

// prep upgradeTo user op
uint256 nonce = IKintoWallet(_from).getNonce();
uint256[] memory privateKeys = new uint256[](1);
privateKeys[0] = _signerPk;
UserOperation[] memory userOps = new UserOperation[](1);
userOps[0] = _createUserOperation(
block.chainid,
_from,
_proxy,
0,
nonce,
privateKeys,
abi.encodeWithSelector(KYCViewer.initialize.selector),
_getChainDeployment("SponsorPaymaster")
);

vm.broadcast(deployerPrivateKey);
IEntryPoint(_getChainDeployment("EntryPoint")).handleOps(userOps, payable(vm.addr(_signerPk)));
}
}
4 changes: 4 additions & 0 deletions src/apps/KintoAppRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,7 @@ contract KintoAppRegistry is
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
}
}

contract KintoAppRegistryV3 is KintoAppRegistry {
constructor(IKintoWalletFactory _walletFactory) KintoAppRegistry(_walletFactory) {}
}
6 changes: 1 addition & 5 deletions src/paymasters/SponsorPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,6 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen
}
}

contract SponsorPaymasterV2 is SponsorPaymaster {
constructor(IEntryPoint __entryPoint) SponsorPaymaster(__entryPoint) {}
}

contract SponsorPaymasterV3 is SponsorPaymaster {
contract SponsorPaymasterV4 is SponsorPaymaster {
constructor(IEntryPoint __entryPoint) SponsorPaymaster(__entryPoint) {}
}
4 changes: 4 additions & 0 deletions src/viewers/KYCViewer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,7 @@ contract KYCViewer is Initializable, UUPSUpgradeable, OwnableUpgradeable, IKYCVi
return _address;
}
}

contract KYCViewerV2 is KYCViewer {
constructor(address _kintoWalletFactory) KYCViewer(_kintoWalletFactory) {}
}
2 changes: 1 addition & 1 deletion src/wallet/KintoWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto
}

// Upgradeable version of KintoWallet
contract KintoWalletV3 is KintoWallet {
contract KintoWalletV4 is KintoWallet {
constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _appRegistry)
KintoWallet(_entryPoint, _kintoID, _appRegistry)
{}
Expand Down
8 changes: 6 additions & 2 deletions src/wallet/KintoWalletFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl
* @param target The target address
*/
function sendMoneyToAccount(address target) external payable override {
require(owner() == msg.sender || kintoID.isKYC(msg.sender), "KYC required");
require(
owner() == msg.sender || kintoID.isKYC(msg.sender)
|| IAccessControl(address(kintoID)).hasRole(kintoID.KYC_PROVIDER_ROLE(), msg.sender),
"KYC or Provider role required"
);
(bool sent,) = target.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}
Expand Down Expand Up @@ -293,6 +297,6 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl
}
}

contract KintoWalletFactoryV6 is KintoWalletFactory {
contract KintoWalletFactoryV7 is KintoWalletFactory {
constructor(IKintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {}
}
12 changes: 6 additions & 6 deletions test/KYCViewer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import "./helpers/UserOp.sol";
import "./helpers/UUPSProxy.sol";
import {AATestScaffolding} from "./helpers/AATestScaffolding.sol";

contract KYCViewerV2 is KYCViewer {
contract KYCViewerUpgraded is KYCViewer {
function newFunction() external pure returns (uint256) {
return 1;
}
Expand All @@ -26,9 +26,9 @@ contract KYCViewerTest is UserOp, AATestScaffolding {

UUPSProxy _proxyViewer;
KYCViewer _implkycViewer;
KYCViewerV2 _implkycViewerV2;
KYCViewerUpgraded _implKYCViewerUpgraded;
KYCViewer _kycViewer;
KYCViewerV2 _kycViewer2;
KYCViewerUpgraded _kycViewer2;

function setUp() public {
vm.chainId(_chainID);
Expand Down Expand Up @@ -59,16 +59,16 @@ contract KYCViewerTest is UserOp, AATestScaffolding {

function testOwnerCanUpgradeViewer() public {
vm.startPrank(_owner);
KYCViewerV2 _implementationV2 = new KYCViewerV2(address(_walletFactory));
KYCViewerUpgraded _implementationV2 = new KYCViewerUpgraded(address(_walletFactory));
_kycViewer.upgradeTo(address(_implementationV2));
// re-wrap the _proxy
_kycViewer2 = KYCViewerV2(address(_kycViewer));
_kycViewer2 = KYCViewerUpgraded(address(_kycViewer));
assertEq(_kycViewer2.newFunction(), 1);
vm.stopPrank();
}

function test_RevertWhen_OthersCannotUpgradeFactory() public {
KYCViewerV2 _implementationV2 = new KYCViewerV2(address(_walletFactory));
KYCViewerUpgraded _implementationV2 = new KYCViewerUpgraded(address(_walletFactory));
vm.expectRevert("only owner");
_kycViewer.upgradeTo(address(_implementationV2));
}
Expand Down
Loading

0 comments on commit 4393e6a

Please sign in to comment.