Conflux Network: An Easy Technique To Deploy Smart Contracts With Hardhat And Js-conflux-sdk
Table of contents
No headings in the article.
The Conflux Network is a newly scalable, secured, and inexpensive blockchain that accommodates users and developers alike. It allows for users with zero wallet balance to engage in blockchain activity, with the aid of sponsors who pay for some portion of the transaction fee. This engagement is easy also for developers who intends to build on the Conflux network. In this article, you will grasp how developer-friendly the Conflux network is from deploying two contracts; an ERC20 contract and a Vault contract – a token and a grant vault respectively, using hardhat and js-conflux-sdk.
SETTING UP THE PROJECT STRUCTURE
Initialize the project folder from your cli and install hardhat.
mkdir Vault
Navigate into the folder and run the following code.
yarn init -y
yarn add hardhat
This begins with installing hardhat and other dependencies that will help in writing and deploying the smart contract. This integrates hardhat into the project but there is one more step to complete this process.
yarn hardhat
You can choose from the list of options provided.
This completes the whole process for setting up hardhat for your development. This brings along with it some dummy codes that explain how hardhat functions. Delete each files in the contracts, scripts, and tests folder if you don't need them as a guide.
CREATING THE ERC20 TOKEN CONTRACT
Create a TestToken.sol file directly under the contracts folder. This is where the smart contract to be deployed will be written. Ensure to install the Openzeppelin contracts in order to have quick pace in creating an ERC20 token. This token is needed to help us understand the Vault contract that allows for the creation of grants for different tokens.
To install, run:
yarn add @openzeppelin/contracts
Then write the contract,
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestToken is ERC20 {
// At the point of deployment, 10000e18 MyToken is minted to the deployer.
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 10000e18);
}
}
To be sure that your code works fine, compile it.
yarn hardhat compile
As a quick walkthrough, the contract above has a MIT license identifier, a fixed solidity version, and an import of the ERC20 openzeppelin standard. The name of the token is initialized at the constructor and 10000e18 worth of token was minted to the address deploying the contract.
Quite easy, right? How then do you deploy on the Conflux network?
Here comes the holy script. Before you jump on the script, install js-conflux-sdk.
yarn add js-conflux-sdk
DEPLOYING THE ERC20 TOKEN CONTRACT
The process of deploying contracts on Conflux is swift with the aid of the js-conflux-sdk. This SDK requires you to make provisions for some properties of the contract after it has been successfully compiled.
// Import the abi and bytecode from the contract artifact
import {abi, bytecode} from "../artifacts/contracts/TestToken.sol/TestToken.json";
import { Conflux } from 'js-conflux-sdk'
// Initialize constant variable
const TESTNET = "https://test.confluxrpc.com"
async function deployTestToken () {
// Create an instance of Conflux testnet
const conflux = new Conflux({
url: TESTNET,
networkId: 1,
logger: console,
})
// Establish wallet to make deployment.
const wallet = conflux.wallet.addPrivateKey(process.env.CONFLUX_PRIVATE_KEY)
// Create instance for the contract to be deployed
const contractInstance = conflux.Contract({abi, bytecode})
// Deploy contract and generate contract address
const deploytx = await contractInstance.constructor().sendTransaction({from: wallet}).executed()
console.log("Contractaddress is", deploytx.contractCreated)
console.log("Deployed token tx is", deploytx.transactionHash)
}
deployTestToken().catch((error) => {
console.error(error);
process.exitCode = 1
})
To deploy therefore, ensure that your hardhat configuration is set up appropriately with the right Conflux testnet rpc for the network, the Solidity version, and your private key (This should be in your dotenv file and added in your gitignore to avoid pushing to the public domain). Then run the script with the following.
yarn hardhat run scripts/deployTestToken.ts --network conflux
Note:
process.env.CONFLUX_PRIVATE_KEY - This should be provided in your .env file to make the code run successfully.
scripts/deployTestToken.ts - This indicates the script to be deployed.
conflux - This is the name configured in the hardhatconfig file.
This is one phase of the article that explains the deployment of ERC20 token on the Conflux network. In the next phase, the Vault contract, uses the Witnet decentralized oracle to aid in the generation of random number. This helps to create a unique id for every created grant.
CREATING THE VAULT CONTRACT
Using the same project structure, create a Vault.sol file in the contracts file.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "./interfaces/IERC20.sol";
import "./interfaces/IWitnetRandomness.sol";
contract Vault {
//---------------STATE VARIABLES-----------------//
address owner;
///Randomness Sate Variable
uint32 public randomness;
uint256 public latestRandomizingBlock;
IWitnetRandomness public witnet;
//---------------STRUCT-----------------//
struct GrantProps {
address token;
address receipient;
uint64 timeline;
uint256 amount;
}
//---------------MAPPING-----------------//
mapping(uint32 => GrantProps) public grantProps;
//---------------EVENTS -----------------//
event createGrant(
address token_,
address receipient,
uint256 amount,
uint256 id
);
event grantClaimed(address receipient, uint256 amount, uint256 id);
event grantRemoved(address token, uint256 amount, uint256 id);
//---------------MODIFIER-----------------//
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
//--------------- CONSTRUCTOR -----------------//
constructor(address _witnetRandomness) payable {
owner = msg.sender;
assert(address(_witnetRandomness) != address(0));
witnet = IWitnetRandomness(_witnetRandomness);
requestRandomNumber();
}
/// @notice create Grants for people to come and claim when the deadline is met
/// @param _token the ERC20 tokens to pay
/// @param _receipient The beneficiary of the ERC20 tokens
/// @param _amount the number of tokens to claim
/// @param timestamp the block.timestamp in which the grant is due for claim
/// @dev will fail if transferFrom is not successful
function createGrantFund(
address _token,
address _receipient,
uint256 _amount,
uint64 timestamp
) external payable onlyOwner {
require(
IERC20(_token).transferFrom(msg.sender, address(this), _amount),
"transfer failed"
);
fetchRandomNumber();
GrantProps storage GF = grantProps[randomness];
GF.token = _token;
GF.receipient = _receipient;
GF.amount = _amount;
GF.timeline = timestamp;
requestRandomNumber();
emit createGrant(_token, _receipient, _amount, randomness);
}
/// @notice remove Grants give access to owner to remove grants before the timestamp is met
/// @param id the id of the grantProperties to remove
function removeGrant(uint32 id) external onlyOwner {
bool notlapse = hasTimelineExpired(id);
require(!(notlapse), "timeelapse for receipient to withdraw");
GrantProps storage GF = grantProps[id];
address token_ = GF.token;
uint256 amount_ = GF.amount;
GF.amount = 0;
bool success = IERC20(token_).transfer(msg.sender, amount_);
require(success, "transfer failes");
emit grantRemoved(token_, amount_, id);
}
/// @notice claim Grants for receipient to claim when the deadline is met
/// @param id the unique number of the grant to claim
/// @dev will fail if transferFrom is not successful
function claimGrant(uint32 id) external {
require(hasTimelineExpired(id), "Not yet time to withdraw");
address receipient_ = grantProps[id].receipient;
address token_ = grantProps[id].token;
uint256 amount_ = grantProps[id].amount;
grantProps[id].amount = 0;
require(amount_ > 0, "NO fund for this grant");
require(
msg.sender == receipient_,
"you are not the beneficiary to this grant"
);
bool success = IERC20(token_).transfer(msg.sender, amount_);
require(success, "transfer fails");
emit grantClaimed(msg.sender, amount_, id);
}
//--------------- VIEW FUNCTIONS --------------//
/// @notice This function check if time for particular grants has passed
/// @param id the unique id of the grant to check
function hasTimelineExpired(uint32 id) public view returns (bool) {
GrantProps memory GF = grantProps[id];
return (GF.timeline <= block.timestamp);
}
/// @notice This function returns the owner of this contract;
/// @return returns the address of the owner.
function getOwner() external view returns (address) {
return owner;
}
/// @notice This function returns the properties of a grant;
/// @param id the unique id of the grant to check
/// @return returns the information of a particular grants.
function getFundProps(uint32 id)
external
view
returns (GrantProps memory)
{
return grantProps[id];
}
//--------------- INTERNAL FUNCTIONS --------------//
/// @notice This function initializes the random number, supposing the caller pays some token or CFX
function requestRandomNumber() internal {
latestRandomizingBlock = block.number;
uint256 _usedFunds = witnet.randomize{value: msg.value}();
if (_usedFunds < msg.value) {
payable(msg.sender).transfer(msg.value - _usedFunds);
}
}
/// @notice This function sets the randomness state variable
function fetchRandomNumber() internal {
assert(latestRandomizingBlock > 0);
randomness =
1 +
witnet.random(type(uint32).max, 0, latestRandomizingBlock);
}
}
The Vault contracts allows for owner of the contract to create and remove grant funds. When a new grant is to be created for a qualified recipient, the Witnet oracle helps generate a unique id, or number, for every grant. Recipients can afterwards claim grant if it is still within the timeline to claim.
import {abi, bytecode} from "../artifacts/contracts/Vault.sol/Vault.json";
import { Conflux, Drip } from 'js-conflux-sdk'
// Initialize relevant constant variables
const testnet = "https://test.confluxrpc.com"
const witnet = "cfxtest:aceh1wg5t0jyctjzsydvwktsvz596nf6ue18kkzpp3"
const tokenAddress = "cfxtest:acf00puuhggcdy2t4ywzruyr5n2f74yvs6psxamfrj"
const recipientAddr = "cfxtest:aarthy7b74x09687hxpe7fzs2kt0k3see2xcevy55r"
async function deployVault () {
// Create an instance of Conflux testnet
const conflux = new Conflux({
url: testnet,
networkId: 1,
logger: console,
})
// Establish wallet to make deployment.
const wallet = conflux.wallet.addPrivateKey(process.env.CONFLUX_PRIVATE_KEY)
// Create instance for the contract to be deployed using the abi and bytecode
const contractInstance = conflux.Contract({abi, bytecode})
// Deploy contract and generate contract address
const deploytx = await contractInstance.constructor(witnet).sendTransaction({from: wallet, value: Drip.fromCFX(4),}).executed()
console.log("Contractaddress is", deploytx.contractCreated)
console.log("Deployed vault tx is", deploytx.transactionHash)
// Create instance with abi and contract address
const createGrant = conflux.Contract({ abi, address: deploytx.contractCreated })
//call the createGrant function is called. Ensure that owner of the token approves the Vault contract to spend its token before the createGrantFund function can be called.
const tx = await createGrant
//@ts-ignore
.createGrantFund(tokenAddress, recipientAddr, "2000", "1659954869")
.sendTransaction({ from: wallet.toString() })
console.log('Create grants', wallet.toString(), 'in txn ', tx)
}
deployVault().catch((error) => {
console.error(error);
process.exitCode = 1
})
In the deployment script for the Vault contract, the abi and bytecode was imported from the artifacts of the Vault contract and also the js-conflux-sdk. Then a series of constant variables were initialized, the testnet rpc url, witnet random address, and token whose created grant is to be created.
yarn hardhat run scripts/deployTestToken.ts --network conflux
CONCLUSION
In this article, we have extensively explored the creation of ERC20 token, integrate the Witnet decentralized oracle in a contract and deployed them all on the Conflux Network. If there are any blocker while following the tutorial, see the source code.
REFERENCES
https://docs.witnet.io/smart-contracts/witnet-randomness-oracle