Skip to content

Commit

Permalink
Merge pull request #435 from primitivefinance/improved-deploy
Browse files Browse the repository at this point in the history
Improved deploy
  • Loading branch information
Alexangelj authored Oct 23, 2023
2 parents 6791f4c + 6bb33bf commit 16c6d6b
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 21 deletions.
40 changes: 40 additions & 0 deletions contracts/PortfolioRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.4;

import "./interfaces/IERC20.sol";
import "./interfaces/IPortfolioRegistry.sol";
import "solmate/auth/Owned.sol";

interface PortfolioLike {
function setProtocolFee(uint256) external;
function claimFee(address, uint256) external;
}

/// @dev Basic registry with a single owner.
contract PortfolioRegistry is IPortfolioRegistry, Owned {
constructor(address owner_) Owned(owner_) { }

function controller() external view returns (address) {
return owner;
}

function setFee(address portfolio, uint256 fee) external onlyOwner {
PortfolioLike(portfolio).setProtocolFee(fee);
}

function claimFee(
address portfolio,
address token,
uint256 amount
) public onlyOwner {
PortfolioLike(portfolio).claimFee(token, amount);
}

function withdraw(address token, uint256 amount) external onlyOwner {
require(amount > 0, "SimpleRegistry/invalid-amount");
require(
IERC20(token).transfer(msg.sender, amount),
"SimpleRegistry/transfer-failed"
);
}
}
2 changes: 1 addition & 1 deletion lib/nugu
96 changes: 76 additions & 20 deletions scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,114 @@ import "../contracts/test/SimpleRegistry.sol";
import "../contracts/Portfolio.sol";
import "../contracts/PositionRenderer.sol";

// This script allows you to deploy the Portfolio contract and its dependencies,
// you can learn how to use it in our documentation:
// https://docs.primitive.xyz/protocol/contracts/deployments#deploying-portfolio
contract Deploy is Script {
function run(address weth, address registry) external {
function printJSON(
string memory name,
address at,
string memory status,
bool isLast
) private view {
console.log(
string.concat(
'"%s":{"address":"%s","status":"%s"}', isLast ? "" : ","
),
name,
at,
status
);
}

function deployIfNecessary(
NuguFactory nugu,
address deployer,
string memory name,
bytes memory creationCode,
bytes32 salt
) private returns (address at) {
at = nugu.getDeployed(deployer, salt);

if (at.code.length == 0) {
nugu.deploy(salt, creationCode, 0);
printJSON(name, at, "deployed", false);
} else {
printJSON(name, at, "skipped", false);
}
}

function run(address factory, address weth, address registry) external {
console.log("{");
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

// Here we specify the factory we want to use
NuguFactory factory =
NuguFactory(0x0000A79C3D8124ED3a7F8EC8427E4DC43a2B154d);
NuguFactory nugu = NuguFactory(factory);

// Let's check if the contract is deployed on this network
// Let's check if the factory contract is deployed on this network, if
// it's not, it's better to avoid deploying it from here, as the address
// is canonical across all networks, we need a specific script for that.
require(
address(factory).code.length > 0,
address(nugu).code.length > 0,
"Nugu Factory not deployed on this network!"
);

// If no WETH address is provided, we deploy a new contract
if (weth == address(0)) {
weth = factory.deploy(keccak256("WETH"), type(WETH).creationCode, 0);
deployIfNecessary(
nugu,
vm.addr(deployerPrivateKey),
"WETH",
type(WETH).creationCode,
keccak256("WETH")
);
} else {
printJSON("WETH", weth, "reused", false);
}

// Same thing for the Portfolio registry
if (registry == address(0)) {
registry = factory.deploy(
keccak256("SimpleRegistry"),
deployIfNecessary(
nugu,
vm.addr(deployerPrivateKey),
"Registry",
type(SimpleRegistry).creationCode,
0
keccak256(type(SimpleRegistry).creationCode)
);
} else {
printJSON("Registry", registry, "reused", false);
}

// First we deploy the PositionRenderer contract
address positionRenderer = factory.deploy(
keccak256("PositionRenderer"),
address positionRenderer = deployIfNecessary(
nugu,
vm.addr(deployerPrivateKey),
"PositionRenderer",
type(PositionRenderer).creationCode,
0
keccak256(type(PositionRenderer).creationCode)
);

// Then we can deploy the Portfolio contract
address portfolio = factory.deploy(
keccak256("Portfolio"),
address portfolio = deployIfNecessary(
nugu,
vm.addr(deployerPrivateKey),
"Portfolio",
abi.encodePacked(
type(Portfolio).creationCode,
abi.encode(weth, registry, positionRenderer)
),
0
0x8B9A3E646B237114DA81624C540FBA61A7E7FFFEA48B90454E66F547AA5B706C
);

console.log(unicode"🚀 Contracts deployed!");
console.log("WETH:", weth);
console.log("Registry:", registry);
console.log("PositionRenderer:", positionRenderer);
console.log("Portfolio:", portfolio);
printJSON(
"NormalStrategy",
Portfolio(payable(portfolio)).DEFAULT_STRATEGY(),
"deployed",
true
);

vm.stopBroadcast();
console.log("}");
}
}
120 changes: 120 additions & 0 deletions scripts/Prepare.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "solmate/tokens/WETH.sol";
import "nugu/NuguFactory.sol";

import "../contracts/PortfolioRegistry.sol";
import "../contracts/Portfolio.sol";
import "../contracts/PositionRenderer.sol";

// This script prepares all the transaction data that must be used to deploy
// Portfolio and its dependency contracts.
//
// Here are the steps to use it:
// 1. Be sure to update all the constants below: contract addresses and salts.
// 2. Run the script with:
// `forge script ./scripts/Prepare.s.sol --via-ir --optimizer-runs 0`.
//
// If you want to deploy from a Safe Wallet, you can follow the steps below:
// 3. Visit https://app.safe.global/, click on your multisig wallet, then on
// "New transaction" and finally "Transaction builder".
// 4. Paste the address of the Nugu factory and enable the "custom data".
// 5. Then for each data generated by this script (3 in total), you'll need to
// copy and paste it into the "data" field of the transaction builder and click
// the "Add transaction" button.
// 6. After that, a total of 3 transactions must compose the batch. You can then
// hit "Create Batch", and either "Simulate" or directly "Send Batch".
// 7. Last step is simply to follow the instructions to send the transaction.
//
// If you want to deploy from a hardware wallet, you can follow the steps below:
// 3. Visit any website allowing you to send a transaction with custom data (such
// as https://www.myetherwallet.com/).
// 4. Paste the address of the Nugu factory as a recipient, paste the first custom
// data that was generated and send the transaction.
// 5. Repeat step 4 for all the data generated by this script (3 in total).

address constant MULTISIG_WALLET = 0x8cDb0095ceddb092aD592aCE2971e4f364b5E8eE;
address constant NUGU_ADDRESS = 0xe50ea0e9849cb17829907Acaf764af8F37d4938E;
address constant WETH_ADDRESS = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6;

bytes32 constant REGISTRY_SALT = keccak256("REGISTRY0");
bytes32 constant POSITION_RENDERER_SALT = keccak256("POSITION_RENDERER0");
bytes32 constant PORTFOLIO_SALT = keccak256("PORTFOLIO0");

contract Deploy is Script {
bytes internal constant PROXY_BYTECODE =
hex"67363d3d37363d34f03d5260086018f3";
bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE);

function run() external {
bytes memory registryDeployData = abi.encodeCall(
NuguFactory.deploy,
(
REGISTRY_SALT,
abi.encodePacked(
type(PortfolioRegistry).creationCode,
abi.encode(MULTISIG_WALLET)
),
0
)
);

bytes memory positionRendererDeployData = abi.encodeCall(
NuguFactory.deploy,
(POSITION_RENDERER_SALT, type(PositionRenderer).creationCode, 0)
);

address predictedRegistryAddress =
getDeployed(NUGU_ADDRESS, MULTISIG_WALLET, REGISTRY_SALT);

address predictedPositionRendererAddress =
getDeployed(NUGU_ADDRESS, MULTISIG_WALLET, POSITION_RENDERER_SALT);

bytes memory portfolioDeployData = abi.encodeCall(
NuguFactory.deploy,
(
PORTFOLIO_SALT,
abi.encodePacked(
type(Portfolio).creationCode,
abi.encode(
WETH_ADDRESS,
predictedRegistryAddress,
predictedPositionRendererAddress
)
),
0
)
);

console.log("\nRegistry deploy data:");
console.logBytes(registryDeployData);

console.log("\nPositionRenderer deploy data:");
console.logBytes(positionRendererDeployData);

console.log("\nPortfolio deploy data:");
console.logBytes(portfolioDeployData);
}

function getDeployed(
address factory,
address deployer,
bytes32 salt
) internal view returns (address) {
salt = keccak256(abi.encodePacked(deployer, salt));

address proxy = Bytes32AddressLib.fromLast20Bytes(
keccak256(
abi.encodePacked(
bytes1(0xFF), factory, salt, PROXY_BYTECODE_HASH
)
)
);

return Bytes32AddressLib.fromLast20Bytes(
keccak256(abi.encodePacked(hex"d694", proxy, hex"01"))
);
}
}
14 changes: 14 additions & 0 deletions test/TestPortfolioRegistry.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "forge-std/Test.sol";
import "../contracts/PortfolioRegistry.sol";

contract TestPortfolioRegistry is Test {
PortfolioRegistry public registry;

function test_controller() public {
registry = new PortfolioRegistry(address(this));
assertEq(registry.controller(), address(this));
}
}

0 comments on commit 16c6d6b

Please sign in to comment.