From 028a2b9be5a65913e881499975d2eee24e0385fc Mon Sep 17 00:00:00 2001 From: Hades Date: Thu, 6 Nov 2025 15:20:25 +0800 Subject: [PATCH 1/7] readme and contribute --- CONTRIBUTE.md | 29 + README.md | 1468 ++++--------------------------------------------- 2 files changed, 132 insertions(+), 1365 deletions(-) create mode 100644 CONTRIBUTE.md diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md new file mode 100644 index 0000000..7fc06e6 --- /dev/null +++ b/CONTRIBUTE.md @@ -0,0 +1,29 @@ +## How to Contribute + +We welcome contributions from everyone! Contributions to `sunhat` are released under the terms of the [MIT License](https://github.com/sun-protocol/sunhat/blob/main/LICENSE). By submitting a pull request, you agree to license your contribution under this license. + +### Reporting Bugs + +If you find a bug, please open an issue on our GitHub repository. When you are creating a bug report, please include: + +* A clear and descriptive title. +* A detailed description of the problem, including the expected and actual behavior. +* Steps to reproduce the bug. +* Information about your environment (e.g., operating system, `sunhat` version). + +### Suggesting Enhancements + +If you have an idea for a new feature or an improvement to an existing one, please open an issue to discuss it. This allows us to coordinate our efforts and prevent duplication of work. + +### Pull Request Process + +We follow a standard GitHub fork and pull request workflow. + +1. **Fork the repository** to your own GitHub account. +2. **Clone your fork** to your local machine. +3. **Create a new branch** for your changes. It's good practice to name your branch something descriptive, like `fix-authentication-bug` or `add-user-profiles-feature`. +4. **Make your changes**. Ensure that your code adheres to the project's coding standards. +5. **Write clear and concise commit messages**. A good commit message explains why a change was made. +6. **Push your changes** to your fork on GitHub. +7. **Open a pull request** from your branch to the `main` branch of the `sunhat` repository. +8. **Respond to feedback**. The project maintainers will review your pull request and may suggest changes. Be open to discussion and be prepared to make further modifications. diff --git a/README.md b/README.md index dcc03da..3cdc6ff 100644 --- a/README.md +++ b/README.md @@ -7,60 +7,6 @@ Sunhat is a comprehensive [Hardhat](https://hardhat.org) plugin designed to provide a seamless, end-to-end development experience on [TRON](https://tron.network/). It manages every stage of your project—from writing code in multiple languages to deploying with confidence—all within a single, unified workflow. -- [What is it for?](#what-is-it-for) - - [A Closed-Loop Lifecycle: Develop, Test, Deploy](#a-closed-loop-lifecycle-develop-test-deploy) - - [Core Features at a Glance](#core-features-at-a-glance) -- [Sunhat in a nutshell](#Sunhat-in-a-nutshell) -- [Installation](#installation) - - [npm install](#npm-install) - - [TypeScript support](#typescript-support) - - [Migrating existing deployment to Sunhat](#migrating-existing-deployment-to-Sunhat) -- [Sunhat Tasks Available/Updated](#hardhat-tasks-availableupdated) - - [1. hardhat deploy](#1-Sunhat) - - [**Options**](#options) - - [**Flags**](#flags) - - [2. hardhat node](#2-hardhat-node) - - [**Options**](#options-1) - - [**Flags**](#flags-1) - - [3. hardhat test](#3-hardhat-test) -- [Hardhat Environment Extensions](#hardhat-environment-extensions) -- [Configuration](#configuration) - - [**1. namedAccounts (ability to name addresses)**](#1-namedaccounts-ability-to-name-addresses) - - [**2. extra hardhat.config networks' options**](#2-extra-hardhatconfig-networks-options) - - [`live`](#live) - - [`saveDeployments`](#savedeployments) - - [`tags`](#tags) - - [`deploy`](#deploy) - - [`companionNetworks`](#companionnetworks) - - [**3. extra hardhat.config paths' options**](#3-extra-hardhatconfig-paths-options) - - [**4. compiler options**](#4-compiler-options) - - [**5. deterministicDeployment (ability to specify a deployment factory)**](#5-deterministicdeployment-ability-to-specify-a-deployment-factory) - - [Importing deployment from other projects (with truffle support)](#importing-deployment-from-other-projects-with-truffle-support) - - [Access to Artifacts (non-deployed contract code and abi)](#access-to-artifacts-non-deployed-contract-code-and-abi) -- [How to Deploy Contracts](#how-to-deploy-contracts) - - [The `deploy` Task](#the-deploy-task) - - [Deploy Scripts](#deploy-scripts) - - [An example of a deploy script :](#an-example-of-a-deploy-script-) - - [The `deployments` field](#the-deployments-field) - - [`deployments.deploy(, options)`](#deploymentsdeployname-options) -- [Handling contract using libraries](#handling-contract-using-libraries) -- [Exporting Deployments](#exporting-deployments) -- [Deploying and Upgrading Proxies](#deploying-and-upgrading-proxies) - - [When the constructor and init functions are different](#when-the-constructor-and-init-functions-are-different) - - [Proxy deployment options](#proxy-deployment-options) -- [Builtin-In Support For Diamonds (EIP2535)](#builtin-in-support-for-diamonds-eip2535) - - [deployment / upgrade](#deployment--upgrade) - - [onUpgrade calls](#onupgrade-calls) - - [more...](#more) -- [Testing Deployed Contracts](#testing-deployed-contracts) - - [Creating Fixtures](#creating-fixtures) -- [More Information On Hardhat Tasks](#more-information-on-hardhat-tasks) - - [**1. node task**](#1-node-task) - - [**2. test task**](#2-test-task) - - [**3. run task**](#3-run-task) - - [**4. console task**](#4-console-task) -- [Deploy Scripts: Tags And Dependencies](#deploy-scripts-tags-and-dependencies) - ## What is it for? This [hardhat](https://hardhat.org) plugin adds a mechanism to deploy contracts to any network, Especially to [Tron](https://tron.network/) , keeping track of them and replicating the same environment for testing. @@ -93,1395 +39,187 @@ Sunhat creates a complete, closed-loop process, ensuring consistency and reliabi * **TRON-Specific Network Data**: Gain access to crucial network-specific parameters directly within your testing and deployment scripts, enabling more accurate pre-deployment simulations. -## Sunhat in a nutshell - -Before going into the details, here is a very simple summary of the basic feature of **Sunhat**. - -**Sunhat** allows you to write [`deploy scripts`](#deploy-scripts) in the `deployTron` folder. If you want to use evm chains, this scripts will be in `deploy` folder. Each of these files that look as follows will be executed in turn when you execute the following task: `hardhat --network --tags alice deploy` - -```js -// deployTron/1.ts -module.exports = async ({getNamedAccounts, deployments}) => { - const {deploy} = deployments; - const {deployer} = await getNamedAccounts(); - const res = await deploy('Lock', { - from: deployer, - gasLimit: 4000000, - args: [1893456000], - tags: 'lumi', - }); - console.log(res) -}; -module.exports.tags = ['lumi']; -``` - -Furthermore you can also ensure these scripts are executed in test too by calling `await deployments.fixture(['MyContract'])` in your test. -This is optimized, so if multiple tests use the same contract, the deployment will be executed once and each test will start with the exact same state. - -This is a huge benefit for testing since you are not required to replicate the deployment procedure in your tests. The tag feature (as seen in the script above) and [dependencies](#deploy-scripts-tags-and-dependencies) will also make your life easier when writing complex deployment procedures. - -You can even group deploy scripts in different sub-folders and ensure they are executed in their logical order. - -Furthermore Sunhat can also support a multi-chain settings like L1, L2 with multiple deploy folder specific to each network. +--- -**All of this can also be bundled in a npm package so users of Sunhat can reuse your deployment procedure and get started integrating with your project locally.** +## QuickStart -There is a demo covering the basics here: https://github.com/sun-protocol/sunhat-demo +This guide walks you through setting up sunhat in a new project from scratch. +> You can get a complete demo [here](https://github.com/sun-protocol/sunhat-demo) -## Prepare for TronNetwork +--- -### Get test coin +### Installation -You can get testnet tokens (TRX and TRC-20 tokens) on the TRON Shasta and Nile testnets (Recommended nile testnet). -There is a guide here: https://developers.tron.network/docs/getting-testnet-tokens-on-tron +This guide walks you through installing sunhat from scratch. -### Get API Keys for RPCs +#### Prerequisites -TronGrid provides all full-node HTTP APIs and extended APIs of the TRON network. In order to ensure reasonable allocation of requested resources, all request APIs need to carry the parameter API Key, and requests without an API Key will be severely limited or not even responded. +* **Node.js**: Version `22.14.0` or higher. + > [Download Node.js](https://nodejs.org/en/download) -#### How To Get -After logging in [Trongrid](#https://www.trongrid.io/), users can quickly create API Keys on the Dashboard or API Key list. Each API Key has its separate configuration page, and users can configure API Keys to meet different needs. +#### Install Sunhat -#### How To Use API Keys -At present, an API Key is used by adding the parameter TRON-PRO-API-KEY=API Key to the header of the request. +You can install Sunhat by executing the following command: -HTTP Examples: -``` -curl -X POST \ - https://api.trongrid.io/wallet/createtransaction \ - -H 'Content-Type: application/json' \ - -H 'TRON-PRO-API-KEY: {YOUR API KEY}' \ - -d '{ - "to_address": "41e9d79cc47518930bc322d9bf7cddd260a0260a8d", - "owner_address": "41D1E7A6BC354106CB410E65FF8B181C600FF14292", - "amount": 1000 -}' +```bash +npm install -g @sun-protocol/sunhat ``` -## Installation -### npm install +#### Install Optional Extensions +For Vyper compiler support: +> [Vyper Installation Guide](https://docs.vyperlang.org/en/stable/installing-vyper.html) ```bash -npm i @sun-protocol/sunhat +pip install vyper==0.2.8 ``` +For Foundry test support: +> [Foundry Installation Guide](https://book.getfoundry.sh/getting-started/installation) -### TypeScript support - -With hardhat the tsconfig.json is optional. - -But if you add folders to the `include` field in `tsconfig.json`, you ll also need to include `hardhat.config.ts` like : - -`"include": ["./hardhat.config.ts", "./scripts", "./deploy", "./test"]` - -for deploy script (see below) you can write them this way to benefit from typing : - -```ts -import {HardhatRuntimeEnvironment} from 'hardhat/types'; -import {DeployFunction} from '@sun-protocol/sunhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - // code here -}; -export default func; +```bash +# Install Foundry +curl -L https://foundry.paradigm.xyz | bash +# Set Foundry to the nightly channel +foundryup --install nightly ``` -### Migrating existing deployment to Sunhat - -> Only needed for an existing project that already deployed contracts and has the deployment information available **(at minimum, address and abi)** - -You might want to switch your current deployment process to use **Sunhat**. In that case you probably have some deployments saved elsewhere. - -In order to port them to **Sunhat**, you'll need to create one `.json` file per contract in the `deployments/` folder (configurable via [paths config](#extra-paths-config)). -The network folder is simply the hardhat network name (as configured in hardhat.config.js) (accessible at runtime via `hre.network.name`). -Such folder need to have a file named `.chainId` containing the chainId as decimal. - -For example for a network named "rinkeby" (for the corresponding network) the file `deployments/nile/.chainId` would be - -``` -3448148188 -``` +--- -Note, prior to hardhat 0.6 the chainId was appended to the folder name (expect for some known network name). This has changed and upgrading to 0.6 will require you to change the folder name and add the '.chainId' file. +### Step 1: Configure Hardhat -Each contract file must follow this type (as defined in [types.ts](types.ts)) : +Add sunhat to your hardhat.config.ts: ```ts -export interface Deployment { - address: Address; - abi: ABI; - receipt?: Receipt; - transactionHash?: string; - history?: Deployment[]; - numDeployments?: number; - implementation?: string; - args?: any[]; - linkedData?: any; - solcInputHash?: string; - metadata?: string; - bytecode?: string; - deployedBytecode?: string; - libraries?: Libraries; - userdoc?: any; - devdoc?: any; - methodIdentifiers?: any; - diamondCut?: FacetCut[]; - facets?: Facet[]; - storageLayout?: any; - gasEstimates?: any; -} -``` - -As you can see, only abi and address are mandatory. But having the other fields allow more feature. For example, metadata and args allow you to benefit from contract code verification. +import { HardhatUserConfig } from '@sun-protocol/sunhat'; -For Receipt, the following type is expected: - -```ts -export type Receipt = { - from: string; - transactionHash: string; - blockHash: string; - blockNumber: number; - transactionIndex: number; - cumulativeGasUsed: string; - gasUsed: string; - contractAddress?: string; - to?: string; - logs?: Log[]; - events?: any[]; - logsBloom?: string; - byzantium?: boolean; - status?: number; - confirmations?: number; +const config: HardhatUserConfig = { + solidity: { + compilers: [ + { + version: '0.8.23', + optimizer: { + enabled: true, // enabled for optimizer + runs: 999999, // runs time for optimizer run + }, + }, + ], + }, + // settings for different networks + networks: { + tron: { + url: 'https://nile.trongrid.io/jsonrpc', // tron mainnet rpc url + tron: true, // enable tron network + deploy: ['deployTron/'], // folder for tron deploy scripts + accounts: [`${process.env.PRIVATE_KEY}`], // account private key for deploy + }, + }, }; -``` - -Here is an example: - -Let's say you have: - -- 2 Contract named Greeter and Registry deployed on rinkeby -- 1 contract named Greeter on mainnet -- 2 Contract named Greeter and Registry deployed on a network named rinkeby2 -You would get the following folder structure: - -``` -deployments/ - mainnet/ - .chainId - Lock.json - rinkeby/ - .chainId - Lock.json - nile/ - .chainId - Lock.json +export default config; ``` -The reason why **Sunhat** save chainId in the `.chainId` file is both for - -- safety: so that if you were to change the network name to point to a different chain, it would not attempt to read the wrong folder and assume that a contract has been deployed while it has not. -- ability to know the chainId without requiring to be connected to a node (and so not dependent on hardhat.config.js settings). Useful for `export` task. - --- -## Hardhat Tasks Available/Updated +### Step 2: Create Your First Contract -Sunhat adds several tasks to hardhat. It also modifies existing one, adding new options and new behavior. All of these are described here: +Create a simple contract in `contracts/Lock.sol`: ---- - -### 1. hardhat deploy - ---- - -This plugin adds the _deploy_ task to Hardhat. - -This task will execute the scripts in the `deploy` folder and save the contract deployments to disk. These deployments are supposed to be saved for example in a git repository. This way they can be accessed later. But you are free to save them elsewhere and get them back via your mechanism of choice. - -With the deployment saved, it allows you to deploy a contract only if changes were made. - -Deploy scripts (also called Deploy functions) can also perform arbitrary logic. - -For further details on how to use it and write deploy script, see [section](#deploy-scripts) below. - -#### **Options** - -`--export `: export one file that contains all contracts (address, abi + extra data) for the network being invoked. The file contains the minimal information so to not bloat your front end. - -`--export-all `: export one file that contains all contracts across all saved deployments, regardless of the network being invoked. - -`--tags `: only execute deploy scripts with the given tags (separated by commas) and their dependencies (see more info [here](#deploy-scripts-tags-and-dependencies) about tags and dependencies) - -`--gasprice `: specify the gasprice (in wei) to use by default for transactions executed via **Sunhat** helpers in deploy scripts - -`--write `: default to true (except for hardhat network). If true, write deployments to disk (in deployments path, see [path config](#extra-paths-config)). - -#### **Flags** - -`--reset`: This flag resets the deployments from scratch. Previously deployed contracts are not considered and deleted from disk. - -`--silent`: This flag removes **Sunhat** log output (see log function and log options for [`hre.deployments`](#the-deployments-field)) - -`--watch`: This flag make the task never-ending, watching for file changes in the deploy scripts folder and the contract source folder. If any changes happen the contracts are recompiled and the deploy script are re-run. Combined with a proxy deployment ([Proxies](#deploying-and-upgrading-proxies) or [Diamond](#builtin-in-support-for-diamonds-eip2535)) this allow to have HCR (Hot Contract Replacement). - ---- - -### 2. hardhat node - ---- - -This plugin modifies the _node_ task so that it also executes the deployment script before exposing the server http RPC interface - -It adds similar options than the `deploy` task : - -#### **Options** - -`--export `: export one file that contains all contracts (address, abi + extra data) for the network being invoked. The file contains the minimal information so to not bloat your front end. If the extension ends in .ts it will generate a typescript file containing the contracts info. - -`--export-all `: export one file that contains all contracts across all saved deployment, regardless of the network being invoked. If the extension ends in .ts it will generate a typescript file containing the contracts info. - -`--tags `: only excutes deploy scripts with the given tags (separated by commas) and their dependencies (see more info [here](#deploy-scripts-tags-and-dependencies) about tags and dependencies) - -`--gasprice `: specify the gasprice to use by default for transactions executed via **Sunhat** helpers in deploy scripts - -`--write `: default to true (except for hardhat network). If true, write deployments to disk (in deployments path, see [path config](#extra-paths-config)). +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; -#### **Flags** +contract Lock { + uint public unlockTime; + address payable public owner; -`--no-reset`: This flag prevents the resetting of the existing deployments. This is usually not desired when running the `node` task as a network is created from scratch and previous deployments are irrelevant. + event Withdrawal(uint amount, uint when); -`--silent`: This flag removes **Sunhat** log output (see log function and log options for [`hre.deployments`](#the-deployments-field)) - -`--watch`: This flag makes the task never-ending, watching for file changes in the deploy scripts folder and the contract source folder. If any changes happen the contracts are recompiled and the deploy script are re-run. Combined with a proxy deployment ([Proxies](#deploying-and-upgrading-proxies) or [Diamond](#builtin-in-support-for-diamonds-eip2535)) this allows to have HCR (Hot Contract Replacement). - -`--no-deploy` that discard all other options to revert to normal `hardhat node` behavior without any deployment being performed. - -> :warning: Note that the deployments are saved as if the network name is `localhost`. This is because `hardhat node` is expected to be used as localhost: You can for example execute `hardhat --network lohe configuration from `hardhat` in the hardhat.config.js file though.rdhat console` would indeed not do anything useful. It still takes t - ---- - -### 3. hardhat test - ---- - -This plugin adds a flag argument `--deploy-fixture` to the _test_ task which if enabled will run the global deployments fixture before the tests and snapshots it. This will generally speed up the tests as further test will be able to revert back to the full deployment. - -> :warning: Note though that if your test behave differently whether that option is on or not, this most likely means that your deploy scripts' tags and dependencies are not configured correctly. This is because the global fixture will ensure all contracts are deployed while test will usually (for efficiency) ask for a particular tag. - - -## Hardhat Environment Extensions - -This plugin extends the Hardhat Runtime Environment by adding 4 fields: - -- `getNamedAccounts: () => Promise<{ [name: string]: string }>`: a function returning an object whose keys are names and values are addresses. It is parsed from the `namedAccounts` configuration (see [Configuration](#configuration)). - -- `getUnnamedAccounts: () => Promise`: accounts which has no names, useful for test where you want to be sure that the account is not one of the predefined one - -- `deployments`: contains functions to access past deployments or to save new ones, as well as helpers functions. - -- `getChainId(): Promise`: offer an easy way to fetch the current chainId. - ---- - - -## Configuration - -### **1. namedAccounts (ability to name addresses)** - ---- - -This plugin extends the `HardhatConfig`'s object with an optional `namedAccounts` field. - -`namedAccounts` allows you to associate names to addresses and have them configured per chain. -This allows you to have meaningful names in your tests while the addresses match to multi sig in real network for example. - -```js -{ - namedAccounts: { - deployer: { - default: 0, // here this will by default take the first account as deployer - 1: 0, // similarly on mainnet it will take the first account as deployer. Note though that depending on how hardhat network are configured, the account 0 on one network can be different than on another - 4: '0xA296a3d5F026953e17F472B497eC29a5631FB51B', // but for rinkeby it will be a specific address - "goerli": '0x84b9514E013710b9dD0811c9Fe46b837a4A0d8E0', //it can also specify a specific netwotk name (specified in hardhat.config.js) - }, - feeCollector:{ - default: 1, // here this will by default take the second account as feeCollector (so in the test this will be a different account than the deployer) - 1: '0xa5610E1f289DbDe94F3428A9df22E8B518f65751', // on the mainnet the feeCollector could be a multi sig - 4: '0xa250ac77360d4e837a13628bC828a2aDf7BabfB3', // on rinkeby it could be another account - } + constructor(uint _unlockTime) payable { + require(_unlockTime > block.timestamp, "Unlock time should be in the future"); + unlockTime = _unlockTime; + owner = payable(msg.sender); } -} -``` - - - ---- - -### **2. extra hardhat.config networks' options** - ---- - -**Sunhat** add 5 new fields to `networks` configuration - -#### `live` - -this is not used internally but is useful to perform action on a network whether it is a live network (rinkeby, mainnet, etc) or a temporary one (localhost, hardhat). The default is true (except for localhost and hardhat where the default is false). - -#### `saveDeployments` -this tell whether **Sunhat** should save the deployments to disk or not. Default to true, except for the hardhat network. - -#### `tags` - -network can have tags to represent them. The config is an array and at runtime the hre.network.tags is an object whose fields (the tags) are set to true. - -This is useful to conditionally operate on network based on their use case. - -Example: - -```js -{ - networks: { - localhost: { - live: false, - saveDeployments: true, - tags: ["local"] - }, - hardhat: { - live: false, - saveDeployments: true, - tags: ["test", "local"] - }, - nile: { - live: true, - saveDeployments: true, - tags: ["staging"] + function withdraw() public { + require(block.timestamp >= unlockTime, "You can't withdraw yet"); + require(msg.sender == owner, "You aren't the owner"); + emit Withdrawal(address(this).balance, block.timestamp); + owner.transfer(address(this).balance); } - } } ``` -#### `deploy` - -the deploy field overrides the paths.deploy option and let you define a set of folder containing the deploy scripts to be executed for this network. - -You can thus have one network that will be executing L1 deployment and other L2 deployments, etc... - -You could also have a folder that deploy contracts that are live on mainnet but that you need to replicate for your test or local network. - -Example: - -```js -{ - networks: { - mainnet: { - deploy: [ 'deploy/' ] - }, - rinkeby: { - deploy: [ 'testnet-deploy/' ] - }, - nile: { - url: "https://nile.trongrid.io/jsonrpc", - tron: true, - deploy: ['deployTron/'], - accounts: [`${process.env.PRIVATE_KEY}`], - }, - } -} -``` - -#### `companionNetworks` - -the companionNetworks field is an object whose key is any name you desire and the value is the name of a network that will be accessible inside the deploy script. For example: +--- -```js -{ - ... - networks: { - optimism: { - url: 'http://127.0.0.1:8545', - ovm: true, - companionNetworks: { - l1: 'localhost', - }, - } - } - ... -} -``` +### Step 3: Create Deploy Directory -By using name you can have the same deploy script used in different set of network. +Create a `deployTron/` directory in your project root. -For your test you could have the companion networks pointing to the same hardhat network, for test deployment, you could have rinkeby acting like your l2 while goerli act as your l1. +--- -An example repo that showcase a multi-network setup with optimism can be found here: https://github.com/wighawag/template-ethereum-contracts/tree/examples/optimism +### Step 4: Write Your First Deploy Script -deploy script can then access the network and its deployment as follow : +Create `deployTron/1.ts`: -```js +```ts module.exports = async ({ getNamedAccounts, deployments, getChainId, getUnnamedAccounts, }) => { - const {deploy, execute} = deployments; + const {deploy} = deployments; const {deployer} = await getNamedAccounts(); - const OVM_L1ERC20Gateway = await hre.companionNetworks['l1'].deployments.get( - 'OVM_L1ERC20Gateway' - ); // layer 1 - - await execute( - 'SimpleERC20_OVM', - {from: deployer, log: true}, - 'init', - OVM_L1ERC20Gateway.address - ); + // the following will only deploy "GenericMetaTxProcessor" if the contract was never deployed or if the code changed since last deployment + const res = await deploy('Lock', { + from: deployer, + gasLimit: 4000000, + args: [1893456000], + tags: 'lumi', + }); + console.log(res) }; -``` - ---- - -### **3. extra hardhat.config paths' options** - ---- - -`Sunhat` also adds fields to `HardhatConfig`'s `ProjectPaths` object. - -Here is an example showing the default values : - -```js -{ - paths: { - deploy: 'deploy', - deployments: 'deployments', - imports: 'imports' - } -} -``` - -The `deploy` folder is expected to contain the deploy scripts that are executed upon invocation of `hardhat deploy` or `hardhat node`. -It can also be an array of folder path. - -The `deployments` folder will contain the resulting deployments (contract addresses along their abi, bytecode, metadata...). One folder per network and one file per contract. - -The `imports` folder is expected to contain artifacts that were pre-compiled. Useful if you want to upgrade to a new solidity version but want to keep using previously compiled contracts. The artifact is the same format as normal hardhat artifact, so you can easily copy them over, before switching to a new compiler version. - -> This is less useful now that hardhat support multiple solidity compiler at once. - ---- - ---- - -### **4. compiler options** - ---- - -This plugin extends the `HardhatConfig`'s object with `solidity` and `vyper` compiler field. - -This feature supports compiling smart contracts written in both Solidity and Vyper, including the use of multiple compiler versions for each language. -Here is an example showing how to config compilers: - -```js -{ - tronSolc: { - enable: true, //if using the tronSolc - }, - solidity: { - compilers: [ - { - "version": "0.8.23", - "optimizer": { - enabled: true, - runs: 999999, - } - }, - { - "version": "0.8.22", - "optimizer": { - enabled: true, - runs: 999999, - } - }, - ] - }, - vyper: { - compilers: [ - { "version": "0.2.8" }, - { "version": "0.3.10" } - ] - } -} +module.exports.tags = ['lumi']; ``` --- -### **5. deterministicDeployment (ability to specify a deployment factory)** - ---- - -This plugin extends the `HardhatConfig`'s object with an optional `deterministicDeployment` field. - -`deterministicDeployment` allows you to associate information that are used on each network for deterministic deployment. The information for each deterministic deployment consist out of a `factory`, a `deployer`, the required `funding` and a `signedTx` to deploy the factory. The default deterministic deployment used is the [Deterministic Deployment Proxy](https://github.com/Arachnid/deterministic-deployment-proxy). The factory expects a 32 bytes `salt` concatenated with the deployment data (see [EIP-1014](https://eips.ethereum.org/EIPS/eip-1014) for more information on these parameters). +### Step 5: Compile and Deploy -Using the `deterministicDeployment` it is possible to define a different setup for the deterministic deployment. One use case for this is the deterministic deployment on networks that required replay protection (such as Celo or Avalanche). The [Deterministic Deployment Proxy](https://github.com/Arachnid/deterministic-deployment-proxy) can only be deployed on networks that don't enforce replay protection, therefore on other networks an alternative library has to be used. An example for this would be the [Safe Singleton Factory](https://github.com/gnosis/safe-singleton-factory) that is an adjusted version of the [Deterministic Deployment Proxy](https://github.com/Arachnid/deterministic-deployment-proxy) that contains signed transactions that include replay protection. +Compile your contracts: -The information can be defined either as an object - -```js -{ - deterministicDeployment: { - "4": { - factory: "", - deployer: "", - funding: "", - signedTx: "", - } - } -} -``` - -or as a function that returns the information for the deterministic deployment - -```js -{ - deterministicDeployment: (network: string) => { - return { - factory: '', - deployer: '', - funding: '', - signedTx: '', - }; - }; -} +```bash +npx hardhat compile ``` -### Importing deployment from other projects (with truffle support) - -`Sunhat` also add the `external` field to `HardhatConfig` +Deploy to tron network: -Such field allows to specify paths for external artifacts or **deployments**. - -The external object has 2 fields: - -```js -{ - external: { - contracts: [ - { - artifacts: "node_modules/@cartesi/arbitration/export/artifacts", - deploy: "node_modules/@cartesi/arbitration/export/deploy" - }, - { - artifacts: "node_modules/someotherpackage/artifacts", - } - ], - deployments: { - rinkeby: ["node_modules/@cartesi/arbitration/build/contracts"], - }, - } -} +```bash +npx hardhat --network tron deploy --tags lumi ``` -The `contracts` field specify an array of object which itself have 2 fields. - -- `artifacts`: (mandatory) it is a path to an artifact folder. This support both hardhat and truffle artifacts. -- `deploy`: (optional) it specifies a path to a folder where reside deploy script. The deploy scripts have only access to the artifact specified in the artifacts field. This allow project to share their deployment procedure. A boon for developer aiming at integrating it as they can get the contracts to be deployed for testing locally. - -The `deployments` fields specify an object whose field names are the hardhat network and the value is an array of path to look for deployments. It supports both **Sunhat** and truffle formats. - --- -### Access to Artifacts (non-deployed contract code and abi) - -Artifacts in hardhat terminology represent a compiled contract (not yet deployed) with at least its bytecode and abi. +### Step 6: Verify Deployment -`Sunhat` gives can access to these artifacts via the `deployments.getArtifact` function : - -```js -const {deployments} = require('hardhat'); -const artifact = await deployments.getArtifact(artifactName); +Check the `deployments/` directory for your deployment files. You should see: ``` - -With the `hardhat-ethers` plugin you can get an artifact as an ethers contract factory, ready to be deployed, via the following: - -```js -const {deployments, ethers} = require('hardhat'); -const factory = await ethers.getContractFactory(artifactName); +├── deployments +│   └── tron +│   └── Lock.json ``` -> Note that the artifact's files need to be either in `artifacts` folder that hardhat generate on compilation or in the `imports` folder where you can store contracts compiled elsewhere. They can also be present in the folder specified in `external.artifacts` see [Importing deployment from other projects](#importing-deployment-from-other-projects-with-truffle-support) - ---- - --- -## How to Deploy Contracts - -### The `deploy` Task - -`hardhat --network deploy [options and flags]` - -This is a new task that the `Sunhat` adds. As the name suggests it deploys contracts. -To be exact it will look for files in the folder `deploy` or whatever was configured in `paths.deploy`, see [paths config](#extra-paths-config) - -It will scan for files in alphabetical order and execute them in turn. - -- it will `require` each of these files and execute the exported function with the HRE as argument - -To specify the network, you can use the builtin hardhat argument `--network ` or set the env variable `HARDHAT_NETWORK` - -> :warning: Note that running `hardhat deploy` without specifying a network will use the default network. If the default network is hardhat (the default's default) then nothing will happen as a result as everything happens in memory, but this can be used to ensure the deployment is without issues. +## Development +To dive deeper into advanced topics of the sunhat project lifecycle, please see the [Documentation](https://docs-hat.sun.io/) for guides and reference. --- -### Deploy Scripts - -The deploy scripts need to be of the following type : - -```ts -export interface DeployFunction { - (env: HardhatRuntimeEnvironment): Promise; - skip?: (env: HardhatRuntimeEnvironment) => Promise; - tags?: string[]; - dependencies?: string[]; - runAtTheEnd?: boolean; - id?: string; -} -``` - -The skip function can be used to skip executing the script under whatever condition. It simply need to resolve a promise to true. - -The tags is a list of string that when the _deploy_ task is executed with, the script will be executed (unless it skips). In other word if the deploy task is executed with a tag that does not belong to that script, that script will not be executed unless it is a dependency of a script that does get executed. - -The dependencies is a list of tag that will be executed if that script is executed. So if the script is executed, every script whose tag match any of the dependencies will be executed first. - -The `runAtTheEnd` is a boolean that if set to true, will queue that script to be executed after all other scripts are executed. +## How to Contribute -These set of fields allow more flexibility to organize the scripts. You are not limited to alphabetical order and you can even organise deploy script in sub folders. - -Finally the function can return true if it wishes to never be executed again. This can be useful to emulate migration scripts that are meant to be executed only once. Once such script return true (async), the `id` field is used to track execution and if that field is not present when the script return true, it will fails. - -In other words, if you want a particular deploy script to run only once, it needs to both return true (async) and have an `id` set. - -In any case, as a general advice every deploy function should be idempotent. This is so they can always recover from failure or pending transaction. This is what underpin most of Sunhat philosophy. - -This is why the `hre.deployments.deploy` function will by default only deploy if the contract code has changed, making it easier to write idempotent script. - -#### An example of a deploy script : - -```js -module.exports = async ({ - getNamedAccounts, - deployments, - getChainId, - getUnnamedAccounts, -}) => { - const {deploy} = deployments; - const {deployer} = await getNamedAccounts(); - - // the following will only deploy "GenericMetaTxProcessor" if the contract was never deployed or if the code changed since last deployment - await deploy('GenericMetaTxProcessor', { - from: deployer, - gasLimit: 4000000, - args: [], - }); -}; -``` - -As you can see the HRE passed in has 4 new fields : - -- `getNamedAccounts` is a function that returns a promise to an object whose keys are names and values are addresses. It is parsed from the `namedAccounts` configuration (see [`namedAccounts`](#namedaccounts)). - -- `getUnnamedAccounts` is a function that return a promise to an array of accounts which were not named (see [`namedAccounts`](#namedaccounts)). It is useful for tests where you want to be sure that the account has no speicifc role in the system (no token given, no admin access, etc...). - -- `getChainId` is a function which return a promise for the chainId, as convenience - -- `deployments` is an object which contains functions to access past deployments or to save new ones, as well as helpers functions. - -That latter field contains for example the `deploy` function that allows you to deploy contract and save them. It contains a lot more functions though : - ---- - -### The `deployments` field - -The deployments field contains several helpers function to deploy contract but also execute transaction. - -```ts -export interface DeploymentsExtension { - deploy(name: string, options: DeployOptions): Promise; // deploy a contract - diamond: { - // deploy diamond based contract (see section below) - deploy(name: string, options: DiamondOptions): Promise; - }; - deterministic( // return the deterministic address as well as a function to deploy the contract, can pass the `salt` field in the option to use different salt - name: string, - options: Create2DeployOptions - ): Promise<{ - address: Address; - implementationAddress?: Address; - deploy(): Promise; - }>; - fetchIfDifferent( // return true if new compiled code is different than deployed contract - name: string, - options: DeployOptions - ): Promise<{differences: boolean; address?: string}>; - save(name: string, deployment: DeploymentSubmission): Promise; // low level save of deployment - get(name: string): Promise; // fetch a deployment by name, throw if not existing - getOrNull(name: string): Promise; // fetch deployment by name, return null if not existing - getDeploymentsFromAddress(address: string): Promise; - all(): Promise<{[name: string]: Deployment}>; // return all deployments - getArtifact(name: string): Promise; // return a hardhat artifact (compiled contract without deployment) - getExtendedArtifact(name: string): Promise; // return a extended artifact (with more info) (compiled contract without deployment) - run( // execute deployment scripts - tags?: string | string[], - options?: { - resetMemory?: boolean; - deletePreviousDeployments?: boolean; - writeDeploymentsToFiles?: boolean; - export?: string; - exportAll?: string; - } - ): Promise<{[name: string]: Deployment}>; - fixture( // execute deployment as fixture for test // use evm_snapshot to revert back - tags?: string | string[], - options?: {fallbackToGlobal?: boolean; keepExistingDeployments?: boolean} - ): Promise<{[name: string]: Deployment}>; - createFixture( // execute a function as fixture using evm_snaphost to revert back each time - func: FixtureFunc, - id?: string - ): (options?: O) => Promise; - log(...args: any[]): void; // log data only ig log enabled (disabled in test fixture) - - execute( // execute function call on contract - name: string, - options: TxOptions, - methodName: string, - ...args: any[] - ): Promise; - rawTx(tx: SimpleTx): Promise; // execute a simple transaction - catchUnknownSigner( // you can wrap other function with this function and it will catch failure due to missing signer with the details of the tx to be executed - action: Promise | (() => Promise), - options?: {log?: boolean} - ): Promise; - read( // make a read-only call to a contract - name: string, - options: CallOptions, - methodName: string, - ...args: any[] - ): Promise; - read(name: string, methodName: string, ...args: any[]): Promise; - // rawCall(to: Address, data: string): Promise; // TODO ? -} -``` - ---- - -#### `deployments.deploy(, options)` - ---- - -The `deploy` function, as mentioned above, allows you to deploy a contract and save it under a specific name. - -The deploy function expect 2 parameters: one for the name and one for the options - -See below the full list of fields that the option parameter allows and requires: - -```ts -export interface DeployOptions = { - from: string; // address (or private key) that will perform the transaction. you can use `getNamedAccounts` to retrieve the address you want by name. - contract?: // this is an optional field. If not specified it defaults to the contract with the same name as the first parameter - | string // this field can be either a string for the name of the contract - | { // or abi and bytecode - abi: ABI; - bytecode: string; - deployedBytecode?: string; - }; - args?: any[]; // the list of argument for the constructor (or the upgrade function in case of proxy) - skipIfAlreadyDeployed?: boolean; // if set it to true, will not attempt to deploy even if the contract deployed under the same name is different - log?: boolean; // if true, it will log the result of the deployment (tx hash, address and gas used) - linkedData?: any; // This allow to associate any JSON data to the deployment. Useful for merkle tree data for example - libraries?: { [libraryName: string]: Address }; // This let you associate libraries to the deployed contract - proxy?: boolean | string | ProxyOptions; // This options allow to consider your contract as a proxy (see below for more details) - - // here some common tx options : - gasLimit?: string | number | BigNumber; - gasPrice?: string | BigNumber; - value?: string | BigNumber; - nonce?: string | number | BigNumber; - - estimatedGasLimit?: string | number | BigNumber; // to speed up the estimation, it is possible to provide an upper gasLimit - estimateGasExtra?: string | number | BigNumber; // this option allow you to add a gas buffer on top of the estimation - - autoMine?: boolean; // this force a evm_mine to be executed. this is useful to speed deployment on test network that allow to specify a block delay (ganache for example). This option basically skip the delay by force mining. - deterministicDeployment? boolean | string; // if true, it will deploy the contract at a deterministic address based on bytecode and constructor arguments. The address will be the same across all network. It use create2 opcode for that, if it is a string, the string will be used as the salt. - waitConfirmations?: number; // number of the confirmations to wait after the transactions is included in the chain -}; -``` - ---- - ---- - -## Handling contract using libraries - -In the deploy function, one of the `DeployOptions` field is the `libraries` field. It allows you to associate external contract as libraries at the time of deployment. - -First, you have deploy the library using the `deploy` function, then when we deploy a contract that needs the linked library, we can pass the deployed library name and address in as an argument to the `libraries` object. - -First step: deploy the library: - -```js -const exampleLibrary = await deploy("ExampleLibary", { - from: -}); - -``` - -ExampleLibrary is now deployed to whatever network was chosen (`hardhat deploy --network `) - -For example, if we are deploying on Rinkeby, this library will get deployed on rinkeby, and the `exampleLibrary` variable will be a deployment object that contains the abi as well as the deployed address for the contract. - -Now that the library is deployed, we can link it in our next deployed contract. - -```js -const example = await deploy("Example", { - from: - args: ["example string argument for the 'Example' contract constructor"], - libraries: { - ExampleLibrary: exampleLibrary.address - } -}); - -``` - -This `libraries` object takes the name of the library, and its deployed address on the network. Multiple libraries can be passed into the `libraries` object. - ---- - ---- - -## Exporting Deployments - -Apart from deployments saved in the `deployments` folder which contains all information available about the contract (compile time data + deployment data), `Sunhat` allows you to export lightweight files. - -These can be used for example to power your frontend with contract's address and abi. - -This come into 2 flavors. - -The first one is exported via the `--export ` option and follow the following format : - -```ts -export interface Export { - chainId: string; - name: string; - contracts: {[name: string]: ContractExport}; -} -``` - -where name is the name of the network configuration chosen (see hardhat option `--network`) - -The second one is exported via the `--export-all ` option and follow the following format : - -```ts -export type MultiExport = { - [chainId: string]: Export[]; -}; -``` - -As you see the second format include the previous. While in most case you'll need the single export where your application will support only one network, there are case where your app would want to support multiple networks at once. This second format allow for that. - -Furthermore as hardhat support multiple network configuration for the same network (rinkeby, mainnet...), the export-all format will contains each of them grouped by their chainId. - -Note: from v0.10.4 the old multi-export down is no more: - -``` -export type MultiExport = { - [chainId: string]: {[name: string]: Export}; -}; -``` - -For both --export and --export-all, if the extension ends in .ts it will generate a typescript file containing the contracts info. - ---- - ---- - -## Deploying and Upgrading Proxies - -As mentioned above, the deploy function can also deploy a contract through a proxy. It can be done without modification of the contract _as long as its number of constructor arguments matches the proxy initialization/update function_. If the arguments do not match, see [this section below](#when-the-constructor-and-init-functions-are-different). - -The default Proxy is both ERC-1967 and ERC-173 Compliant, but other proxy can be specified, like openzeppelin transparent proxies. - -Code for the default Proxy can be found [here](solc_0.8/proxy/EIP173Proxy.sol). - -To perform such proxy deployment, you just need to invoke the deploy function with the following options : `{..., proxy: true}` - -See example : - -```js -module.exports = async ({getNamedAccounts, deployments, getChainId}) => { - const {deploy} = deployments; - const {deployer} = await getNamedAccounts(); - await deploy('Greeter', { - from: deployer, - proxy: true, - }); -}; -``` - -You can also set it to `proxy: ""` in which case the function `` will be executed upon upgrade. -the `args` field will be then used for that function instead of the contructor. It is also possible to then have a constructor with the same arguments and have the proxy be disabled. It can be useful if you want to have your contract as upgradeable in a test network but be non-upgradeable on the mainnet. - -See example : - -```js -module.exports = async ({ - getNamedAccounts, - deployments, - getChainId, - network, -}) => { - const {deploy} = deployments; - const {deployer} = await getNamedAccounts(); - await deploy('Greeter', { - from: deployer, - proxy: network.live ? false : 'postUpgrade', - args: ['arg1', 2, 3], - }); -}; -``` - -The proxy option can also be an object which can set the specific owner that the proxy is going to be managed by. - -See example: - -```js -module.exports = async ({getNamedAccounts, deployments, getChainId}) => { - const {deploy} = deployments; - const {deployer, greeterOwner} = await getNamedAccounts(); - await deploy('Greeter', { - from: deployer, - proxy: { - owner: greeterOwner, - methodName: 'postUpgrade', - }, - args: ['arg1', 2, 3], - }); -}; -``` - -Note that for the second invocation, this deployment will not be executed from the specified `from: deployer` as otherwise these tx will always fail. It will instead be automatically executed from the proxy's current owner (in that case : `greeterOwner`) - -Now, it is likely you do not want to locally handle the private key / mnemonic of the account that manage the proxy or it could even be that the `greeterOwner` in question is a multi sig. As such that second invocation will throw an error as it cannot find a local signer for it. - -The error will output the necessary information to upgrade the contract but `Sunhat` comes also with a utility function for such case: `deployments.catchUnknownSigner` which will catch the error and output to the console the necessary information while continuing to next step. - -Here is the full example : - -```js -module.exports = async ({getNamedAccounts, deployments, getChainId}) => { - const {deploy, catchUnknownSigner} = deployments; - const {deployer, greeterOwner} = await getNamedAccounts(); - await catchUnknownSigner( - deploy('Greeter', { - from: deployer, - proxy: { - owner: greeterOwner, - methodName: 'postUpgrade', - }, - args: ['arg1', 2, 3], - }) - ); - // you could pause the deployment here and wait for input to continue -}; -``` - -#### When the constructor and init functions are different - -When the constructor and proxy have different signatures, you will not be able to use the top level `args` property. Instead you can use the `execute` property of `proxy` to specify the `init` method and arguments. This will not try to pass any arguments to the constructor. - -```typescript -const deployed = await deploy('YourContract', { - from: deployer, - proxy: { - execute: { - init: { - methodName: 'initialize', - args: ['arg1', 'arg2'], - }, - }, - proxyContract: 'OpenZeppelinTransparentProxy', - }, - log: true, - autoMine: true, -}); -``` - -#### Proxy deployment options - -The full proxy options is as follow: - -```ts -type ProxyOptionsBase = { - owner?: Address; // this set the owner of the proxy. further upgrade will need to be executed from that owner - upgradeIndex?: number; // allow you to breakdown your upgrades into separate deploy script, each with their own index. A deploy call with a specific upgradeIndex will be executed only once, only if the current upgradeIndex is one less. - proxyContract?: // default to "EIP173Proxy". See below for more details - string | ArtifactData; - viaAdminContract?: // allow to specify a contract that act as a middle man to perform upgrades. Useful and Recommended for Transparent Proxies - | string - | { - name: string; - artifact?: string | ArtifactData; - }; -}; - -export type ProxyOptions = - | (ProxyOptionsBase & { - methodName?: string; // method to be executed when the proxy is deployed for the first time or when the implementation is modified. Use the deployOptions args field for arguments - }) - | (ProxyOptionsBase & { - execute?: - | { - methodName: string; // method to be executed when the proxy is deployed for the first time or when the implementation is modified. - args: any[]; - } - | { - init: { - methodName: string; // method to be executed when the proxy is deployed - args: any[]; - }; - onUpgrade?: { - methodName: string; // method to be executed when the proxy is upgraded (not first deployment) - args: any[]; - }; - }; - }); -``` - -The `proxyContract` field allow you to specify your own Proxy contract. If it is a string, it will first attempt to get an artifact with that name. If not found it will fallback on the following if - -it matches: - -- `EIP173Proxy`: use the default Proxy that is EIP-173 compliant - -- `EIP173ProxyWithReceive`: Same as above except that the proxy contains a receive hook to accept empty ETH payment. - -- `OpenZeppelinTransparentProxy`: Use Openzeppelin Transparent Proxy (copied from openzeppelin repo, see code [here](solc_0.7/openzeppelin/proxy/TransparentUpgradeableProxy.sol)) - When this option is chosen, the `DefaultProxyAdmin` is also used as admin since Transparent Proxy kind of need an intermediary contract for administration. This can be configured via the `viaAdminContract` option. Note that the DefaultProxyAdmin is slightly different than the one used by openzeppelin as it allow you to set a different owner than msg.sender on first deploy, something openzeppelin version do not allow, see : https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2639 - -- `OptimizedTransparentProxy`: This contract is similar to above, except that it is optimized to not require storage read for the admin on every call. - -## Builtin-In Support For Diamonds (EIP2535) - -The deployments field also expose the diamond field: `hre.deployments.diamond` that let you deploy [Diamonds](https://eips.ethereum.org/EIPS/eip-2535) in an easy way. - -### deployment / upgrade - -Instead of specifying the facets to cut out or cut in, which the diamond contract expects, you specify the facets you want to end up having on the deployed contract. - -This declarative approach allow you to focus on what you want instead of how to do it. - -`diamond.deploy` expect the facet as names. The names represent contract to be deployed as facet. In future version you ll be able to specify deployed contract or artifact object as facet. - -To deploy a contract with 3 facet you can do as follow : - -```js -module.exports = async ({getNamedAccounts, deployments, getChainId}) => { - const {diamond} = deployments; - const {deployer, diamondAdmin} = await getNamedAccounts(); - await diamond.deploy('ADiamondContract', { - from: deployer, - owner: diamondAdmin, - facets: ['Facet1', 'Facet2', 'Facet3'], - }); -}; -``` - -if you then later execute the following script: - -```js -module.exports = async ({getNamedAccounts, deployments, getChainId}) => { - const {diamond} = deployments; - const {deployer, diamondAdmin} = await getNamedAccounts(); - await diamond.deploy('ADiamondContract', { - from: diamondAdmin, // this need to be the diamondAdmin for upgrade - owner: diamondAdmin, - facets: ['NewFacet', 'Facet2', 'Facet3'], - }); -}; -``` - -Then the NewFacet will be deployed automatically if needed and then the diamondCut will cut Facet1 out and add NewFacet. - -Note that if the code for Facet2 and Facet3 changes, they will also be redeployed automatically and the diamondCuts will replace the existing facets with these new ones. - -Note that the diamond has 3 facet added by default. These facets are used for ownership, diamondCut and diamond loupe. - -The implementation is a slightly modified version of the [reference implementation by Nick Mudge](https://github.com/mudgen/diamond-3). -The only difference is the custom constructor that allow multiple initialization, used to allow the default ERC165 facet to be initialised along your custom initialization function. - -### onUpgrade calls - -Like normal proxies you can also execute a function at the time of an upgrade. - -This is done by specifying the execute field in the diamond deploy options : - -```js -diamond.deploy('ADiamondContract', { - from: deployer, - owner: diamondAdmin, - facets: ['NewFacet', 'Facet2', 'Facet3'], - execute: { - methodName: 'postUpgrade', - args: ['one', 2, '0x3'], - }, -}); -``` - -### use diamond contract for your scripts - -set external typechain in `hardhat.config.ts`, this make sure typechain knows this abi and generate typechain files for the diamond contract - -```ts -const config: HardhatUserConfig = { - typechain: { - externalArtifacts: ['deployments/localhost/ADiamondContract.json'] - }, - ... -``` - -in your scripts - -```ts -const ADiamondContract = await ethers.getContract( - 'ADiamondContract' -); -// do your stuff -``` - -### more... - -There are more options, to be described later... - -## Testing Deployed Contracts - -You can continue using the usual test task: - -`hardhat test` - -Tests can use the `hre.deployments.fixture` function to run the deployment and snapshot it so that tests don't need to perform all the deployment transactions every time. They can simply reuse the snapshot for every test (this leverages `evm_snapshot` and `evm_revert` provided by both `hardhat` and `ganache`). You can for example set them in a `beforeEach`. - -Here is an example of a test : - -```js -const {deployments} = require('hardhat'); - -describe('Token', () => { - it('testing 1 2 3', async function () { - await deployments.fixture(['Token']); - const Token = await deployments.get('Token'); // Token is available because the fixture was executed - console.log(Token.address); - const ERC721BidSale = await deployments.get('ERC721BidSale'); - console.log({ERC721BidSale}); - }); -}); -``` - -Tests can also leverage named accounts for clearer test. Combined with `hardhat-ethers` plugin, you can write succint test : - -```js -const {ethers, getNamedAccounts} = require('hardhat'); - -describe('Token', () => { - it('testing 1 2 3', async function () { - await deployments.fixture(['Token']); - const {tokenOwner} = await getNamedAccounts(); - const TokenContract = await ethers.getContract('Token', tokenOwner); - await TokenContract.mint(2).then((tx) => tx.wait()); - }); -}); -``` - ---- - -### Creating Fixtures - -Furthermore, tests can easily create efficient fixture using `deployments.createFixture` - -See example : - -```js -const setupTest = deployments.createFixture( - async ({deployments, getNamedAccounts, ethers}, options) => { - await deployments.fixture(); // ensure you start from a fresh deployments - const {tokenOwner} = await getNamedAccounts(); - const TokenContract = await ethers.getContract('Token', tokenOwner); - await TokenContract.mint(10).then((tx) => tx.wait()); //this mint is executed once and then `createFixture` will ensure it is snapshotted - return { - tokenOwner: { - address: tokenOwner, - TokenContract, - }, - }; - } -); -describe('Token', () => { - it('testing 1 2 3', async function () { - const {tokenOwner} = await setupTest(); - await tokenOwner.TokenContract.mint(2); - }); -}); -``` - -While this example is trivial, some fixture can require several transaction and the ability to snapshot them automatically speed up the tests greatly. - ---- - ---- - -## More Information On Hardhat Tasks - -### **1. node task** - -as mentioned above, the node task is slightly modified and augmented with various flags and options - -`hardhat node` - -In particular it adds an argument `--export` that allows you to specify a destination file where the info about the contracts deployed is written. -Your webapp can then access all contracts information. - ---- - -### **2. test task** - -`hardhat test` - -the test task is augmented with one flag argument `--deploy-fixture` that allows to run all deployments in a fixture snapshot before executing the tests. This can speed up the tests that use specific tags as the global fixture take precedence (unless specified). - -In other words tests can use `deployments.fixture()` where specific tag only deploys the minimal contracts for tests, while still benefiting from global deployment snapshot if used. - -If a test needs the deployments to only include the specific deployment specified by the tag, it can use the following : - -```js -deployments.fixture('', {fallbackToGlobal: false}); -``` - -Due to how snapshot/revert works in hardhat, this means that these tests will not benefit from the global fixture snapshot and will have to deploy their contracts as part of the fixture call. This is automatic but means that these tests will run slower. - ---- - -### **3. run task** - -`hardhat --network run