From a9a73f03477d4ef152b2052702c88d51b55f8ef3 Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 13 Jun 2023 09:02:56 +0200 Subject: [PATCH 1/5] feat: generic UP and Owner Deployer --- .../UniversalProfileAndOwnerDeployer.sol | 106 ++++++++++++++++++ contracts/UniversalProfileDeployer.sol | 1 - 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 contracts/UniversalProfileAndOwnerDeployer.sol diff --git a/contracts/UniversalProfileAndOwnerDeployer.sol b/contracts/UniversalProfileAndOwnerDeployer.sol new file mode 100644 index 0000000..d7f16d0 --- /dev/null +++ b/contracts/UniversalProfileAndOwnerDeployer.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {_LSP6KEY_ADDRESSPERMISSIONS_PERMISSIONS_PREFIX, _PERMISSION_CHANGEOWNER, _PERMISSION_EDITPERMISSIONS, ALL_REGULAR_PERMISSIONS} from "@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6Constants.sol"; +import {LSP6Utils} from '@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6Utils.sol'; +import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; +import {ILSP14Ownable2Step} from '@lukso/lsp-smart-contracts/contracts/LSP14Ownable2Step/ILSP14Ownable2Step.sol'; + + + +// 1st step: deploy the universal profile contract + +// 2nd step: deploy the owner contract (the owner that will be set as the owner of the universal profile contract at the end of the deployment process) + +// 3rd step: call firstOwner that will be in charge of making the necessary calls that could be needed to set up the universal profile contract +// in this case FirstOwner will be in charge of deploying UPs and KeyManagers and setting up the permissions (we could use delegatecall to do this) + + +contract UniversalProfileAndOwnerDeployer { + + struct UniversalProfileDeployment { + uint256 value; + bytes32 salt; + bytes byteCode; + } + + struct OwnerDeployment { + uint256 value; + bytes32 salt; + bytes byteCode; // owner contract bytecode + constructor params to be appended to the constructor before the universal profile address + bool appendUniversalProfileAddress; // will append the universal profile address to the constructor params if true + the extraConstructorParams + bytes extraConstructorParams; // params to be appended to the constructor after the universal profile address + } + + + function deployUniversalProfileAndOwner(UniversalProfileDeployment calldata universalProfileDeployment, OwnerDeployment calldata ownerDeployment, address universalProfileFirstOwner, bytes calldata calldaToFirstOwner) + public + payable + returns (address universalProfile, address owner ) + { + if(msg.value < universalProfileDeployment.value + ownerDeployment.value ) { + revert("UniversalProfileDeployer: insufficient funds"); + } + + bytes32 universalProfileGeneratedSalt = keccak256(abi.encode(universalProfileDeployment.salt,ownerDeployment,universalProfileFirstOwner, calldaToFirstOwner)); + + universalProfile = Create2.deploy(universalProfileDeployment.value, universalProfileGeneratedSalt, abi.encodePacked(universalProfileDeployment.byteCode, abi.encode(universalProfileFirstOwner))); + + // if appendUniversalProfileAddress is true, the universal profile address + extraConstructorParams will be appended to the constructor params + bytes memory ownerByteCode = ownerDeployment.appendUniversalProfileAddress ? abi.encodePacked(ownerDeployment.byteCode, abi.encode(universalProfile),ownerDeployment.extraConstructorParams) : ownerDeployment.byteCode; + + // here owner refers as the future owner of the UP at the end of the transaction + owner = Create2.deploy(ownerDeployment.value, ownerDeployment.salt, ownerByteCode); + + uint256 totalValueSent = universalProfileDeployment.value + ownerDeployment.value ; + + (bool success,) = universalProfileFirstOwner.call{value: msg.value - totalValueSent}(abi.encodePacked(calldaToFirstOwner, abi.encodePacked(universalProfile, owner))); + require(success, "UniversalProfileDeployer: first owner call failed"); + } + +} + +contract FirstOwner { + + function setUpUniversalProfile(bytes calldata initializationBytes) public { + address allPermissionsAddress = address(bytes20(initializationBytes)); + address universalProfileAddress = address(bytes20(initializationBytes[20:40])); + address keyManagerAddress = address(bytes20(initializationBytes[40:])); + + // calculate deployer permissions + bytes32[] memory deployerPermissionsArray = new bytes32[](2); + deployerPermissionsArray[0] = _PERMISSION_CHANGEOWNER; + deployerPermissionsArray[1] = _PERMISSION_EDITPERMISSIONS; + bytes32 deployerPermissions = LSP6Utils.combinePermissions(deployerPermissionsArray); + + // setDataBatch keys + bytes32 deloyerKey = bytes32(abi.encodePacked(_LSP6KEY_ADDRESSPERMISSIONS_PERMISSIONS_PREFIX, bytes2(0), address(this))); + bytes32 allPermissionsKey = bytes32(abi.encodePacked(_LSP6KEY_ADDRESSPERMISSIONS_PERMISSIONS_PREFIX, bytes2(0), allPermissionsAddress)); + bytes32[] memory keys = new bytes32[](2); + keys[0] = deloyerKey; + keys[1] = allPermissionsKey; + + // setDataBach values + bytes[] memory values = new bytes[](2); + values[0] = abi.encodePacked(deployerPermissions); + values[1] = abi.encodePacked(ALL_REGULAR_PERMISSIONS); + + // setDataBatch on UP contract + (bool success, ) = universalProfileAddress.call(abi.encodeWithSignature("setDataBatch(bytes32[],bytes[])", keys, values)); + require(success, "setDataBatch failed"); + + // transferOwnership on UP contract + (bool successTransfer, ) = universalProfileAddress.call(abi.encodeWithSignature("transferOwnership(address)", keyManagerAddress)); + require(successTransfer, "transferOwnership failed"); + + // acceptOwnership on keyManager contract + bytes memory acceptOwnershipBytes = abi.encodeWithSignature("acceptOwnership()"); + (bool successAccept,) = keyManagerAddress.call(abi.encodeWithSignature("execute(bytes)", acceptOwnershipBytes)); + require(successAccept, "acceptOwnership failed"); + + // setData on keyManager contract + bytes memory setDataBytes = abi.encodeWithSignature("setData(bytes32,bytes)", deloyerKey, ""); + (bool successSetData,) = keyManagerAddress.call(abi.encodeWithSignature("execute(bytes)", setDataBytes)); + require(successSetData, "setData failed"); + } +} diff --git a/contracts/UniversalProfileDeployer.sol b/contracts/UniversalProfileDeployer.sol index f087281..652bf53 100644 --- a/contracts/UniversalProfileDeployer.sol +++ b/contracts/UniversalProfileDeployer.sol @@ -13,7 +13,6 @@ contract UniversalProfileDeployer { function deployUPAndKeyManager(bytes calldata universalProfileByteCode, bytes calldata keyManagerByteCode, address allPermissionsAddress, bytes32 universalProfleProvidedSalt, bytes32 keyManagerProvidedSalt) public payable - virtual returns (address universalProfile, address keyManager) { // generate salt for the UP contract bytes32 universalProfileGeneratedSalt = keccak256(abi.encodePacked(allPermissionsAddress, keyManagerByteCode, universalProfleProvidedSalt)); From a6c02fccc6c90cef763877c1c9f91816f38b986f Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 13 Jun 2023 11:18:46 +0200 Subject: [PATCH 2/5] feat: add deployments scripts --- .../UniversalProfileAndOwnerDeployer.sol | 8 ++- scripts/deployFirstOwner.ts | 19 +++++++ scripts/deployUPAndOwnerDeployer.ts | 17 ++++++ scripts/deployUPThroughUPAndOwnerDeployer.ts | 55 +++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 scripts/deployFirstOwner.ts create mode 100644 scripts/deployUPAndOwnerDeployer.ts create mode 100644 scripts/deployUPThroughUPAndOwnerDeployer.ts diff --git a/contracts/UniversalProfileAndOwnerDeployer.sol b/contracts/UniversalProfileAndOwnerDeployer.sol index d7f16d0..307d867 100644 --- a/contracts/UniversalProfileAndOwnerDeployer.sol +++ b/contracts/UniversalProfileAndOwnerDeployer.sol @@ -13,7 +13,7 @@ import {ILSP14Ownable2Step} from '@lukso/lsp-smart-contracts/contracts/LSP14Owna // 2nd step: deploy the owner contract (the owner that will be set as the owner of the universal profile contract at the end of the deployment process) // 3rd step: call firstOwner that will be in charge of making the necessary calls that could be needed to set up the universal profile contract -// in this case FirstOwner will be in charge of deploying UPs and KeyManagers and setting up the permissions (we could use delegatecall to do this) +// in this example FirstOwner will be in charge of setting up the permissions between the key manager and the UP etc... (we could use delegatecall to do this) contract UniversalProfileAndOwnerDeployer { @@ -54,12 +54,16 @@ contract UniversalProfileAndOwnerDeployer { uint256 totalValueSent = universalProfileDeployment.value + ownerDeployment.value ; - (bool success,) = universalProfileFirstOwner.call{value: msg.value - totalValueSent}(abi.encodePacked(calldaToFirstOwner, abi.encodePacked(universalProfile, owner))); + (bool success,) = universalProfileFirstOwner.call{value: msg.value - totalValueSent}(abi.encodeWithSignature("setUpUniversalProfile(bytes)", abi.encodePacked(calldaToFirstOwner, universalProfile, owner))); require(success, "UniversalProfileDeployer: first owner call failed"); } } +interface IFirstOwner { + function setUpUniversalProfile(bytes calldata initializationBytes) external; +} + contract FirstOwner { function setUpUniversalProfile(bytes calldata initializationBytes) public { diff --git a/scripts/deployFirstOwner.ts b/scripts/deployFirstOwner.ts new file mode 100644 index 0000000..c738f34 --- /dev/null +++ b/scripts/deployFirstOwner.ts @@ -0,0 +1,19 @@ +import { ethers } from "hardhat"; + + +async function main() { + const firstOwner = await ethers.getContractFactory('FirstOwner') + const firstOwnerInstance = await firstOwner.deploy({gasPrice: (await ethers.provider.getGasPrice()).add(ethers.utils.parseUnits('1', 'gwei'))}) + await firstOwnerInstance.deployed() + + console.log('firstOwnerInstance address', firstOwnerInstance.address); + +} + + + + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/deployUPAndOwnerDeployer.ts b/scripts/deployUPAndOwnerDeployer.ts new file mode 100644 index 0000000..f6865d7 --- /dev/null +++ b/scripts/deployUPAndOwnerDeployer.ts @@ -0,0 +1,17 @@ +import { ethers } from "hardhat"; + +async function main() { + + const universalprofileDeployer = await ethers.getContractFactory('UniversalProfileAndOwnerDeployer') + const universalprofileDeployerInstance = await universalprofileDeployer.deploy({gasPrice: (await ethers.provider.getGasPrice()).add(ethers.utils.parseUnits('1', 'gwei'))}) + await universalprofileDeployerInstance.deployed() + + console.log('universalprofileDeployerInstance address', universalprofileDeployerInstance.address); + +} + + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/deployUPThroughUPAndOwnerDeployer.ts b/scripts/deployUPThroughUPAndOwnerDeployer.ts new file mode 100644 index 0000000..861dac9 --- /dev/null +++ b/scripts/deployUPThroughUPAndOwnerDeployer.ts @@ -0,0 +1,55 @@ + +import { ethers } from "hardhat"; +import { bytecode as universalProfileByteCode } from '../artifacts/@lukso/lsp-smart-contracts/contracts/UniversalProfile.sol/UniversalProfile.json' +import {bytecode as ownerBytecode } from '../artifacts/@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6KeyManager.sol/LSP6KeyManager.json' + +const UP_AND_OWNER_DEPLOYER_ADDRESS = '0x37d586a523B8953c37ffA019e8748CDcb416bBd3' +const ALL_PERMISSIONS_SIGNER = '0x0eCC079C20DaA9fDE0e26b6d745c0b38479ff200' +const UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS = '0x50b1950b351F8422fdEA736Bf262164B618E69eF' + + + +const UNIVERSAL_PROFILE_DEPLOYMENT = { + value: 0, + salt: ethers.utils.keccak256(ethers.utils.toUtf8Bytes('universalprofile')), + byteCode: universalProfileByteCode +} + +const OWNER_DEPLOYMENT = { + value: 0, + salt: ethers.utils.keccak256(ethers.utils.toUtf8Bytes('owner')), + byteCode: ownerBytecode, + appendUniversalProfileAddress: true, + extraConstructorParams: '0x' +} + + +async function main() { + const universalprofileDeployer = await ethers.getContractAt('UniversalProfileAndOwnerDeployer', UP_AND_OWNER_DEPLOYER_ADDRESS) + + const [universalprofile, owner] = await universalprofileDeployer.callStatic.deployUniversalProfileAndOwner( + UNIVERSAL_PROFILE_DEPLOYMENT, + OWNER_DEPLOYMENT, + UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS, + ALL_PERMISSIONS_SIGNER + ) + console.log('universalprofile', universalprofile); + console.log('owner', owner); + + const tx = await universalprofileDeployer.deployUniversalProfileAndOwner( + UNIVERSAL_PROFILE_DEPLOYMENT, + OWNER_DEPLOYMENT, + UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS, + ALL_PERMISSIONS_SIGNER, + {gasPrice: (await ethers.provider.getGasPrice()).add(ethers.utils.parseUnits('1', 'gwei'))} + ) + + await tx.wait() + console.log('tx hash', tx.hash); +} + + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); From 4112a47eb3e3f71687960e6c9c2c4fcb8eee9428 Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 13 Jun 2023 16:46:07 +0200 Subject: [PATCH 3/5] feat: delegator deployement --- .../UniversalProfileAndOwnerDeployer.sol | 4 +- .../UPDeployer/deployFirstOwnerDelegator.ts | 21 +++++ .../UPDeployer/deployUPThroughDelegator.ts | 90 +++++++++++++++++++ .../deployUPThroughUPAndOwnerDeployer.ts | 60 +++++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 scripts/UPDeployer/deployFirstOwnerDelegator.ts create mode 100644 scripts/UPDeployer/deployUPThroughDelegator.ts create mode 100644 scripts/UPDeployer/deployUPThroughUPAndOwnerDeployer.ts diff --git a/contracts/UniversalProfileAndOwnerDeployer.sol b/contracts/UniversalProfileAndOwnerDeployer.sol index 307d867..1265227 100644 --- a/contracts/UniversalProfileAndOwnerDeployer.sol +++ b/contracts/UniversalProfileAndOwnerDeployer.sol @@ -61,12 +61,12 @@ contract UniversalProfileAndOwnerDeployer { } interface IFirstOwner { - function setUpUniversalProfile(bytes calldata initializationBytes) external; + function setUpUniversalProfile(bytes calldata initializationBytes) external; } contract FirstOwner { - function setUpUniversalProfile(bytes calldata initializationBytes) public { + function setUpUniversalProfile(bytes calldata initializationBytes) public payable { address allPermissionsAddress = address(bytes20(initializationBytes)); address universalProfileAddress = address(bytes20(initializationBytes[20:40])); address keyManagerAddress = address(bytes20(initializationBytes[40:])); diff --git a/scripts/UPDeployer/deployFirstOwnerDelegator.ts b/scripts/UPDeployer/deployFirstOwnerDelegator.ts new file mode 100644 index 0000000..d76c66d --- /dev/null +++ b/scripts/UPDeployer/deployFirstOwnerDelegator.ts @@ -0,0 +1,21 @@ +import { ethers } from "hardhat"; + +async function main() { + const gasPrice = await ethers.provider.getGasPrice(); + + const FirstOwnerDelegatorFactory = await ethers.getContractFactory( + "FirstOwnerDelegator" + ); + const firstOwnerDelegator = await FirstOwnerDelegatorFactory.deploy({ + gasPrice: gasPrice.add(ethers.utils.parseUnits("5", "gwei")), + }); + + await firstOwnerDelegator.deployed(); + + console.log("firstOwnerDelegator", firstOwnerDelegator.address); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/UPDeployer/deployUPThroughDelegator.ts b/scripts/UPDeployer/deployUPThroughDelegator.ts new file mode 100644 index 0000000..84b9c94 --- /dev/null +++ b/scripts/UPDeployer/deployUPThroughDelegator.ts @@ -0,0 +1,90 @@ +import { ethers } from "hardhat"; +import { bytecode as universalProfileByteCode } from "../../artifacts/@lukso/lsp-smart-contracts/contracts/UniversalProfile.sol/UniversalProfile.json"; +import { bytecode as ownerBytecode } from "../../artifacts/@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6KeyManager.sol/LSP6KeyManager.json"; + +const UP_AND_OWNER_DEPLOYER_ADDRESS = + "0x37d586a523B8953c37ffA019e8748CDcb416bBd3"; + +const ALL_PERMISSIONS_SIGNER = "0x0eCC079C20DaA9fDE0e26b6d745c0b38479ff200"; + +const UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS = + "0xf7E8f56a487F0204856E54789E6635470D3A2ca9"; + +const UNIVERSAL_PROFILE_DEPLOYMENT = { + value: 0, + salt: ethers.utils.keccak256(ethers.utils.toUtf8Bytes("universalprofile")), + byteCode: universalProfileByteCode, +}; + +const OWNER_DEPLOYMENT = { + value: 0, + salt: ethers.utils.keccak256(ethers.utils.toUtf8Bytes("owner")), + byteCode: ownerBytecode, + appendUniversalProfileAddress: true, + extraConstructorParams: "0x", +}; + +async function main() { + const universalprofileDeployer = await ethers.getContractAt( + "UniversalProfileAndOwnerDeployer", + UP_AND_OWNER_DEPLOYER_ADDRESS + ); + + const FirstOwnerDelegatorFactory = await ethers.getContractFactory( + "FirstOwnerDelegator" + ); + + const allPermissionsSignerPermissionsKey = + "0x4b80742de2bf82acb3630000" + ALL_PERMISSIONS_SIGNER.slice(2); + + const allPermissionsSignerPermissionsValue = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + + const firstOwnerDelegatorBytes = + FirstOwnerDelegatorFactory.interface.encodeFunctionData( + "giveOwnershipToKeyManager", + [ + [allPermissionsSignerPermissionsKey], + [allPermissionsSignerPermissionsValue], + ] + ); + + const [universalprofile, owner] = + await universalprofileDeployer.callStatic.deployUniversalProfileAndOwner( + UNIVERSAL_PROFILE_DEPLOYMENT, + OWNER_DEPLOYMENT, + UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS, + firstOwnerDelegatorBytes + ); + console.log("universalprofile", universalprofile); + console.log("owner", owner); + + const tx = await universalprofileDeployer.deployUniversalProfileAndOwner( + UNIVERSAL_PROFILE_DEPLOYMENT, + OWNER_DEPLOYMENT, + UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS, + firstOwnerDelegatorBytes, + { + gasPrice: ( + await ethers.provider.getGasPrice() + ).add(ethers.utils.parseUnits("1", "gwei")), + } + ); + + await tx.wait(); + console.log("tx hash", tx.hash); + + const universalprofileContract = await ethers.getContractAt( + "UniversalProfile", + universalprofile + ); + const allPermissionsPermissions = await universalprofileContract.getData( + allPermissionsSignerPermissionsKey + ); + console.log("allPermissionsPermissions", allPermissionsPermissions); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/UPDeployer/deployUPThroughUPAndOwnerDeployer.ts b/scripts/UPDeployer/deployUPThroughUPAndOwnerDeployer.ts new file mode 100644 index 0000000..936d1d5 --- /dev/null +++ b/scripts/UPDeployer/deployUPThroughUPAndOwnerDeployer.ts @@ -0,0 +1,60 @@ +import { ethers } from "hardhat"; +import { bytecode as universalProfileByteCode } from "../../artifacts/@lukso/lsp-smart-contracts/contracts/UniversalProfile.sol/UniversalProfile.json"; +import { bytecode as ownerBytecode } from "../../artifacts/@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6KeyManager.sol/LSP6KeyManager.json"; + +const UP_AND_OWNER_DEPLOYER_ADDRESS = + "0x37d586a523B8953c37ffA019e8748CDcb416bBd3"; +const ALL_PERMISSIONS_SIGNER = "0x0eCC079C20DaA9fDE0e26b6d745c0b38479ff200"; +const UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS = + "0x50b1950b351F8422fdEA736Bf262164B618E69eF"; + +const UNIVERSAL_PROFILE_DEPLOYMENT = { + value: 0, + salt: ethers.utils.keccak256(ethers.utils.toUtf8Bytes("universalprofile")), + byteCode: universalProfileByteCode, +}; + +const OWNER_DEPLOYMENT = { + value: 0, + salt: ethers.utils.keccak256(ethers.utils.toUtf8Bytes("owner")), + byteCode: ownerBytecode, + appendUniversalProfileAddress: true, + extraConstructorParams: "0x", +}; + +async function main() { + const universalprofileDeployer = await ethers.getContractAt( + "UniversalProfileAndOwnerDeployer", + UP_AND_OWNER_DEPLOYER_ADDRESS + ); + + const [universalprofile, owner] = + await universalprofileDeployer.callStatic.deployUniversalProfileAndOwner( + UNIVERSAL_PROFILE_DEPLOYMENT, + OWNER_DEPLOYMENT, + UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS, + ALL_PERMISSIONS_SIGNER + ); + console.log("universalprofile", universalprofile); + console.log("owner", owner); + + const tx = await universalprofileDeployer.deployUniversalProfileAndOwner( + UNIVERSAL_PROFILE_DEPLOYMENT, + OWNER_DEPLOYMENT, + UNIVERSAL_PROFILE_FIRST_OWNER_ADDRESS, + ALL_PERMISSIONS_SIGNER, + { + gasPrice: ( + await ethers.provider.getGasPrice() + ).add(ethers.utils.parseUnits("1", "gwei")), + } + ); + + await tx.wait(); + console.log("tx hash", tx.hash); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); From 0f81e95f545fb92781d11320cff3e19d8094c7c6 Mon Sep 17 00:00:00 2001 From: Maxime Date: Mon, 19 Jun 2023 10:12:51 +0200 Subject: [PATCH 4/5] feat: OwnerDelegator --- contracts/FirstOwnerDelegator.sol | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 contracts/FirstOwnerDelegator.sol diff --git a/contracts/FirstOwnerDelegator.sol b/contracts/FirstOwnerDelegator.sol new file mode 100644 index 0000000..720f27f --- /dev/null +++ b/contracts/FirstOwnerDelegator.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import { UniversalProfile } from '@lukso/lsp-smart-contracts/contracts/UniversalProfile.sol'; + +contract FirstOwnerDelegator is UniversalProfile { + + address private _keyManager; + address thisContract = address(this); + + constructor() UniversalProfile(address(0)) { + + } + + function _executeCall(address, uint256, bytes memory) internal pure override returns (bytes memory) { + revert("FirstOwnerDelegator: selfdestruct is not allowed"); + } + + + function giveOwnershipToKeyManager(bytes32[] memory keys_, bytes[] memory values_) external { + if(thisContract == address(this)) { + revert("FirstOwnerDelegator: only delegate calls are allowed"); + } + + _setOwner(_keyManager); + for (uint256 i = 0; i < keys_.length; i++) { + _setData(keys_[i], values_[i]); + } + } + + + function setUpUniversalProfile(bytes calldata initializationBytes) public { + bytes memory datas = initializationBytes[0: initializationBytes.length - 40]; + address universalProfile = address(bytes20(initializationBytes[initializationBytes.length - 40: initializationBytes.length - 20])); + address keyManager = address(bytes20(initializationBytes[initializationBytes.length - 20: initializationBytes.length])); + _keyManager = keyManager; + + (bool success,) = universalProfile.call(abi.encodeWithSignature("execute(uint256,address,uint256,bytes)",4, address(this),0,datas)); + require(success, "FirstOwnerDelegator: execute call failed"); + } + + +} From 2e43b025eb63d88f5e388d248e4eca434c538805 Mon Sep 17 00:00:00 2001 From: Maxime Date: Mon, 19 Jun 2023 10:33:56 +0200 Subject: [PATCH 5/5] refactor: add proxy logic to Deployer --- .../UniversalProfileAndOwnerDeployer.sol | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/contracts/UniversalProfileAndOwnerDeployer.sol b/contracts/UniversalProfileAndOwnerDeployer.sol index 1265227..bf0136a 100644 --- a/contracts/UniversalProfileAndOwnerDeployer.sol +++ b/contracts/UniversalProfileAndOwnerDeployer.sol @@ -4,10 +4,12 @@ pragma solidity ^0.8.4; import {_LSP6KEY_ADDRESSPERMISSIONS_PERMISSIONS_PREFIX, _PERMISSION_CHANGEOWNER, _PERMISSION_EDITPERMISSIONS, ALL_REGULAR_PERMISSIONS} from "@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6Constants.sol"; import {LSP6Utils} from '@lukso/lsp-smart-contracts/contracts/LSP6KeyManager/LSP6Utils.sol'; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; +import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {ILSP14Ownable2Step} from '@lukso/lsp-smart-contracts/contracts/LSP14Ownable2Step/ILSP14Ownable2Step.sol'; + // 1st step: deploy the universal profile contract // 2nd step: deploy the owner contract (the owner that will be set as the owner of the universal profile contract at the end of the deployment process) @@ -15,6 +17,7 @@ import {ILSP14Ownable2Step} from '@lukso/lsp-smart-contracts/contracts/LSP14Owna // 3rd step: call firstOwner that will be in charge of making the necessary calls that could be needed to set up the universal profile contract // in this example FirstOwner will be in charge of setting up the permissions between the key manager and the UP etc... (we could use delegatecall to do this) +// TODO: deal with msg.value logic contract UniversalProfileAndOwnerDeployer { @@ -32,6 +35,54 @@ contract UniversalProfileAndOwnerDeployer { bytes extraConstructorParams; // params to be appended to the constructor after the universal profile address } + struct UniversalProfileDeploymentInit { + uint256 value; + bytes32 salt; + address implementation; + bytes initializationBytes; + } + + struct OwnerDeploymentInit { + uint256 value; + bytes32 salt; + address implementation; + bool appendUniversalProfileAddress; // will append the universal profile address to the initialisation bytes if true + the extraConstructorParams + bytes initializationBytes; + bytes extraInitialisationBytes; // params to be appended to the constructor after the universal profile address + } + + + function deployCreate2ProxyInit( + UniversalProfileDeploymentInit calldata universalProfileDeployment, + OwnerDeploymentInit calldata ownerDeployment, + address universalProfileFirstOwner, + bytes calldata calldaToFirstOwner + ) public payable virtual returns (address upProxy, address ownerProxy) { + + if(msg.value < universalProfileDeployment.value + ownerDeployment.value ) { + revert("UniversalProfileDeployer: insufficient funds"); + } + + bytes32 universalProfileGeneratedSalt = keccak256(abi.encode(universalProfileDeployment.salt,ownerDeployment,universalProfileFirstOwner, calldaToFirstOwner)); + + + upProxy = Clones.cloneDeterministic(universalProfileDeployment.implementation, universalProfileGeneratedSalt); + + (bool success,) = upProxy.call{value: msg.value}(universalProfileDeployment.initializationBytes); + require(success, 'failed initialization of UP proxy'); + + ownerProxy = Clones.cloneDeterministic(ownerDeployment.implementation, keccak256(abi.encode(ownerDeployment.salt, upProxy))); + + bytes memory ownerInitializationBytes = ownerDeployment.appendUniversalProfileAddress ? abi.encodePacked(ownerDeployment.initializationBytes, abi.encode(upProxy)) : ownerDeployment.initializationBytes; + + (success,) = ownerProxy.call{value: msg.value}(ownerInitializationBytes); + require(success, 'failed initialization of owner proxy'); + + (success,) = universalProfileFirstOwner.call{value: msg.value}(abi.encodeWithSignature("setUpUniversalProfile(bytes)", abi.encodePacked(calldaToFirstOwner, upProxy, ownerProxy))); + require(success, "UniversalProfileDeployer: first owner call failed"); + + } + function deployUniversalProfileAndOwner(UniversalProfileDeployment calldata universalProfileDeployment, OwnerDeployment calldata ownerDeployment, address universalProfileFirstOwner, bytes calldata calldaToFirstOwner) public