Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add table creator script contract #287

Merged
merged 5 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ out-svg
lcov.info
package-lock.json
pnpm-lock.yaml
script/*.md
yarn.lock
12 changes: 11 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
fs_permissions = [
{ access = "read", path = "./out-optimized" },
{ access = "read", path = "package.json" },
{ access = "read-write", path = "./benchmark/results"}
{ access = "read-write", path = "./benchmark/results"},
{ access = "read-write", path = "./script"}
]
gas_limit = 9223372036854775807
gas_reports = ["SablierFlow"]
Expand Down Expand Up @@ -78,12 +79,21 @@
avalanche = "${AVALANCHE_RPC_URL}"
base = "https://mainnet.base.org"
base_sepolia = "https://sepolia.base.org"
berachain_artio = "https://bartio.rpc.berachain.com/"
bnb = "https://bsc-dataseed.binance.org"
gnosis = "https://rpc.gnosischain.com"
linea = "https://rpc.linea.build"
linea_sepolia = "https://rpc.sepolia.linea.build"
localhost = "http://localhost:8545"
mainnet = "${MAINNET_RPC_URL}"
mode = "https://mainnet.mode.network/"
mode_sepolia = "https://sepolia.mode.network/"
optimism = "${OPTIMISM_RPC_URL}"
optimism_sepolia = "https://sepolia.optimism.io"
polygon = "${POLYGON_RPC_URL}"
scroll = "https://rpc.scroll.io/"
sei = "https://evm-rpc.sei-apis.com"
sei_testnet = "https://evm-rpc.arctic-1.seinetwork.io"
sepolia = "${SEPOLIA_RPC_URL}"
taiko_hekla = "https://rpc.hekla.taiko.xyz"
taiko_mainnet = "https://rpc.mainnet.taiko.xyz"
12 changes: 8 additions & 4 deletions script/Base.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ abstract contract BaseScript is Script {
///
/// Notes:
/// - The salt format is "ChainID <chainid>, Version <version>".
/// - The version is obtained from `package.json`.
function constructCreate2Salt() public view returns (bytes32) {
function constructCreate2Salt() internal view returns (bytes32) {
string memory chainId = block.chainid.toString();
string memory json = vm.readFile("package.json");
string memory version = json.readString(".version");
string memory version = getVersion();
string memory create2Salt = string.concat("ChainID ", chainId, ", Version ", version);
console2.log("The CREATE2 salt is %s", create2Salt);
return bytes32(abi.encodePacked(create2Salt));
}

/// @dev The version is obtained from `package.json`.
function getVersion() internal view returns (string memory) {
string memory json = vm.readFile("package.json");
return json.readString(".version");
}
}
17 changes: 14 additions & 3 deletions script/DeployDeterministicFlow.s.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { FlowNFTDescriptor } from "src/FlowNFTDescriptor.sol";
import { FlowNFTDescriptor } from "src/FlowNFTDescriptor.sol";
import { SablierFlow } from "src/SablierFlow.sol";
import { BaseScript } from "./Base.s.sol";
import { DeploymentLogger } from "./DeploymentLogger.s.sol";

/// @notice Deploys {SablierFlow} at a deterministic address across chains.
/// @dev Reverts if the contract has already been deployed.
contract DeployDeterministicFlow is BaseScript {
function run(address initialAdmin) public broadcast returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
contract DeployDeterministicFlow is DeploymentLogger("deterministic") {
function run() public returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
(flow, nftDescriptor) = _run(adminMap[block.chainid]);
}

function run(address initialAdmin) public returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
smol-ninja marked this conversation as resolved.
Show resolved Hide resolved
(flow, nftDescriptor) = _run(initialAdmin);
}

function _run(address initialAdmin) public broadcast returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
bytes32 salt = constructCreate2Salt();
nftDescriptor = new FlowNFTDescriptor{ salt: salt }();
flow = new SablierFlow{ salt: salt }(initialAdmin, nftDescriptor);

appendToFileDeployedAddresses(address(flow), address(nftDescriptor));
}
}
21 changes: 18 additions & 3 deletions script/DeployFlow.s.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { FlowNFTDescriptor } from "src/FlowNFTDescriptor.sol";
import { FlowNFTDescriptor } from "src/FlowNFTDescriptor.sol";
import { SablierFlow } from "src/SablierFlow.sol";
import { BaseScript } from "./Base.s.sol";
import { DeploymentLogger } from "./DeploymentLogger.s.sol";

/// @notice Deploys {SablierFlow}.
contract DeployFlow is BaseScript {
function run(address initialAdmin) public broadcast returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
contract DeployFlow is DeploymentLogger("not-deterministic") {
function run() public returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
(flow, nftDescriptor) = _run(adminMap[block.chainid]);
}

function run(address initialAdmin) public returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor) {
(flow, nftDescriptor) = _run(initialAdmin);
}

function _run(address initialAdmin)
internal
broadcast
returns (SablierFlow flow, FlowNFTDescriptor nftDescriptor)
{
nftDescriptor = new FlowNFTDescriptor();
flow = new SablierFlow(initialAdmin, nftDescriptor);

appendToFileDeployedAddresses(address(flow), address(nftDescriptor));
}
}
182 changes: 182 additions & 0 deletions script/DeploymentLogger.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { stdJson } from "forge-std/src/StdJson.sol";

import { BaseScript } from "./Base.s.sol";

/// @dev This contract appends to the `script` directory a markdown file with the deployed addresses.
contract DeploymentLogger is BaseScript {
using stdJson for string;
using Strings for address;
using Strings for string;
using Strings for uint256;

/// @dev The address of the default Sablier admin.
address internal constant DEFAULT_SABLIER_ADMIN = 0xb1bEF51ebCA01EB12001a639bDBbFF6eEcA12B9F;

/// @dev Admin address mapped by the chain Id.
mapping(uint256 chainId => address admin) internal adminMap;

/// @dev Chain names mapped by the chain Id.
mapping(uint256 chainId => string name) internal chainNameMap;

/// @dev The path to the file where the deployment addresses are stored.
string internal deploymentFile;

/// @dev Explorer URL mapped by the chain Id.
mapping(uint256 chainId => string explorerUrl) internal explorerMap;

constructor(string memory deterministicOrNot) {
// Populate the admin map.
populateAdminMap();

// Populate the chain name map.
populateChainNameMap();

// Populate the explorer URLs.
populateExplorerMap();

// If there is no admin set for a specific chain, use the default Sablier admin.
if (adminMap[block.chainid] == address(0)) {
adminMap[block.chainid] = DEFAULT_SABLIER_ADMIN;
}

// If there is no explorer URL set for a specific chain, use a placeholder.
if (explorerMap[block.chainid].equal("")) {
explorerMap[block.chainid] = "N/A";
}

// If there is no chain name set for a specific chain, use the chain ID.
if (chainNameMap[block.chainid].equal("")) {
chainNameMap[block.chainid] = string.concat("Chain ID: ", block.chainid.toString());
}

// Set the deployment file path.
deploymentFile = string.concat("script/", deterministicOrNot, ".md");

// Append the chain name to the deployment file.
_appendToFile(string.concat("## ", chainNameMap[block.chainid], "\n"));
}

/// @dev Function to append the deployed addresses to the deployment file.
function appendToFileDeployedAddresses(address flow, address flowNFTDescriptor) internal {
string memory firstTwoLines = "| Contract | Address | Deployment |\n | :------- | :------ | :----------|";
_appendToFile(firstTwoLines);

string memory flowLine = _getContractLine({ contractName: "SablierFlow", contractAddress: flow.toHexString() });
_appendToFile(flowLine);

string memory flowNFTDescriptorLine =
_getContractLine({ contractName: "FlowNFTDescriptor", contractAddress: flowNFTDescriptor.toHexString() });
_appendToFile(flowNFTDescriptorLine);

_appendToFile("\n");
}

/// @dev Populates the admin map. The reason the chain IDs configured for the admin map do not match the other
/// maps is that we only have multisigs for the chains listed below, otherwise, the default admin is used.​
function populateAdminMap() internal {
adminMap[42_161] = 0xF34E41a6f6Ce5A45559B1D3Ee92E141a3De96376; // Arbitrum
adminMap[43_114] = 0x4735517616373c5137dE8bcCDc887637B8ac85Ce; // Avalanche
adminMap[8453] = 0x83A6fA8c04420B3F9C7A4CF1c040b63Fbbc89B66; // Base
adminMap[56] = 0x6666cA940D2f4B65883b454b7Bc7EEB039f64fa3; // BNB
adminMap[100] = 0x72ACB57fa6a8fa768bE44Db453B1CDBa8B12A399; // Gnosis
adminMap[1] = 0x79Fb3e81aAc012c08501f41296CCC145a1E15844; // Mainnet
adminMap[59_144] = 0x72dCfa0483d5Ef91562817C6f20E8Ce07A81319D; // Linea
adminMap[10] = 0x43c76FE8Aec91F63EbEfb4f5d2a4ba88ef880350; // Optimism
adminMap[137] = 0x40A518C5B9c1d3D6d62Ba789501CE4D526C9d9C6; // Polygon
adminMap[534_352] = 0x0F7Ad835235Ede685180A5c611111610813457a9; // Scroll
}

/// @dev Populates the chain name map.
function populateChainNameMap() internal {
chainNameMap[42_161] = "Arbitrum";
chainNameMap[43_114] = "Avalanche";
chainNameMap[8453] = "Base";
chainNameMap[84_532] = "Base Sepolia";
chainNameMap[80_084] = "Berachain Bartio";
chainNameMap[81_457] = "Blast";
chainNameMap[168_587_773] = "Blast Sepolia";
chainNameMap[56] = "BNB Smart Chain";
chainNameMap[100] = "Gnosis";
chainNameMap[1890] = "Lightlink";
chainNameMap[59_144] = "Linea";
chainNameMap[59_141] = "Linea Sepolia";
chainNameMap[1] = "Mainnet";
chainNameMap[333_000_333] = "Meld";
chainNameMap[34_443] = "Mode";
chainNameMap[919] = "Mode Sepolia";
chainNameMap[2810] = "Morph Holesky";
chainNameMap[10] = "Optimism";
chainNameMap[11_155_420] = "Optimism Sepolia";
chainNameMap[137] = "Polygon";
chainNameMap[534_352] = "Scroll";
chainNameMap[11_155_111] = "Sepolia";
chainNameMap[53_302] = "Superseed Sepolia";
chainNameMap[167_009] = "Taiko Hekla";
chainNameMap[167_000] = "Taiko Mainnet";
}

/// @dev Populates the explorer map.
function populateExplorerMap() internal {
explorerMap[42_161] = "https://arbiscan.io/address/";
explorerMap[43_114] = "https://snowtrace.io/address/";
explorerMap[8453] = "https://basescan.org/address/";
explorerMap[84_532] = "https://sepolia.basescan.org/address/";
explorerMap[80_084] = "https://bartio.beratrail.io/address/";
explorerMap[81_457] = "https://blastscan.io/address/";
explorerMap[168_587_773] = "https://sepolia.blastscan.io/address/";
explorerMap[56] = "https://bscscan.com/address/";
explorerMap[1] = "https://etherscan.io/address/";
explorerMap[100] = "https://gnosisscan.io/address/";
explorerMap[59_144] = "https://lineascan.build/address/";
explorerMap[59_141] = "https://sepolia.lineascan.build/address/";
explorerMap[1890] = "https://phoenix.lightlink.io/address/";
explorerMap[34_443] = "https://explorer.mode.network/address/";
explorerMap[919] = "https://sepolia.explorer.mode.network/address/";
explorerMap[2810] = "https://explorer-holesky.morphl2.io/address/";
explorerMap[333_000_333] = "https://meldscan.io/address/";
explorerMap[10] = "https://optimistic.etherscan.io/address/";
explorerMap[11_155_420] = "https://sepolia-optimistic.etherscan.io/address/";
explorerMap[137] = "https://polygonscan.com/address/";
explorerMap[534_352] = "https://scrollscan.com/address/";
explorerMap[11_155_111] = "https://sepolia.etherscan.io/address/";
explorerMap[53_302] = "https://sepolia-explorer.superseed.xyz/address/";
explorerMap[167_009] = "https://explorer.hekla.taiko.xyz/address/";
explorerMap[167_000] = "https://taikoscan.io/address/";
}

/// @dev Append a line to the deployment file path.
function _appendToFile(string memory line) private {
vm.writeLine({ path: deploymentFile, data: line });
}

/// @dev Returns a string for a single contract line formatted according to the docs.
function _getContractLine(
string memory contractName,
string memory contractAddress
)
private
view
returns (string memory)
{
string memory version = getVersion();
version = string.concat("v", version);

return string.concat(
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
"| ",
contractName,
" | [",
contractAddress,
"](",
explorerMap[block.chainid],
contractAddress,
") | [",
version,
"](https://github.com/sablier-labs/v2-deployments/tree/main/",
") |"
);
}
}
Loading