diff --git a/.gitignore b/.gitignore index 7624885d..68f10f8b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ forge-cache/ # Ignores development broadcast logs !/broadcast +# ignore 1 for now, it's tenderly fork +/broadcast/*/1/ /broadcast/*/31337/ /broadcast/**/dry-run/ @@ -24,3 +26,11 @@ node_modules/ pnpm-lock.yaml coverage + +# contracts +abi +typechain +!./script/out + +# hardhat-tenderly plugin +deployments \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 5b006662..ce2b8af1 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,5 +5,13 @@ "tabWidth": 4, "semi": false, "singleQuote": false, - "bracketSpacing": true + "bracketSpacing": true, + "overrides": [ + { + "files": ["*.ts", "*.js"], + "options": { + "tabWidth": 2 + } + } + ] } diff --git a/Makefile b/Makefile index c370266f..f633200e 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,19 @@ -include .env -.PHONY: all test clean coverage +.PHONY: all test clean coverage typechain deploy-main all: clean install build +# function: generate abi for given contract name (key) +# requires contract name to match the file name +define generate_abi + $(eval $@_CONTRACT_NAME = $(1)) + $(eval $@_CONTRACT_PATH = $(2)) + forge inspect --optimize --optimizer-runs 2000 contracts/${$@_CONTRACT_PATH}/${$@_CONTRACT_NAME}.sol:${$@_CONTRACT_NAME} abi > abi/${$@_CONTRACT_NAME}.json +endef + # Clean the repo -forge-clean :; forge clean +forge-clean :; forge clean clean :; npx hardhat clean # Remove modules @@ -14,9 +22,9 @@ forge-remove :; rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib && tou install :; npm install # Update Dependencies -forge-update:; forge update +forge-update :; forge update -forge-build:; forge build +forge-build :; forge build build :; npx hardhat compile test :; forge test @@ -33,6 +41,24 @@ coverage: lcov --remove lcov.info -o lcov.info 'test/*' genhtml lcov.info --output-dir coverage +abi: + mkdir -p abi + @$(call generate_abi,"AccessController",".") + @$(call generate_abi,"DisputeModule","./modules/dispute-module") + @$(call generate_abi,"RoyaltyModule","./modules/royalty-module") + @$(call generate_abi,"TaggingModule","./modules/tagging") + @$(call generate_abi,"IPAccountRegistry","./registries") + @$(call generate_abi,"IPRecordRegistry","./registries") + @$(call generate_abi,"LicenseRegistry","./registries") + @$(call generate_abi,"ModuleRegistry","./registries") + @$(call generate_abi,"IPMetadataResolver","./resolvers") + +# typechain: +# make abi +# rm -rf ./types-typechain +# npx typechain --target ethers-v6 ./abi/*.json --out-dir ./types-typechain +typechain :; npx hardhat typechain + # solhint should be installed globally lint :; npx solhint contracts/**/*.sol && npx solhint contracts/*.sol @@ -41,3 +67,10 @@ verify-goerli :; npx hardhat verify --network goerli ${contract} anvil :; anvil -m 'test test test test test test test test test test test junk' +# run: RPC_URL=https://rpc.url make deploy-main +deploy-main :; forge script script/foundry/deployment/Main.s.sol:Main --rpc-url ${RPC_URL} --broadcast --verify -vvvv + +deploy-main-hh: + rm -rf deployments/hardhat/*.json + npx hardhat run script/hardhat/post-deployment/99-revert-chain.ts --network tenderly + npx hardhat run script/hardhat/deployment/00-deploy-main.ts --network tenderly \ No newline at end of file diff --git a/contracts/mocks/MockERC20.sol b/contracts/mocks/MockERC20.sol new file mode 100644 index 00000000..04f9bbc3 --- /dev/null +++ b/contracts/mocks/MockERC20.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MockERC20 is ERC20 { + constructor() ERC20("MockERC20", "MERC20") {} + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } + + function burn(address from, uint256 amount) external { + _burn(from, amount); + } +} diff --git a/contracts/mocks/MockERC721.sol b/contracts/mocks/MockERC721.sol new file mode 100644 index 00000000..efd6ef33 --- /dev/null +++ b/contracts/mocks/MockERC721.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract MockERC721 is ERC721 { + uint256 private _counter; + + constructor() ERC721("MockERC721", "M721") { + _counter = 0; + } + + function mint(address to) public returns (uint256 tokenId) { + tokenId = ++_counter; + _safeMint(to, tokenId); + return tokenId; + } + + function mintId(address to, uint256 tokenId) public returns (uint256) { + _safeMint(to, tokenId); + return tokenId; + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } + + function transferFrom(address from, address to, uint256 tokenId) public override { + _transfer(from, to, tokenId); + } +} diff --git a/deploy-out/deployment-1.json b/deploy-out/deployment-1.json new file mode 100644 index 00000000..0d02fcb9 --- /dev/null +++ b/deploy-out/deployment-1.json @@ -0,0 +1,10 @@ +{ + "main": { + "AccessController": "0xE2307e3710d108ceC7a4722a020a050681c835b3", + "ERC6551Registry": "0xD28F3246f047Efd4059B24FA1fa587eD9fa3e77F", + "IPAccountImpl": "0x15F2ea83eB97ede71d84Bd04fFF29444f6b7cd52", + "IPAccountRegistry": "0x519b05b3655F4b89731B677d64CEcf761f4076f6", + "LicenseRegistry": "0x057cD3082EfED32d5C907801BF3628B27D88fD80", + "ModuleRegistry": "0x0B32a3F8f5b7E5d315b9E52E640a49A89d89c820" + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 9c329feb..db00bfaf 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,6 +8,7 @@ optimizer = true optimizer_runs = 20000 test = 'test' solc = '0.8.23' +fs_permissions = [{ access = 'read-write', path = './deployments' }] [rpc_endpoints] # Comment out for local development (testing requires 0xSplit forks — will add Mock soon) diff --git a/hardhat.config.js b/hardhat.config.js deleted file mode 100644 index 882aba83..00000000 --- a/hardhat.config.js +++ /dev/null @@ -1,73 +0,0 @@ -require("dotenv").config() -require("hardhat-deploy") - -require("@nomiclabs/hardhat-etherscan") -require("@nomiclabs/hardhat-waffle") -require("hardhat-gas-reporter") -require("solidity-coverage") -require("@nomiclabs/hardhat-ethers") -require("@nomiclabs/hardhat-etherscan") -require("@openzeppelin/hardhat-upgrades") - -// This is a sample Hardhat task. To learn how to create your own go to -// https://hardhat.org/guides/create-task.html -// eslint-disable-next-line no-undef -task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { - const accounts = await hre.ethers.getSigners() - - for (const account of accounts) { - console.log(account.address) - } -}) - -// You need to export an object to set up your config -// Go to https://hardhat.org/config/ to learn more - -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - networks: { - mainnet: { - url: process.env.MAINNET_URL || "", - chainId: 1, - accounts: [ - process.env.MAINNET_PRIVATEKEY || - "0x1234567890123456789012345678901234567890123456789012345678901234", - ], - }, - goerli: { - url: process.env.GOERLI_URL || "", - chainId: 5, - accounts: [ - process.env.GOERLI_PRIVATEKEY || - "0x1234567890123456789012345678901234567890123456789012345678901234", - ], - }, - }, - gasReporter: { - enabled: process.env.REPORT_GAS !== undefined, - currency: "USD", - }, - solidity: { - version: "0.8.18", - settings: { - optimizer: { - enabled: true, - runs: 2000, - }, - }, - }, - etherscan: { - apiKey: `${process.env.ETHERSCAN_API_KEY}`, - }, - paths: { - sources: "./contracts", - tests: "./test", - cache: "./cache", - artifacts: "./artifacts", - }, - mocha: { - timeout: 20000, - }, -} diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 00000000..77a02761 --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,109 @@ +import "@nomicfoundation/hardhat-foundry" +import "@nomiclabs/hardhat-waffle" +import "@nomiclabs/hardhat-ethers" +import "@nomicfoundation/hardhat-verify" +import "@tenderly/hardhat-tenderly" +import * as tdly from "@tenderly/hardhat-tenderly" // also import tdly for setup, in addition to global import above +import "@typechain/hardhat" +// import "@openzeppelin/hardhat-upgrades" +import "hardhat-gas-reporter" +import "solidity-coverage" +import "hardhat-deploy" +import { HardhatConfig, HardhatUserConfig } from "hardhat/types" +import "hardhat-contract-sizer" // npx hardhat size-contracts + +require("dotenv").config() + +tdly.setup({ + automaticVerifications: true, +}) + +// +// NOTE: +// To load the correct .env, you must run this at the root folder (where hardhat.config is located) +// +const MAINNET_URL = process.env.MAINNET_URL || "https://eth-mainnet" +const MAINNET_PRIVATEKEY = process.env.MAINNET_PRIVATEKEY || "0xkey" +const GOERLI_URL = process.env.GOERLI_URL || "https://eth-goerli" +const GOERLI_PRIVATEKEY = process.env.GOERLI_PRIVATEKEY || "0xkey" + +const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || "key" + +const COINMARKETCAP_API_KEY = process.env.COINMARKETCAP_API_KEY || "key" + +/** @type import('hardhat/config').HardhatUserConfig */ +const config: HardhatUserConfig = { + solidity: { + compilers: [ + { + version: "0.8.18", + }, + { + version: "0.8.23", + }, + ], + settings: { + optimizer: { + enabled: true, + runs: 2000, + }, + }, + }, + paths: { + sources: "./contracts", + tests: "./test", + cache: "./cache", + artifacts: "./artifacts", + }, + defaultNetwork: "tenderly", + networks: { + hardhat: { + chainId: 31337, + }, + localhost: { + chainId: 31337, + url: "http://127.0.0.1:8545/", + }, + tenderly: { + url: MAINNET_URL || "", + chainId: 1, + accounts: [MAINNET_PRIVATEKEY], + }, + goerli: { + chainId: 5, + url: GOERLI_URL || "", + accounts: [GOERLI_PRIVATEKEY], + }, + }, + // @ts-ignore + namedAccounts: { + deployer: { + default: 0, // here this will by default take the first account as deployer + }, + }, + gasReporter: { + enabled: process.env.REPORT_GAS !== undefined, + outputFile: "gas-report.txt", + noColors: true, + currency: "USD", + coinmarketcap: COINMARKETCAP_API_KEY, + }, + mocha: { + timeout: 20_000, + }, + etherscan: { + apiKey: ETHERSCAN_API_KEY, + }, + tenderly: { + project: process.env.TENDERLY_PROJECT_SLUG || "", + username: process.env.TENDERLY_USERNAME || "", + forkNetwork: 1, // fork mainnet + privateVerification: process.env.TENDERLY_PRIVATE_VERIFICATION === "true", + }, + typechain: { + outDir: "typechain", + target: "ethers-v6", + }, +} + +export default config diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts new file mode 100644 index 00000000..c44e49ed --- /dev/null +++ b/helper-hardhat-config.ts @@ -0,0 +1,22 @@ +import { ethers } from "ethers" + +export interface networkConfigItem { + name?: string + blockConfirmations?: number +} + +export interface networkConfigInfo { + [key: number]: networkConfigItem +} + +export const networkConfig: networkConfigInfo = { + 5: { + name: "goerli", + blockConfirmations: 6, + }, + 31337: { + name: "hardhat", + }, +} + +export const developmentChains = ["hardhat", "localhost"] diff --git a/package.json b/package.json index a895b2eb..abbffb5f 100644 --- a/package.json +++ b/package.json @@ -16,25 +16,35 @@ "author": "StoryProtocol", "license": "MIT", "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-verify": "^2.0.3", "@nomiclabs/hardhat-ethers": "^2.2.3", - "@nomiclabs/hardhat-etherscan": "^3.1.7", "@nomiclabs/hardhat-waffle": "^2.0.5", - "@openzeppelin/hardhat-upgrades": "^1.22.1", + "@openzeppelin/hardhat-upgrades": "^3.0.2", + "@tenderly/hardhat-tenderly": "^2.1.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", "base64-sol": "^1.1.0", "chai": "^4.3.7", "dotenv": "^16.0.3", "eslint": "^8.38.0", "eslint-plugin-prettier": "^4.2.1", "ethereum-waffle": "^4.0.10", - "hardhat": "^2.13.0", - "hardhat-deploy": "^0.11.25", + "ethers": "^6.10.0", + "hardhat": "^2.19.4", + "hardhat-contract-sizer": "^2.10.0", + "hardhat-deploy": "^0.11.45", + "hardhat-deploy-ethers": "^0.4.1", "hardhat-gas-reporter": "^1.0.9", "mocha": "^10.2.0", "prettier": "2.8.7", "prettier-plugin-solidity": "^1.1.3", "solhint": "^3.4.1", "solhint-plugin-prettier": "^0.0.5", - "solidity-coverage": "^0.8.2" + "solidity-coverage": "^0.8.2", + "ts-node": "^10.9.2", + "typechain": "^8.3.2" }, "dependencies": { "@openzeppelin/contracts": "5.0.1", diff --git a/script/foundry/deployment/Main.s.sol b/script/foundry/deployment/Main.s.sol new file mode 100644 index 00000000..3823e233 --- /dev/null +++ b/script/foundry/deployment/Main.s.sol @@ -0,0 +1,169 @@ +/* solhint-disable no-console */ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +// external +import { console2 } from "forge-std/console2.sol"; +import { Script } from "forge-std/Script.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { ERC6551Registry } from "lib/reference/src/ERC6551Registry.sol"; +import { IERC6551Account } from "lib/reference/src/interfaces/IERC6551Account.sol"; +// contracts +import { AccessController } from "contracts/AccessController.sol"; +import { IPAccountImpl } from "contracts/IPAccountImpl.sol"; +import { IIPAccount } from "contracts/interfaces/IIPAccount.sol"; +import { Errors } from "contracts/lib/Errors.sol"; +import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol"; +import { IPRecordRegistry } from "contracts/registries/IPRecordRegistry.sol"; +import { ModuleRegistry } from "contracts/registries/ModuleRegistry.sol"; +import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; +import { RegistrationModule } from "contracts/modules/RegistrationModule.sol"; +import { TaggingModule } from "contracts/modules/tagging/TaggingModule.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; +import { DisputeModule } from "contracts/modules/dispute-module/DisputeModule.sol"; +import { IPMetadataResolver } from "contracts/resolvers/IPMetadataResolver.sol"; + +// script +import { StringUtil } from "script/foundry/utils/StringUtil.sol"; +import { BroadcastManager } from "script/foundry/utils/BroadcastManager.s.sol"; +import { JsonDeploymentHandler } from "script/foundry/utils/JsonDeploymentHandler.s.sol"; + +contract Main is Script, BroadcastManager, JsonDeploymentHandler { + using StringUtil for uint256; + using stdJson for string; + + address public constant ERC6551_REGISTRY = address(0x000000006551c19487814612e58FE06813775758); + AccessController public accessController; + + IPAccountRegistry public ipAccountRegistry; + IPRecordRegistry public ipRecordRegistry; + LicenseRegistry public licenseRegistry; + ModuleRegistry public moduleRegistry; + + IPAccountImpl public implementation; + // MockERC721 nft = new MockERC721(); + + IIPAccount public ipAccount; + // ERC6551Registry public erc6551Registry; + + RegistrationModule registrationModule; + TaggingModule taggingModule; + RoyaltyModule royaltyModule; + DisputeModule disputeModule; + IPMetadataResolver ipMetadataResolver; + + constructor() JsonDeploymentHandler("main") {} + + /// @dev To use, run the following command (e.g. for Sepolia): + /// forge script script/foundry/deployment/Main.s.sol:Main --rpc-url $RPC_URL --broadcast --verify -vvvv + + function run() public { + _beginBroadcast(); // BroadcastManager.s.sol + + bool configByMultisig = vm.envBool("DEPLOYMENT_CONFIG_BY_MULTISIG"); + console2.log("configByMultisig:", configByMultisig); + + if (configByMultisig) { + _deployProtocolContracts(multisig); + } else { + _deployProtocolContracts(deployer); + _configureDeployment(); + } + + _writeDeployment("./deploy-out"); // write deployment json to deploy-out/deployment-{chainId}.json + _endBroadcast(); // BroadcastManager.s.sol + } + + function _deployProtocolContracts(address accessControlAdmin) private { + string memory contractKey; + + contractKey = "AccessController"; + _predeploy(contractKey); + accessController = new AccessController(); + _postdeploy(contractKey, address(accessController)); + + contractKey = "IPAccountImpl"; + _predeploy(contractKey); + implementation = new IPAccountImpl(); + _postdeploy(contractKey, address(implementation)); + + contractKey = "ModuleRegistry"; + _predeploy(contractKey); + moduleRegistry = new ModuleRegistry(); + _postdeploy(contractKey, address(moduleRegistry)); + + contractKey = "LicenseRegistry"; + _predeploy(contractKey); + licenseRegistry = new LicenseRegistry("https://example.com/{id}.json"); + _postdeploy(contractKey, address(licenseRegistry)); + + contractKey = "IPAccountRegistry"; + _predeploy(contractKey); + ipAccountRegistry = new IPAccountRegistry(ERC6551_REGISTRY, address(accessController), address(implementation)); + _postdeploy(contractKey, address(ipAccountRegistry)); + + contractKey = "IPRecordRegistry"; + _predeploy(contractKey); + ipRecordRegistry = new IPRecordRegistry(address(moduleRegistry), address(ipAccountRegistry)); + _postdeploy(contractKey, address(ipRecordRegistry)); + + contractKey = "IPMetadataResolver"; + _predeploy(contractKey); + ipMetadataResolver = new IPMetadataResolver( + address(accessController), + address(ipRecordRegistry), + address(ipAccountRegistry), + address(licenseRegistry) + ); + _postdeploy(contractKey, address(ipMetadataResolver)); + + contractKey = "RegistrationModule"; + _predeploy(contractKey); + registrationModule = new RegistrationModule( + address(accessController), + address(ipRecordRegistry), + address(ipAccountRegistry), + address(licenseRegistry), + address(ipMetadataResolver) + ); + _postdeploy(contractKey, address(registrationModule)); + + contractKey = "TaggingModule"; + _predeploy(contractKey); + taggingModule = new TaggingModule(); + _postdeploy(contractKey, address(taggingModule)); + + contractKey = "RoyaltyModule"; + _predeploy(contractKey); + royaltyModule = new RoyaltyModule(); + _postdeploy(contractKey, address(royaltyModule)); + + contractKey = "DisputeModule"; + _predeploy(contractKey); + disputeModule = new DisputeModule(); + _postdeploy(contractKey, address(disputeModule)); + + // mockModule = new MockModule(address(ipAccountRegistry), address(moduleRegistry), "MockModule"); + } + + function _predeploy(string memory contractKey) private { + console2.log(string.concat("Deploying ", contractKey, "...")); + } + + function _postdeploy(string memory contractKey, address newAddress) private { + _writeAddress(contractKey, newAddress); + console2.log(string.concat(contractKey, " deployed to:"), newAddress); + } + + function _configureDeployment() private { + _configureAccessController(); + // _configureModuleRegistry(); + // _configureLicenseRegistry(); + // _configureIPAccountRegistry(); + // _configureIPRecordRegistry(); + } + + function _configureAccessController() private { + accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); + } +} diff --git a/script/foundry/utils/BroadcastManager.s.sol b/script/foundry/utils/BroadcastManager.s.sol new file mode 100644 index 00000000..e12c1793 --- /dev/null +++ b/script/foundry/utils/BroadcastManager.s.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { Script } from "forge-std/Script.sol"; + +import { StringUtil } from "script/foundry/utils/StringUtil.sol"; + +contract BroadcastManager is Script { + address public multisig; + address public deployer; + + function _beginBroadcast() internal { + uint256 deployerPrivateKey; + if (block.chainid == 1) { // Tenderly mainnet fork + deployerPrivateKey = vm.envUint("MAINNET_PRIVATEKEY"); + deployer = vm.envAddress("MAINNET_DEPLOYER_ADDRESS"); + multisig = vm.envAddress("MAINNET_MULTISIG_ADDRESS"); + vm.startBroadcast(deployerPrivateKey); + } else if (block.chainid == 11155111) { + deployerPrivateKey = vm.envUint("SEPOLIA_PRIVATEKEY"); + deployer = vm.envAddress("SEPOLIA_DEPLOYER_ADDRESS"); + multisig = vm.envAddress("SEPOLIA_MULTISIG_ADDRESS"); + vm.startBroadcast(deployerPrivateKey); + } else if (block.chainid == 31337) { + multisig = address(0x456); + deployer = address(0x999); + vm.startPrank(deployer); + } else { + revert("Unsupported chain"); + } + } + + function _endBroadcast() internal { + if (block.chainid == 31337) { + vm.stopPrank(); + } else { + vm.stopBroadcast(); + } + } +} diff --git a/script/foundry/utils/JsonDeploymentHandler.s.sol b/script/foundry/utils/JsonDeploymentHandler.s.sol new file mode 100644 index 00000000..dd47c99a --- /dev/null +++ b/script/foundry/utils/JsonDeploymentHandler.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { Script } from "forge-std/Script.sol"; +import { stdJson } from "forge-std/StdJson.sol"; + +import { StringUtil } from "script/foundry/utils/StringUtil.sol"; + +contract JsonDeploymentHandler is Script { + using StringUtil for uint256; + using stdJson for string; + + string output; + string readJson; + string chainId; + string key; + string internalKey = "key"; + + constructor(string memory _key) { + chainId = (block.chainid).toString(); + key = _key; + } + + function _readAddress(string memory readPath) internal returns (address) { + try vm.parseJsonAddress(readJson, readPath) returns (address addr) { + return addr; + } catch { + return address(0); + } + } + + function _readDeployment() internal { + string memory root = vm.projectRoot(); + string memory filePath = string.concat("/deployment-", (block.chainid).toString(), ".json"); + string memory path = string.concat(root, filePath); + readJson = vm.readFile(path); + } + + function _writeAddress(string memory contractKey, address newAddress) internal { + output = vm.serializeAddress(internalKey, contractKey, newAddress); + } + + function _writeToJson(string memory contractKey, string memory value) internal { + vm.writeJson(value, string.concat("./deployment-", chainId, ".json"), contractKey); + } + + function _writeDeployment(string memory prePath) internal { + vm.writeJson(output, string.concat(prePath, "/./deployment-", chainId, ".json"), string.concat(".", key)); + } +} diff --git a/script/foundry/utils/StringUtil.sol b/script/foundry/utils/StringUtil.sol new file mode 100644 index 00000000..8da5e011 --- /dev/null +++ b/script/foundry/utils/StringUtil.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +/// @title String Utility Library +library StringUtil { + /// @dev Converts a uint256 into a string. + function toString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } +} diff --git a/script/hardhat/deployment/00-deploy-main.ts b/script/hardhat/deployment/00-deploy-main.ts new file mode 100644 index 00000000..b1f0cc36 --- /dev/null +++ b/script/hardhat/deployment/00-deploy-main.ts @@ -0,0 +1,215 @@ +import { JsonRpcProvider } from "ethers" +import fs from "fs" +import hre from "hardhat" +import { DeployResult, DeployOptions } from "hardhat-deploy/dist/types" +import path from "path" + +import { verify } from "../utils/verify" + +const libraries: { [key: string]: string } = {} +const deploys: { [key: string]: { address: string; args?: any[] } } = {} + +const ERC6551_REGISTRY = "0x000000006551c19487814612e58FE06813775758" + +async function deployMain() { + // DeployFunction + const { deployments, getNamedAccounts, network } = hre + const { deploy: deployFn } = deployments + const { deployer } = await getNamedAccounts() + // const deployerSigner = await ethers.getSigner(deployer) + const provider = new JsonRpcProvider((hre.network.config as { url: string }).url) + + const defaultDeployOpts: DeployOptions = { + from: deployer, + log: true, + waitConfirmations: 1, + } + + const deployLibrary = async (libraryKey: string): Promise => { + console.log("/*////////////////////////////////////////////////////////////*/") + console.log(`Deploying ${libraryKey}...`) + const deployRes: DeployResult = await deployFn(libraryKey, { ...defaultDeployOpts }) + libraries[libraryKey] = deployRes.address + console.log(`Deployed ${libraryKey} to: `, deployRes.address) + + await hre.tenderly.persistArtifacts({ + name: libraryKey, + address: deployRes.address, + }) + + return deployRes.address + } + + const deployContract = async (contractKey: string, extraDeployOpts?: Partial): Promise => { + console.log("/*////////////////////////////////////////////////////////////*/") + console.log(`Deploying ${contractKey}...`) + const deployRes: DeployResult = await deployFn(contractKey, { ...defaultDeployOpts, ...extraDeployOpts }) + deploys[contractKey] = { address: deployRes.address, args: deployRes.args } + console.log(`Deployed ${contractKey} to: `, deployRes.address) + + await hre.tenderly.persistArtifacts({ + name: contractKey, + address: deployRes.address, + }) + + return deployRes.address + } + + console.log( + `/*////////////////////////////////////////////////////////////\n + \n Network: ${network.name} + \n////////////////////////////////////////////////////////////*/` + ) + + await _saveStateBeforeDeploy(provider) + await _deployLibaries(deployLibrary) + await _deployContracts(deployContract) + // await _verifyAll() + await _postDeploy() +} + +async function _saveStateBeforeDeploy(provider: JsonRpcProvider) { + // Save this checkpoint to revert back to this state anytime after deployment + const checkpoint = (await provider.send("evm_snapshot", [])) as string + console.log("Checkpoint created:", checkpoint) + + const outPath = path.join(__dirname, "../../out") + const checkpointPath = path.join(outPath, "checkpoint.json") + fs.writeFileSync(checkpointPath, JSON.stringify({ checkpoint }, null, 2)) +} + +async function _deployLibaries(deployLibrary: any) { + // do one by one to keep nonce in order + await deployLibrary("AccessPermission") + await deployLibrary("Errors") + await deployLibrary("IP") + await deployLibrary("Licensing") + await deployLibrary("IPAccountChecker") +} + +async function _deployContracts(deployContract: any) { + let contractKey: string + + contractKey = "AccessController" + const accessController = await deployContract(contractKey, { + libraries: { + AccessPermission: libraries.AccessPermission, + Errors: libraries.Errors, + IPAccountChecker: libraries.IPAccountChecker, + }, + }) + + contractKey = "IPAccountImpl" + const implementation = await deployContract(contractKey) + + contractKey = "ModuleRegistry" + const moduleRegistry = await deployContract(contractKey, { + libraries: { + Errors: libraries.Errors, + }, + }) + + contractKey = "LicenseRegistry" + const licenseRegistry = await deployContract(contractKey, { + args: ["https://example.com/{id}.json"], + libraries: { + Errors: libraries.Errors, + Licensing: libraries.Licensing, + }, + }) + + contractKey = "IPAccountRegistry" + const ipAccountRegistry = await deployContract(contractKey, { + args: [ERC6551_REGISTRY, accessController, implementation], + }) + + contractKey = "IPRecordRegistry" + const ipRecordRegistry = await deployContract(contractKey, { + args: [moduleRegistry, ipAccountRegistry], + libraries: { + Errors: libraries.Errors, + }, + }) + + contractKey = "IPMetadataResolver" + const ipMetadataResolver = await deployContract(contractKey, { + args: [accessController, ipRecordRegistry, ipAccountRegistry, licenseRegistry], + libraries: { + Errors: libraries.Errors, + IP: libraries.IP, + }, + }) + + contractKey = "RegistrationModule" + await deployContract(contractKey, { + args: [accessController, ipRecordRegistry, ipAccountRegistry, licenseRegistry, ipMetadataResolver], + libraries: { + Errors: libraries.Errors, + IP: libraries.IP, + }, + }) + + contractKey = "TaggingModule" + await deployContract(contractKey, { + libraries: { + Errors: libraries.Errors, + }, + }) + + contractKey = "RoyaltyModule" + await deployContract(contractKey, { + libraries: { + Errors: libraries.Errors, + }, + }) + + contractKey = "DisputeModule" + await deployContract(contractKey, { + libraries: { + Errors: libraries.Errors, + }, + }) +} + +async function _postDeploy() { + // write content of deploys and libraries to out file + const outPath = path.join(__dirname, "../../out") + const deploysPath = path.join(outPath, "deploys.json") + const librariesPath = path.join(outPath, "libraries.json") + const deploysContent = JSON.stringify(deploys, null, 2) + const librariesContent = JSON.stringify(libraries, null, 2) + fs.writeFileSync(deploysPath, deploysContent) + fs.writeFileSync(librariesPath, librariesContent) + + // combine deploys and libraries to all + const deployAddresses: { [key: string]: string } = {} + Object.keys(deploys).forEach((ck) => { + deployAddresses[ck] = deploys[ck].address + }) + + // console.log("deploys", deploys) + // console.log("deployAddresses", deployAddresses) + const allAddresses = { contracts: { ...deployAddresses }, libraries } + const allPath = path.join(outPath, "all.json") + const allContent = JSON.stringify(allAddresses, null, 2) + fs.writeFileSync(allPath, allContent) +} + +async function _verifyAll() { + const proms = Promise.all(Object.keys(deploys).map((ck) => verify(deploys[ck].address, deploys[ck].args || []))) + return proms + .then((res) => { + console.log("Verified all contracts!") + }) + .catch((err) => { + console.log("Error verifying contracts:", err) + }) +} + +// export default deployMain +// deployMain.tags = ["all"] + +deployMain().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/script/hardhat/deployment/01-deploy-mock.ts b/script/hardhat/deployment/01-deploy-mock.ts new file mode 100644 index 00000000..8b4e23d9 --- /dev/null +++ b/script/hardhat/deployment/01-deploy-mock.ts @@ -0,0 +1,17 @@ +import hre from "hardhat" + +import { MockAsset } from "../utils/mock-assets" + +async function runMock() { + const { deployments, getNamedAccounts, ethers } = hre + const { deploy: deployFn } = deployments + const { deployer } = await getNamedAccounts() + const deployerSigner = await ethers.getSigner(deployer) + + await MockAsset.deploy(deployFn, deployer, deployerSigner) +} + +runMock().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/script/hardhat/post-deployment/00-combined-flow.ts b/script/hardhat/post-deployment/00-combined-flow.ts new file mode 100644 index 00000000..27d44547 --- /dev/null +++ b/script/hardhat/post-deployment/00-combined-flow.ts @@ -0,0 +1,74 @@ +import type { AddressLike, BytesLike, ContractTransactionReceipt } from "ethers" +import hre from "hardhat" +import { DeployResult } from "hardhat-deploy/dist/types" + +import * as deployedAll from "../../out/all.json" +import { type DeployedContracts, getDeployedContracts } from "../utils/deployed" +import { MockAsset } from "../utils/mock-assets" +import { Licensing } from "../../../typechain/contracts/registries/LicenseRegistry" + +async function runCombinedFlow() { + const { deployments, getNamedAccounts, network, ethers } = hre + const { deployer } = await getNamedAccounts() + const deployerSigner = await ethers.getSigner(deployer) + const chainId = network.config.chainId as number + + const c = getDeployedContracts(deployerSigner) + const ma = new MockAsset(deployerSigner) + + await ma.mint20(deployer, 100) + let tokenId = await ma.mint721(deployer) + + // grant access control + // register IPAccount through registration module + _createLicenseFrameworks(c, { + licenseUrl: "https://example.com/license/{id}.json", + }) + // create policy from framework + // attach policy to IPAccount + // mint licenses + // link IPAccounts to parents using licenses + + let tx = await c.IPRecordRegistry.createIPAccount(chainId, ma.addr.MockNFT, tokenId) + console.log(tx) +} + +interface CreateLicenseFrameworkParams { + minting?: { + paramVerifiers: AddressLike[] + paramDefaultValues: BytesLike[] + } + activation?: { + paramVerifiers: AddressLike[] + paramDefaultValues: BytesLike[] + } + linkParent?: { + paramVerifiers: AddressLike[] + paramDefaultValues: BytesLike[] + } + defaultNeedsActivation?: boolean + licenseUrl: string +} + +async function _createLicenseFrameworks(c: DeployedContracts, p: CreateLicenseFrameworkParams) { + const fwParams: Licensing.FrameworkCreationParamsStruct = { + mintingParamVerifiers: p.minting?.paramVerifiers || [], + mintingParamDefaultValues: p.minting?.paramDefaultValues || [], + activationParamVerifiers: p.activation?.paramVerifiers || [], + activationParamDefaultValues: p.activation?.paramDefaultValues || [], + defaultNeedsActivation: p.defaultNeedsActivation || false, + linkParentParamVerifiers: p.linkParent?.paramVerifiers || [], + linkParentParamDefaultValues: p.linkParent?.paramDefaultValues || [], + licenseUrl: p.licenseUrl, + } + + const txRes = await c.LicenseRegistry.addLicenseFramework(fwParams) + const receipt = await txRes.wait() as ContractTransactionReceipt + receipt.logs.find((l) => l.topics[0] === c.LicenseRegistry.interface.getEvent("LicenseFrameworkCreated").topicHash) + const event = receipt.events?.find((e) => e.event === "LicenseFrameworkCreated") +} + +runCombinedFlow().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/script/hardhat/post-deployment/00-mint-mock.ts b/script/hardhat/post-deployment/00-mint-mock.ts new file mode 100644 index 00000000..93ece330 --- /dev/null +++ b/script/hardhat/post-deployment/00-mint-mock.ts @@ -0,0 +1,21 @@ +import hre from "hardhat" + +import * as mockAddresses from "../../out/mock.json" +import { MockERC20__factory, MockERC721__factory } from "../../../typechain" + +async function runFlow() { + const { getNamedAccounts, ethers } = hre + const { deployer } = await getNamedAccounts() + const deployerSigner = await ethers.getSigner(deployer) + + const MockToken = MockERC20__factory.connect(mockAddresses.MockToken, deployerSigner) + const MockNFT = MockERC721__factory.connect(mockAddresses.MockNFT, deployerSigner) + + await MockToken.mint(deployer, 100 * (await MockToken.decimals())) + await MockNFT.mint(deployer) +} + +runFlow().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/script/hardhat/post-deployment/01-create-ipaccounts.ts b/script/hardhat/post-deployment/01-create-ipaccounts.ts new file mode 100644 index 00000000..e69de29b diff --git a/script/hardhat/post-deployment/02-create-policies.ts b/script/hardhat/post-deployment/02-create-policies.ts new file mode 100644 index 00000000..e69de29b diff --git a/script/hardhat/post-deployment/03-create-licenses.ts b/script/hardhat/post-deployment/03-create-licenses.ts new file mode 100644 index 00000000..e69de29b diff --git a/script/hardhat/post-deployment/04-link-ipaccounts.ts b/script/hardhat/post-deployment/04-link-ipaccounts.ts new file mode 100644 index 00000000..e69de29b diff --git a/script/hardhat/post-deployment/05-tag-ipaccounts.ts b/script/hardhat/post-deployment/05-tag-ipaccounts.ts new file mode 100644 index 00000000..e69de29b diff --git a/script/hardhat/post-deployment/99-revert-chain.ts b/script/hardhat/post-deployment/99-revert-chain.ts new file mode 100644 index 00000000..d74a86e4 --- /dev/null +++ b/script/hardhat/post-deployment/99-revert-chain.ts @@ -0,0 +1,19 @@ +import { JsonRpcProvider } from "ethers" +import hre from "hardhat" +import { DeployResult, DeployOptions } from "hardhat-deploy/dist/types" + +import * as checkpointJson from "../../out/checkpoint.json" + +async function revertChainToCheckpoint() { + const provider = new JsonRpcProvider((hre.network.config as { url: string }).url) + const checkpoint = checkpointJson.checkpoint + + await provider.send("evm_revert", [checkpoint]) + + console.log(`Reverted to checkpoint: ${checkpoint}`) +} + +revertChainToCheckpoint().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/script/hardhat/pre-deployment/00-fund-accounts.ts b/script/hardhat/pre-deployment/00-fund-accounts.ts new file mode 100644 index 00000000..70625c5c --- /dev/null +++ b/script/hardhat/pre-deployment/00-fund-accounts.ts @@ -0,0 +1,35 @@ +import * as ethers from "ethers" +import hre from "hardhat" + +require("dotenv").config() + +import { USERS } from "../utils/constants" + +async function runFundAccounts() { + const provider = new ethers.JsonRpcProvider((hre.network.config as { url: string }).url) + + if (!process.env.MAINNET_DEPLOYER_ADDRESS) throw new Error("MAINNET_DEPLOYER_ADDRESS not set") + const wallets: string[] = [ + process.env.MAINNET_DEPLOYER_ADDRESS as string, + USERS.ALICE, + USERS.BOB, + USERS.CARL, + USERS.EVE, + ] + + await provider.send("tenderly_setBalance", [ + wallets, + // amount in wei will be set for ALL wallets + ethers.toQuantity(ethers.parseUnits("10000", "ether")), + ]) + + for (const wallet of wallets) { + const balance = await provider.getBalance(wallet) + console.log(`Balance of ${wallet} is ${ethers.formatEther(balance)}`) + } +} + +runFundAccounts().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/script/hardhat/utils/constants.ts b/script/hardhat/utils/constants.ts new file mode 100644 index 00000000..8e1c8bfc --- /dev/null +++ b/script/hardhat/utils/constants.ts @@ -0,0 +1,18 @@ +/// +/// !!!! WARNING !!!! +/// DO NOT USE THESE ADDRESSES IN PRODUCTION +/// THESE ADDRESSES ARE FOR TESTING ONLY +/// THE PRIVATE KEYS ARE FROM COMMON ANVIL MNEMONIC +/// 'test test test test test test test test test test test junk' +/// + +export const USERS = { + ALICE: "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", + ALICE_PRIVATE_KEY: "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", + BOB: "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", + BOB_PRIVATE_KEY: "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", + CARL: "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", + CARL_PRIVATE_KEY: "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", + EVE: "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + EVE_PRIVATE_KEY: "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", +} diff --git a/script/hardhat/utils/deployed.ts b/script/hardhat/utils/deployed.ts new file mode 100644 index 00000000..48d73734 --- /dev/null +++ b/script/hardhat/utils/deployed.ts @@ -0,0 +1,59 @@ +import * as deployedAll from "../../out/all.json" +import { + DisputeModule, + DisputeModule__factory, + IERC6551Registry, + IERC6551Registry__factory, + IPAccountRegistry, + IPAccountRegistry__factory, + IPMetadataResolver, + IPMetadataResolver__factory, + IPRecordRegistry, + IPRecordRegistry__factory, + LicenseRegistry, + LicenseRegistry__factory, + ModuleRegistry, + ModuleRegistry__factory, + RegistrationModule, + RegistrationModule__factory, + RoyaltyModule, + RoyaltyModule__factory, + TaggingModule, + TaggingModule__factory, +} from "../../../typechain" + +export interface DeployedContracts { + // Registries + ERC6551Registry: IERC6551Registry + IPAccountRegistry: IPAccountRegistry + IPRecordRegistry: IPRecordRegistry + LicenseRegistry: LicenseRegistry + ModuleRegistry: ModuleRegistry + // Resolvers + IPMetadataResolver: IPMetadataResolver + // Modules + DisputeModule: DisputeModule + RoyaltyModule: RoyaltyModule + TaggingModule: TaggingModule + RegistrationModule: RegistrationModule +} + +export function getDeployedContracts(deployerSigner: any) { + const ERC6551_REGISTRY = "0x000000006551c19487814612e58FE06813775758" // v0.3.1 Ethereum + + return { + // Registries + ERC6551Registry: IERC6551Registry__factory.connect(ERC6551_REGISTRY, deployerSigner), + IPAccountRegistry: IPAccountRegistry__factory.connect(deployedAll.contracts.IPAccountRegistry, deployerSigner), + IPRecordRegistry: IPRecordRegistry__factory.connect(deployedAll.contracts.IPRecordRegistry, deployerSigner), + LicenseRegistry: LicenseRegistry__factory.connect(deployedAll.contracts.LicenseRegistry, deployerSigner), + ModuleRegistry: ModuleRegistry__factory.connect(deployedAll.contracts.ModuleRegistry, deployerSigner), + // Resolvers + IPMetadataResolver: IPMetadataResolver__factory.connect(deployedAll.contracts.IPMetadataResolver, deployerSigner), + // Modules + DisputeModule: DisputeModule__factory.connect(deployedAll.contracts.DisputeModule, deployerSigner), + RoyaltyModule: RoyaltyModule__factory.connect(deployedAll.contracts.RoyaltyModule, deployerSigner), + TaggingModule: TaggingModule__factory.connect(deployedAll.contracts.TaggingModule, deployerSigner), + RegistrationModule: RegistrationModule__factory.connect(deployedAll.contracts.RegistrationModule, deployerSigner), + } +} diff --git a/script/hardhat/utils/interfaces.ts b/script/hardhat/utils/interfaces.ts new file mode 100644 index 00000000..a58766b0 --- /dev/null +++ b/script/hardhat/utils/interfaces.ts @@ -0,0 +1,8 @@ +// https://github.com/dethcrypto/TypeChain/blob/master/packages/target-ethers-v5/static/common.ts +import type { EventFilter } from "ethers" + +export interface TypedEvent = any, TArgsObject = any> extends Event { + args: TArgsArray & TArgsObject +} + +export interface TypedEventFilter<_TEvent extends TypedEvent> extends EventFilter {} diff --git a/script/hardhat/utils/mock-assets.ts b/script/hardhat/utils/mock-assets.ts new file mode 100644 index 00000000..aa2910e2 --- /dev/null +++ b/script/hardhat/utils/mock-assets.ts @@ -0,0 +1,96 @@ +import { Contract, ContractTransactionReceipt } from "ethers" +import { DeployResult } from "hardhat-deploy/dist/types" +import * as fs from "fs" +import * as path from "path" + +import * as mockTokens from "../../out/mock/tokens.json" +import { MockERC20, MockERC20__factory, MockERC721, MockERC721__factory } from "../../../typechain" + +const erc721MintAbi = [ + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + ], + name: "mint", + outputs: [ + { + internalType: "uint256", + name: "id", + type: "uint256", + }, + ], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, +] + +export class MockAsset { + public MockToken: MockERC20 + public MockNFT: MockERC721 + public addr: { MockToken: string; MockNFT: string } + + private deployerSigner: any + + constructor(deployerSigner: any) { + this.deployerSigner = deployerSigner + this.MockToken = MockERC20__factory.connect(mockTokens.MockToken, deployerSigner) + this.MockNFT = MockERC721__factory.connect(mockTokens.MockNFT, deployerSigner) + + this.addr = { + MockToken: mockTokens.MockToken, + MockNFT: mockTokens.MockNFT, + } + } + + static async deploy(deployFn: any, deployer: string, deployerSigner: any) { + let deployRes: DeployResult + + deployRes = await deployFn("MockERC20", { + from: deployer, + log: true, + waitConfirmations: 1, + }) + const MockToken = MockERC20__factory.connect(deployRes.address, deployerSigner) + await MockToken.waitForDeployment() + + deployRes = await deployFn("MockERC721", { + from: deployer, + log: true, + waitConfirmations: 1, + }) + const MockNFT = MockERC721__factory.connect(deployRes.address, deployerSigner) + await MockNFT.waitForDeployment() + + // save MockToken and MockNFT addresses to json in this folder + const outMockPath = path.join(__dirname, "../../out/mock") + const mockPath = path.join(outMockPath, "tokens.json") + const mock = { MockToken: await MockToken.getAddress(), MockNFT: await MockNFT.getAddress() } + fs.writeFileSync(mockPath, JSON.stringify(mock, null, 2)) + + return new MockAsset(deployerSigner) + } + + async mint20(recipient: string, amount: number): Promise { + const decimals = BigInt(await this.MockToken.decimals()) + const txRes = await this.MockToken.mint(recipient, BigInt(amount) * decimals) + await txRes.wait() + return this.MockToken.balanceOf(recipient) + } + + async mint721(recipient: string): Promise { + const tokenAddr = await this.MockNFT.getAddress() + const txRes = await this.MockNFT.mint(recipient) + const receipt = (await txRes.wait()) as ContractTransactionReceipt + + // get token ID from the receipt + const newTokenId = receipt.logs?.[0].topics?.[3] + if (!newTokenId) throw new Error("tokenId not found in receipt") + + return BigInt(newTokenId) + } +} diff --git a/script/hardhat/utils/verify.ts b/script/hardhat/utils/verify.ts new file mode 100644 index 00000000..de4bdf04 --- /dev/null +++ b/script/hardhat/utils/verify.ts @@ -0,0 +1,20 @@ +import { run } from "hardhat" + +export const verify = async (contractAddress: string, args: any[]) => { + console.log("Verifying contract...") + + try { + await run("verify:verify", { + address: contractAddress, + constructorArguments: args, + }) + console.log("Contract verified!") + } catch (error: any) { + if (error.message.toLowerCase().includes("already verified")) { + console.log("Already Verified!") + } else { + console.log("Verification failed!") + console.log(error) + } + } +} diff --git a/tenderly.yaml b/tenderly.yaml new file mode 100644 index 00000000..23e27f84 --- /dev/null +++ b/tenderly.yaml @@ -0,0 +1,3 @@ +account_id: 179b2237-715d-4ea3-9c29-dfa0145fd536 +project_slug: story/beta-test +provider: Hardhat diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..6d2760ef --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + /* Basic Options */ + "incremental": true, + "target": "esnext", + "module": "commonjs", + "outDir": "dist", + "pretty": true, + "removeComments": true, + + /* Strict Type-Checking Options */ + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": false, + "alwaysStrict": true, + + /* Additional Checks */ + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": false, + "skipLibCheck": true, + + /* Module Resolution Options */ + "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "types": ["jest", "node"], + "baseUrl": "src", + "paths": { + "@gtp/*": ["./*"] + } + }, + "include": ["scripts/**/*"], + "exclude": ["node_modules"] +}