Smart Contract Library
Table of Content
- Dependencies
- Installation
- Additional Packages
- Folder & File Structure
- Draft Contract
- Version Control
- Post Contract & Test Development Steps
- Contract List
- Contracts
- Utility Contracts
- Contract Interfaces
IERC721Burnable
IERC721Minable
IERC721MinterBurnable
IRandomNumberGenerator
Dependencies
OpenZeppelin Version: ^4.3.3
Solidity Version: ^0.8.0
Installation
Clone the repository as submodule and place it in the ~/contracts/library directory
⚠️ Always remove the artifacts and cache directory when use it as a submodule⚠️
Additional Packages
crytic/slither
Prerequisites
- python3 & pip3
brew install python3
- solc-select
pip3 install solc-select
To install slither
pip3 install slither-analyzer
Implementation
Install the version of solidity compiler
solc-select install {version}
e.g.
solc-select install 0.8.4
Implement the wanted version
solc-select use {version}
e.g.
solc-select use 0.8.4
Run static analysis with slither
yarn run slither
Folder & File Structure
.
├── contracts # Contracts folder
│ ├── interfaces # Interface contracts
│ ├── mocks # Mock contracts for testing
│ ├── utils # Utility contracts
│ └── ...
├── test # Test scripts folder
│ ├── helper.js # Test helper functions for testing
│ └── ... # Test files with *.test.js
└── ...
Draft Contract
For any unfinished contract, please name it with the prefix draft-
.
e.g.
draft-Contract.sol
The prefix draft-
should be deleted only after test cases were completed with 100% coverage and confirmed it is bug-free.
Version Control
Please be aware that new version of a contract should be created if any change is wanted to be made to a deployed contract with Vn
while n is the version number
e.g.
Payable.sol to PayableV2.sol as Payable.sol has been deployed in other project
New version of PayableV2.sol will be PayableV3.sol, PayableV4.sol, PayableV5.sol, ...
Post Contract & Test Development Steps
solidity-coverage
Run coverage test with npx hardhat coverage
# with harthat-shorthand installed
hh coverage
slither
Run static analysis with yarn run slither
Contract List
Contracts
Utility Contracts
Contract Interfaces
IERC721Burnable
IERC721Minable
IERC721MinterBurnable
IRandomNumberGenerator
Contracts Description
TokenVesting.sol
Type:
Contract
Description:
Vesting contract that supports single ERC20 that linearly distributes tokens over time. Each address can contain its own vesting schedule including the start time, duration, cliff duration and amount of tokens.
Deployment:
- Deploy the contract with the vesting token address
- Transfer sufficient amount of vesting token to the contract
Implementation:
- Use
addVesting
to add a vesting wallet to the contract
VRFRandomNumberGenerator.sol
Type:
Contract
Description:
Random number generator contract that implements the Chainlink Oracle Service's Verifiable Random Function
Deployment:
- Create a subscription to Chainlink VRF in the target chain
- Fund the subscription with $LINK
- Get the
vrfCoordinator
address andkeyHash
of the target chain in Chainlink docs - Deploy the contract with the following parameters:
subcriptionId
: the subscription ID of the target chain created at Chainlink VRFvrfCoordinator
: the address of the VRF coordinatorkeyHash
: the key hash of the target chain
- Copy the deployed
VRFRandomNumberGenerator
address and add it as a consumer in the created subscription in Chainlink VRF - Run
requestRandomWords
function to fill up the first queue - Always make sure the subscription is funded with $LINK
Payable.sol
Type:
Utility Contract
Description:
Utility contract that provides functions for payment control for both native tokens and ERC20 tokens with 1 fee receiver
Implementation:
Extends with
import "./library/contracts/utils/Payable.sol";
contract Contract is Payable {
constructor (
address payable _feeReceiver
)
Payable(_feeReceiver)
{
// ...
}
}
Switch between native token and ERC20 token
// Run following functions with owner account
const erc20 = await ethers.getContract('Erc20')
await contract.setIsErc20(erc20.address)
await contract.setIsNonErc20()
Payment
// Run following function in contract
function fn(uint256 _amount) public payable {
pay(_amount);
// ...
}
StakingPeriodControl.sol
Type:
Utility Contract
Description:
Utility contract that provides functions for staking period control, including staking period start, end and reward end time in both block.timestamp
and block.number
measures
Implementation:
Extends with
import "./library/contracts/utils/StakingPeriodControl.sol";
contract Contract is StakingPeriodControl {
# instead of setting up with constructor, additional initailize() function should be used
function initialize(
uint256 _isTimestamp,
uint256 _start,
uint256 _end,
uint256 _bonusEnd
) {
__StakingPeriodControl__init(_isTimestamp, _start, _end, _bonusEnd);
}
}
Provided modifiers
beforeStakeStart
afterStakeStart
beforeStakeEnd
afterStakeEnd
beforeBonusEnd
afterBonusEnd
Whitelist.sol
Type:
Utility Contract
Description:
Utility contract that provides functions for whitelist control with merkle tree implementation
Implementation:
Extends with
import "./library/contracts/utils/Whitelist.sol";
contract Contract is Whitelist {
// ...
}
Provided modifier
onlyWhitelisted(bytes32[] memory proof)
// Contract level implementation
function fn(bytes32[] memory _proof) public onlyWhitelisted(_proof) {
// ...
}
To add whitelist
const keccal256 = require('keccak256')
const { MerkleTree } = require('merkletreejs')
const addresses = [...]
const tree = new MerkleTree(addresses, keccak256, { hashLeaves: true, sortPairs: true })
await contract.addTree(tree.getRoot())
// save hashed addresses for later use
const hashedAddresses = addresses.map(address => keccal256(address))
// ...
To run function with whitelist
const keccal256 = require('keccak256')
const { MerkleTree } = require('merkletreejs')
const targetAddress = '0x...'
// this should be generated when adding the address to the whitelist
const hashedAddresses = []
const tree = new MerkleTree(hashedAddresses, keccal256, { hashLeaves: false, sortPairs: true })
const leaf = keccal256(targetAddress)
const proof = tree.getProof(leaf)
// proof is always needed for whitelist checking, input [] when not needed
await contract.fn(proof)
IERC721Burnable.sol
Type:
Contract Interface
Description:
Interface ERC721 with burn function supported
Inheritance:
IERC721
IERC721Metadata
IERC721Enumerable
Functions:
burn(uint256 tokenId)