Subscribe to our free newsletter

To make sure you won't miss any valuable content we share with our community.

Lottery Project Compiling: A Complete Tutorial

In this tutorial, We are going to finally compile the lottery project. After making sure that it has been successfully compiled, we will run all the written tests to make sure everything works correctly in the contract. In the end, we will deploy the Decentralized Application on the Rinkeby test network. To be able to interact with the deployed contract, we will get some test Link tokens from Rinkeby Faucet.

Compiling the Lottery Project Dapp

In this part, we are going to finally compile, run and test our Lottery Dapp. brownie compile The result should be: Brownie v1.18.1 - Python development framework for Ethereum 1.33MiB [00:03, 402kiB/s] New compatible solc version available: 0.7.0 Compiling contracts... Solc version: 0.7.0 Optimizer: Enabled Runs: 200 EVM Version: Istanbul WARNING: Unable to compile OpenZeppelin/[email protected] due to a CompilerError - you may still be able to import sources from the package, but will be unable to load the package directly. Downloading from https://solc-bin.ethereum.org/linux-amd64/solc-linux-amd64-v0.4.26+commit.4563c3fc 100%|██████████████████████████████████████████████████████████████████████| 5.24M/5.24M [00:10<00:00, 498kiB/s] solc 0.4.26 successfully installed at: /home/mohamad/.solcx/solc-v0.4.26 Downloading from https://solc-bin.ethereum.org/linux-amd64/solc-linux-amd64-v0.6.6+commit.6c089d02 100%|██████████████████████████████████████████████████████████████████████| 8.92M/8.92M [00:23<00:00, 378kiB/s] solc 0.6.6 successfully installed at: /home/mohamad/.solcx/solc-v0.6.6 Compiling contracts... Solc version: 0.6.12 Optimizer: Enabled Runs: 200 EVM Version: Istanbul Generating build data... - OpenZeppelin/[email protected]/Ownable - OpenZeppelin/[email protected]/Context - smartcontractkit/[email protected]/LinkTokenReceiver - smartcontractkit/[email protected]/VRFConsumerBase - smartcontractkit/[email protected]/VRFRequestIDBase - smartcontractkit/[email protected]/AggregatorInterface - smartcontractkit/[email protected]/AggregatorV2V3Interface - smartcontractkit/[email protected]/AggregatorV3Interface - smartcontractkit/[email protected]/ChainlinkRequestInterface - smartcontractkit/[email protected]/LinkTokenInterface - smartcontractkit/[email protected]/SafeMathChainlink - Lottery - MockOracle - MockV3Aggregator Compiling contracts... Solc version: 0.4.26 Optimizer: Enabled Runs: 200 EVM Version: Byzantium Generating build data... - smartcontractkit/[email protected]/ERC677Token - smartcontractkit/[email protected]/ERC20 - smartcontractkit/[email protected]/ERC20Basic - smartcontractkit/[email protected]/ERC677 - smartcontractkit/[email protected]/ERC677Receiver - smartcontractkit/[email protected]/BasicToken - smartcontractkit/[email protected]/SafeMathChainlink - smartcontractkit/[email protected]/StandardToken - LinkToken Compiling contracts... Solc version: 0.6.6 Optimizer: Enabled Runs: 200 EVM Version: Istanbul Generating build data... - smartcontractkit/[email protected]/VRFConsumerBase - smartcontractkit/[email protected]/VRFRequestIDBase - smartcontractkit/[email protected]/LinkTokenInterface - smartcontractkit/[email protected]/SafeMathChainlink - VRFCoordinatorMock Generating interface ABIs... Project has been compiled. Build artifacts saved at /home/mohamad/smartcontract_lottery/build/contracts

Applying the Tests

Now, we apply our tests with following command in the terminal: brownie test -k test_get_entrance_fee Result: Brownie v1.18.1 - Python development framework for Ethereum Python-dotenv could not parse statement starting at line 1 Python-dotenv could not parse statement starting at line 1 ============================================= test session starts ============================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/smartcontract_lottery plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 6 items / 5 deselected / 1 selected Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... tests/test_lottery_unit.py . [100%] ======================================= 1 passed, 5 deselected in 2.42s ======================================== Terminating local RPC client...

Test Number 2

brownie test -k test_cant_enter_unless_started Result: Brownie v1.18.1 - Python development framework for Ethereum Python-dotenv could not parse statement starting at line 1 Python-dotenv could not parse statement starting at line 1 ============================================= test session starts ============================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/smartcontract_lottery plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 6 items / 5 deselected / 1 selected Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... tests/test_lottery_unit.py . [100%] ======================================= 1 passed, 5 deselected in 2.10s ======================================== Terminating local RPC client...

Test Number 3

brownie test -k test_can_start_and_enter_lottery Result: Brownie v1.18.1 - Python development framework for Ethereum Python-dotenv could not parse statement starting at line 1 Python-dotenv could not parse statement starting at line 1 ============================================= test session starts ============================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/smartcontract_lottery plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 6 items / 5 deselected / 1 selected Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... tests/test_lottery_unit.py . [100%] ======================================= 1 passed, 5 deselected in 2.10s ======================================== Terminating local RPC client...

Test Number 4

brownie test -k test_can_end_lottery Result: Brownie v1.18.1 - Python development framework for Ethereum Python-dotenv could not parse statement starting at line 1 Python-dotenv could not parse statement starting at line 1 ============================================= test session starts ============================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/smartcontract_lottery plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 6 items / 5 deselected / 1 selected Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... tests/test_lottery_unit.py . [100%] ======================================= 1 passed, 5 deselected in 2.34s ======================================== Terminating local RPC client...

Test Number 5

brownie test -k test_can_pick_winner_correctly Result: Brownie v1.18.1 - Python development framework for Ethereum Python-dotenv could not parse statement starting at line 1 Python-dotenv could not parse statement starting at line 1 ============================================= test session starts ============================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/smartcontract_lottery plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 6 items / 5 deselected / 1 selected Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... tests/test_lottery_unit.py . [100%] ======================================= 1 passed, 5 deselected in 2.53s ======================================== Terminating local RPC client…

Deploying Our Lottery Contract

All the tests have been successfully run. Now it is time to finally deploy our contract: brownie run scripts/deploy_lottery.py Result: Brownie v1.18.1 - Python development framework for Ethereum Python-dotenv could not parse statement starting at line 1 Python-dotenv could not parse statement starting at line 1 SmartcontractLotteryProject is the active project. Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... Running 'scripts/deploy_lottery.py::main'... Transaction sent: 0x8dece070c593b0dcea9caa4740f89c80ff2fe29744f8e6b6ed26bdf879c7ec9f Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0 MockV3Aggregator.constructor confirmed Block: 1 Gas used: 430659 (3.59%) MockV3Aggregator deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87 Transaction sent: 0xab119cd52792e53c56eee35f433c3f4b8ae409e8a5d397ac2dd16aa2d9d88e27 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 1 LinkToken.constructor confirmed Block: 2 Gas used: 669136 (5.58%) LinkToken deployed at: 0x602C71e4DAC47a042Ee7f46E0aee17F94A3bA0B6 Transaction sent: 0xb549afbc42364cb3e72877bf6d3634d655fbe0b608a09e921a3f881983dff488 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 2 VRFCoordinatorMock.constructor confirmed Block: 3 Gas used: 276395 (2.30%) VRFCoordinatorMock deployed at: 0xE7eD6747FaC5360f88a2EFC03E00d25789F69291 Deployed! Transaction sent: 0xbb65e39ea0771823b51b07af3e19122d0a9be34b51e768a536a215a9571fdcc7 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 3 Lottery.constructor confirmed Block: 4 Gas used: 897156 (7.48%) Lottery deployed at: 0x6951b5Bd815043E3F842c1b026b0Fa888Cc2DD85 Deployed lottery! Transaction sent: 0xdada264c84b388a9674518ac1fff2a6e7c2851f9adf5dc7ecd871bed9d0c8629 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 4 Lottery.startLottery confirmed Block: 5 Gas used: 28902 (0.24%) Lottery.startLottery confirmed Block: 5 Gas used: 28902 (0.24%) The lottery is started! Transaction sent: 0x9556bd5fecc58be55b36e84b6612ae264d00fe685e4862f0458846a30e305e2e Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 5 Lottery.enter confirmed Block: 6 Gas used: 70995 (0.59%) Lottery.enter confirmed Block: 6 Gas used: 70995 (0.59%) You entered the lottery! Transaction sent: 0x94f77b768b68faefacdc57046a42caadf5d954a0d0e089bb551f6594cb44037e Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 6 LinkToken.transfer confirmed Block: 7 Gas used: 51398 (0.43%) LinkToken.transfer confirmed Block: 7 Gas used: 51398 (0.43%) Fund contract! LinkToken.transfer confirmed Block: 7 Gas used: 51398 (0.43%) Transaction sent: 0x6591ec2cc1bfcddb9aea2f80e2723a773506e71bc8b649d58cf02c9708da1847 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 7 Lottery.endLottery confirmed Block: 8 Gas used: 79626 (0.66%) Lottery.endLottery confirmed Block: 8 Gas used: 79626 (0.66%) 0x0000000000000000000000000000000000000000 is the new winner! Terminating local RPC client...

Getting Some Test Link

By the way, before the deployment of this contract, make sure you have some Link Rinkeby tokens in your Metamask wallet. If you don’t have any, you can first add a Link Rinkeby token from Rinkeby Etherscan (By entering the decimals and contract address and so on). Then use Rinkeby Faucet which gives you 10 Link tokens. By using this link, you can get a Rinkeby Link token to be able to test your project:

https://docs.chain.link/docs/link-token-contracts/#rinkeby

Deploying Lottery Project on the Rinkeby Testnet

After running all the tests successfully and deploying it on Ganache-CLI, it is finally time to deploy our Lottery smart contract on Rinkeby chain, and inter-act with it using Rinkeby etherscan. To do so, in the terminal, we type: brownie run scripts/deploy_lottery.py --network rinkeby Result: Brownie v1.18.1 - Python development framework for Ethereum SmartcontractLotteryProject is the active project. Running 'scripts/deploy_lottery.py::main'... Transaction sent: 0xd0172e3f2d1caf7a56b7a1f88b5b95d152843f38f8e2f0bba68d2a5c3b534df7 Gas price: 1.000006975 gwei Gas limit: 995904 Nonce: 54 Lottery.constructor confirmed Block: 10461557 Gas used: 905368 (90.91%) Lottery deployed at: 0x2a5D4140962F09f4a5B4F5f7C563af4Eb45B79b4 Waiting for https://api-rinkeby.etherscan.io/api to process contract... Verification submitted successfully. Waiting for result... Verification pending... Verification complete. Result: Pass - Verified Deployed lottery! Transaction sent: 0x7b803fc2bedd3a1be5fa689e527e317baa4177c35ba589c1ea69ce3d6834298c Gas price: 1.000008155 gwei Gas limit: 31572 Nonce: 55 Lottery.startLottery confirmed Block: 10461565 Gas used: 28702 (90.91%) Lottery.startLottery confirmed Block: 10461565 Gas used: 28702 (90.91%) The lottery is started! Transaction sent: 0x7f2f38f4cfc0d34753c2635e8477b4c32118c3211ea3a3e6613a034a83242f99 Gas price: 1.000008155 gwei Gas limit: 97460 Nonce: 56 Lottery.enter confirmed Block: 10461566 Gas used: 88600 (90.91%) Lottery.enter confirmed Block: 10461566 Gas used: 88600 (90.91%) You entered the lottery! Transaction sent: 0xe1c566be0f8aff444154cf2d655cfc75082d83a7d13b9fb04f4315db3cece877 Gas price: 1.000008524 gwei Gas limit: 56992 Nonce: 57 LinkToken.transfer confirmed Block: 10461567 Gas used: 51811 (90.91%) LinkToken.transfer confirmed Block: 10461567 Gas used: 51811 (90.91%) Fund contract! LinkToken.transfer confirmed Block: 10461567 Gas used: 51811 (90.91%) Transaction sent: 0x585048e0b048833c35f3bd8257425e1e6f1f781cbd507df97abd756e7343c606 Gas price: 1.000008079 gwei Gas limit: 174062 Nonce: 58 Lottery.endLottery confirmed Block: 10461568 Gas used: 153439 (88.15%) Lottery.endLottery confirmed Block: 10461568 Gas used: 153439 (88.15%) 0x25E681EE76469E4cF846567b772e94e082907117 is the new winner!

Rinkeby Etherscan

Now, we can copy the and paste 0x2a5D4140962F09f4a5B4F5f7C563af4Eb45B79b4 address in Rinkeby etherscan to be able to interact with your smart contract:

https://rinkeby.etherscan.io

Once you enter Etherscan and paste your address, you will be able to see your address, ABI of the contract, dependency contracts, contract creation code, and so on.

Interacting with the Deployed Contract

To interact with the deployed smart contract, head over to Read the contract and write the contract.

You can read some of the details of the public variables after deployment in the Read contract section:

You can also run another lottery using Write Contract section:

Connecting to Metamask Wallet

But, before writing anything in there, make sure you connect to your wallet by clicking on Connect to Web3 button:

Now you can start the lottery:

Enter the lottery:

And execute other functions:

And that’s it. You have written a lottery smart contract, deployed it on a test net, Rinkeby chain, and other networks, and learned how to interact with it using Rinkeby Etherscan!

Lottery Project: Done!

In this tutorial, we have managed to deploy the contract on Rinkeby Testnet and interact with it from the address of the deployment on the Rinkeby Etherscan. To make this interaction possible from Etherscan, we connected the Metamask account of the admin of the contract to this website and applied all the stages of the lottery from the contract section.

Download this Article in PDF format

metaverse

Care to Know About Metaverse?

In Arashtad, we are providing custom services on 3d developments such as 3d websites, 3d models, metaverses and all 3d applications.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development

Lottery Project Using Brownie: A Full Scale Dapp

In this article, we are going to get started with the lottery project using Brownie. The main purpose of a lottery project in every network is to check the reliability of the randomness and use it for different purposes such as the lottery itself. In the lottery project, we are going to create a decentralized application using Brownie. In the end, we will be able to run the smart contract via Etherscan.

Using Brownie for Lottery Project

In this tutorial, we are going to first write a smart contract related to a lottery and write scripts related to testing and deploying the smart contract. We also want to make it a full-scale decentralized application, meaning that it is going to be an end-to-end Dapp with easy to use user experience. Every lottery needs a random variable. As randomness is a very complicated concept when it is going to be applied on the internet and here we are going to run it via the blockchain, it must be protected from hacks and cheating. What makes randomness really complex, is that we are dealing with deterministic variables rather than probabilistic ones. As a result, we are going to use some Chainlink tools to cover this complexity.

In this first part, we only focus on the lottery.sol related to the smart contracts, its dependency files, and the Brownie-config.yaml in addition to seeing how the Solidity scripts are written and how they work.

Getting Started with the Lottery Project

It is also important to notice that this tutorial is written to explain the GitHub repository published by Patrick Alpha C more explicitly and solve the probable issues that the programmers might face when running and facing bugs related to the Solidity version or the configuration of the project. With that said let’s get started:

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";


We have first declared the version of Solidity and the license comment before that. Also, we have imported 3 necessary Solidity libraries including AggregatorV3Interface which is used for fetching the Ethereum price from Chainlink oracle, openzeppelin Ownable.sol which is used for determining the owner of the contract and finally the VRFConsumer.sol which is used to get a random number.

contract Lottery is VRFConsumerBase, Ownable {
        address payable[] public players;
	address payable public recentWinner;
	uint256 public randomness;
	uint256 public usdEntryFee;
	AggregatorV3Interface internal ethUsdPriceFeed;
	enum LOTTERY_STATE {
		OPEN,
		CLOSED,
		CALCULATING_WINNER
	}
	LOTTERY_STATE public lottery_state;
	uint256 public fee;
	bytes32 public keyhash;
	event RequestedRandomness(bytes32 requestId);

	constructor(address _priceFeedAddress,
			address _vrfCoordinator,
			address _link,
			uint256 _fee,
			bytes32 _keyhash
			) public VRFConsumerBase(_vrfCoordinator, _link) {
		usdEntryFee = 50 * (10**18);
		ethUsdPriceFeed = AggregatorV3Interface(_priceFeedAddress);
		lottery_state = LOTTERY_STATE.CLOSED;
		fee = _fee;
		keyhash = _keyhash;
	}
}

We start the contract named Lottery and determine their declarations ownable, VRFConsumerBase. Inside the contract we define the variables that we are going to work with including:
  • The array of the addresses of the participants in the lottery defined here as players (declared as payable and public),
  • The recent winner who has won the prize in the last lottery,
  • The randomness which is the random number we receive from VRFC,
  • UsdEntryFee which is the minimum amount that a player needs to participate in the lottery, ethUsdPriceFeed which is the conversion rate of ETH to USD.
The lottery state is declared as Enum and has 3 states Open: Close, and Calculating winner.
  1. Open is when everyone can participate in the lottery,
  2. Closed is when nobody can participate
  3. Calculating is used when the random number related to the winner is being calculated.
We also have a constructor for some variables which are going to be explained later.

function enter() public payable {
	// $50 minimum
	require(lottery_state == LOTTERY_STATE.OPEN);
	require(msg.value >= getEntranceFee(), "Not enough ETH!");
	players.push(msg.sender);
}


The above function is related to the entrance to the lottery. At first, the state of the lottery is open and we check if the players pay the minimum entrance fee to participate in it.

function getEntranceFee() public view returns (uint256) {
	(, int256 price, , , ) = ethUsdPriceFeed.latestRoundData();
	uint256 adjustedPrice = uint256(price) * 10**10;
	// 18 decimals
	// $50, $2,000 / ETH
	// 50/2,000
	// 50 * 100000 / 2000
	uint256 costToEnter = (usdEntryFee * 10**18) / adjustedPrice;
	return costToEnter;
}


The above function is responsible for checking the price of the Ethereum in USD and determining the price of participation in ETH.

function startLottery() public onlyOwner {
	require(lottery_state == LOTTERY_STATE.CLOSED,"Can't start a new lottery yet!");
	lottery_state = LOTTERY_STATE.OPEN;
}


The above function starts the lottery and before that checks if the lottery has not yet got closed.

function endLottery() public onlyOwner {
	lottery_state = LOTTERY_STATE.CALCULATING_WINNER;
	bytes32 requestId = requestRandomness(keyhash, fee);	
	emit RequestedRandomness(requestId);
}


The above function gives the authority to only the owner of the contract. This function also determines the winner to end the lottery. This process is done by requesting a random number.

function fulfillRandomness(bytes32 _requestId, uint256 _randomness)internal override{
	require(lottery_state == LOTTERY_STATE.CALCULATING_WINNER,"You aren't there yet!");
	require(_randomness > 0, "random-not-found");
	uint256 indexOfWinner = _randomness % players.length;
	recentWinner = players[indexOfWinner];
	recentWinner.transfer(address(this).balance);
	// Reset
	players = new address payable[](0);
	lottery_state = LOTTERY_STATE.CLOSED;
	randomness = _randomness;
}


The above function requires the lottery state to be in the calculating winner. It also checks whether the given random number is positive. Then it uses the remainder operator to calculate the id of the winner from the random number it gets. After that, it transfers the balance of the contract to the player that has won the lottery And finally changes the state of the lottery to closed.

Configuration

Notice that up to here you need to enter the following into the Brownie-config.yaml:

dependencies:
	smartcontractkit/[email protected]
 	OpenZeppelin/[email protected]
compiler:
	solc:
		remappings:
			'@chainlink=smartcontractkit/[email protected]'
			'@openzeppelin=OpenZeppelin/[email protected]'
dotenv: .env
networks:
	default: development
	development:
		keyhash:'0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311'
		fee: 100000000000000000
	rinkeby:
		vrf_coordinator:'0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B'
		eth_usd_price_feed:'0x8A753747A1Fa494EC906cE90E9f37563A8AF630e'
		link_token: '0x01BE23585060835E02B77ef475b0Cc51aA1e0709'
		keyhash:'0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311'
		fee: 100000000000000000
		verify: True
	mainnet-fork:
		eth_usd_price_feed:'0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419'
		verify: False
wallets:
	from_key: ${PRIVATE_KEY}



Up to here, we only need the following to be able to compile the Brownie project:

dependencies:
	smartcontractkit/[email protected]
 	OpenZeppelin/[email protected]
compiler:
	solc:
		remappings:
			'@chainlink=smartcontractkit/[email protected]'
			'@openzeppelin=OpenZeppelin/[email protected]'


In the next parts, we are going to see how we can deploy and test this smart contract on different networks.

Deployment of the Lottery Project Using Brownie

In this section, we are going to continue the lottery project and focus on its deployment of it. The main python file as always is the deploy.py where we are going to deploy the four stages of a lottery: deploy the lottery, start the lottery, enter the lottery and end the lottery. The prize here is paid with a Chainlink token. As a result, we will add the ChainlinkTokenInterface contract to the contracts directory.

Managing the Folders of the Lottery Project Directory

In order to deploy the smart contract that we have just written, we need to complete the deployment and test folders and also add some other files like the dependencies of our smart contract. As always, the first step is to tyoe in the terminal: brownie init Then, you will see the folders and files that are created afterward. Then it is time to write our deploy.py file. As you know every lottery contract has 4 stages, deploying the lottery, starting it, entering the lottery, and ending it. So accordingly, we have 4 functions:

from scripts.helpful_scripts import get_account, get_contract, fund_with_link
from brownie import Lottery, network, config
import time

def deploy_lottery():
	account = get_account()
	lottery = Lottery.deploy(get_contract("eth_usd_price_feed").address, 						 
		get_contract("vrf_coordinator").address,
		get_contract("link_token").address,
		config["networks"][network.show_active()]["fee"],
		config["networks"][network.show_active()]["keyhash"],
		{"from": account},
		publish_source=config["networks"][network.show_active()].get("verify", False),)								
	print("Deployed lottery!")
	return lottery


In every contract deployment, we should determine some of the specifications related to a contract such as contract addresses (address of the dependency contracts) and some config specifications about every network.

def start_lottery():
	account = get_account()
	lottery = Lottery[-1]
	starting_tx = lottery.startLottery({"from": account})
	starting_tx.wait(1)
	print("The lottery is started!")


The above function starts the lottery. At first, it gets the account address which uses the get account function from helpful_scripts.py, then it gets the latest lottery smart contract, After that, it starts the lottery transaction using the startLottery function inside the contract.

def enter_lottery():
	account = get_account()
	lottery = Lottery[-1]
	value = lottery.getEntranceFee() + 100000000
	tx = lottery.enter({"from": account, "value": value})
	tx.wait(1)
	print("You entered the lottery!")


The above function also gets the account and the latest lottery contract records, then determine the value of the entrance fee (notice that 10000000 that has been added up with the entrance fee, is not a big number as it is in the Wei unit). Then, we enter the participant by using the enter function from the lottery.sol contract.

def end_lottery():
	account = get_account()
	lottery = Lottery[-1]
	# fund the contract
	# then end the lottery
	tx = fund_with_link(lottery.address)
	tx.wait(1)
	ending_transaction = lottery.endLottery({"from": account})
	ending_transaction.wait(1)
	time.sleep(180)
	print(f"{lottery.recentWinner()} is the new winner!")


In the above function, again get the account and the lottery contract, fund the winner with some link token (using the function written in helpful_scripts.py), and ends the transaction using the function in the lottery contract.sol.

def main():
	deploy_lottery()
	start_lottery()
	enter_lottery()
	end_lottery()


In the above main function, we apply other functions in the sequence of a lottery.

Helpful_Scripts.py

Now, it is time to go after helpful_scripts.py file and explain the codes:

from brownie import (accounts, network, config, MockV3Aggregator, VRFCoordinatorMock, LinkToken, 
	Contract, interface,)
FORKED_LOCAL_ENVIRONMENTS = ["mainnet-fork", "mainnet-fork-dev"]
LOCAL_BLOCKCHAIN_ENVIRONMENTS = ["development", "ganache-local"]

def get_account(index=None, id=None):
	if index:
		return accounts[index]
	if id:
		return accounts.load(id)
	if ( network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS or network.show_active() in 
			FORKED_LOCAL_ENVIRONMENTS):
		return accounts[0]
	return accounts.add(config["wallets"]["from_key"])


The above function determines the address of the available account according to the active network.

contract_to_mock = {"eth_usd_price_feed": MockV3Aggregator,"vrf_coordinator": VRFCoordinatorMock,
	"link_token": LinkToken,}
def get_contract(contract_name):
	contract_type = contract_to_mock[contract_name]
	if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		if len(contract_type) <= 0:
			# MockV3Aggregator.length
			deploy_mocks()
			contract = contract_type[-1]
			# MockV3Aggregator[-1]
	else:
		contract_address = config["networks"][network.show_active()][contract_name]
		contract = Contract.from_abi(contract_type._name, contract_address, contract_type.abi)
	return contract


The above function will grab the contract addresses from the Brownie config when defined. Otherwise, it will deploy a mock version of that contract, and return that mock contract.

DECIMALS = 8
INITIAL_VALUE = 200000000000
def deploy_mocks(decimals=DECIMALS, initial_value=INITIAL_VALUE):
	account = get_account()
	MockV3Aggregator.deploy(decimals, initial_value, {"from": account})
	link_token = LinkToken.deploy({"from": account})
	VRFCoordinatorMock.deploy(link_token.address, {"from": account})
	print("Deployed!")


The above function deploys the mock version of the 2 contracts: 1. VRFCoordinator and 2. V3Aggregator contracts.

def fund_with_link(contract_address, account=None, link_token=None,amount=100000000000000000): # 0.1 LINK
	account = account if account else get_account()
	link_token = link_token if link_token else get_contract("link_token")
	tx = link_token.transfer(contract_address, amount, {"from": account})
	tx.wait(1)
	print("Fund contract!")
	return tx


The above function funds the winner with a link token. Notice that we should create a file in the interfaces folder for the link token contract and name it LinkTokenInterface.sol. You can copy and paste the contract below:

Link Token Interface Contract

The below contract which is called LinkTokenInterface.sol, should be added to the contacts folder. We use the functions of this contract in the helpful_scripts.py and deploy.py to fund the winner of the lottery with some ChainLink tokens. If you look at the contract in detail, you will see that the functions and methods are the same as of an ERC-20 token contract.

pragma solidity ^ 0.6.6;

interface LinkTokenInterface {
	function allowance(address owner, address spender) external view returns (uint256 remaining);
	function approve(address spender, uint256 value) external returns (bool success);
	function balanceOf(address owner) external view returns (uint256 balance);
	function decimals() external view returns (uint8 decimalPlaces);
	function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
	function increaseApproval(address spender, uint256 subtractedValue)external;
	function name() external view returns (string memory tokenName);
	function symbol() external view returns (string memory tokenSymbol);
	function totalSupply() external view returns (uint256 totalTokensIssued);
	function transfer(address to, uint256 value) external returns (bool success);
	function transferAndCall(
		address to,
		uint256 value,
		bytes calldata data
		) external returns (bool success);
	function transferFrom(
		address from,
		address to,
		uint256 value
		) external returns (bool success);
}


Compiling Our Complete Lottery Project Using Brownie for Final Deployment

In this section, following the lottery project, we are going to complete the scripts that should be written so that we can finally compile our complete contract and be able to deploy it fully. The focus of this section is on the testing of the contract, where we are going to test the different functionalities of different stages of the lottery project. In the following of the lottery project, we are going to complete the scripts that should be written so that we can finally compile our complete contract and be able to deploy it fully.

Brownie_config.sol

There are some .sol dependencies that need to be copied and pasted into the contracts/test folder so that we can use them to deploy our main lottery smart contract. So make sure you copy them from this link and paste them into your directory.

.env File

Also, in the .env file enter the private key of your test Metamask account (Do not use a Metamask wallet with real crypto in it), your Etherscan Token, and Infura ID.

export WEB3_INFURA_PROJECT_ID=''
export PRIVATE_KEY=''
export ETHERSCAN_TOKEN=''


Testing the Contract Functionalities

Now, every standard smart contract deployment needs testing and this one is not an exception, we start our test with test_lottery_unit.py:

from scripts.helpful_scripts import ( LOCAL_BLOCKCHAIN_ENVIRONMENTS,get_account,fund_with_link,
	get_contract,)
from brownie import Lottery, accounts, config, network, exceptions
from scripts.deploy_lottery import deploy_lottery
from web3 import Web3
import pytest

def test_get_entrance_fee():
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip()
	# Arrange
	lottery = deploy_lottery()
	# Act
	# 2,000 eth / usd
	# usdEntryFee is 50
	# 2000/1 == 50/x == 0.025
	expected_entrance_fee = Web3.toWei(0.025, "ether")
	entrance_fee = lottery.getEntranceFee()
	# Assert
	assert expected_entrance_fee == entrance_fee


The above test considers the price of ETH is 2000 dollars and the entrance fee is 50 dollars so the 0.025 ether is required to participate in the lottery. as a result, we expect the entrance fee to be 0.025 ether, so we call the getEn-tranceFee() function to check the validity of the result.

def test_cant_enter_unless_started():
	# Arrange
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip()
	lottery = deploy_lottery()
	# Act / Assert
	with pytest.raises(exceptions.VirtualMachineError):
		lottery.enter({"from": get_account(), "value": lot-tery.getEntranceFee()})


The above test checks whether the participant can enter or not at the times the lottery has been closed or has not opened yet.

def test_can_start_and_enter_lottery():
	# Arrange
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip()
	lottery = deploy_lottery()
	account = get_account()
	lottery.startLottery({"from": account})
	# Act
	lottery.enter({"from": account, "value": lottery.getEntranceFee()})
	# Assert
	assert lottery.players(0) == account


The above test checks whether the player who has participated in the lottery is recorded in the list of lottery players in other words it checks if the player has entered or not.

def test_can_end_lottery():
	# Arrange
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip()
	lottery = deploy_lottery()
	account = get_account()
	lottery.startLottery({"from": account})
	lottery.enter({"from": account, "value": lottery.getEntranceFee()})
	fund_with_link(lottery)
	lottery.endLottery({"from": account})
	assert lottery.lottery_state() == 2



The above test checks whether the end lottery function works. To do so, it first passes all the other tests and applies the other stages of the lottery and at the end tests the lottery state.

def test_can_pick_winner_correctly():
	# Arrange
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip()
	lottery = deploy_lottery()
	account = get_account()
	lottery.startLottery({"from": account})
	lottery.enter({"from": account, "value": lottery.getEntranceFee()})
	lottery.enter({"from": get_account(index=1), "value": 	lot-tery.getEntranceFee()})
	lottery.enter({"from": get_account(index=2), "value": 	lot-tery.getEntranceFee()})
	fund_with_link(lottery)
	starting_balance_of_account = account.balance()
	balance_of_lottery = lottery.balance()
	transaction = lottery.endLottery({"from": account})
	request_id = transaction.events["RequestedRandomness"]["requestId"]
	STATIC_RNG = 777
	get_contract("vrf_coordinator").callBackWithRandomness(request_id, STATIC_RNG, lottery.address,
		{"from": account})
	# 777 % 3 = 0
	assert lottery.recentWinner() == account
	assert lottery.balance() == 0
	assert account.balance() == starting_balance_of_account + bal-ance_of_lottery
	


The above test uses all of the operations of the lottery that have been tested to test the correctness of the winner picking. To do so, it first enters an account into the lottery and at the end, tests whether the winner is in the same account, whether the lottery contract balance has turned 0 as a result of sending money to the winner, and whether the winner’s account has been added up with the balance of the lottery contract.

from brownie import network
import pytest
from scripts.helpful_scripts import ( LOCAL_BLOCKCHAIN_ENVIRONMENTS,
	get_account, fund_with_link,)
from scripts.deploy_lottery import deploy_lottery
import time

def test_can_pick_winner():
	if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip()
	lottery = deploy_lottery()
	account = get_account()
	lottery.startLottery({"from": account})
	lottery.enter({"from": account, "value": lottery.getEntranceFee()})
	lottery.enter({"from": account, "value": lottery.getEntranceFee()})
	fund_with_link(lottery)
	lottery.endLottery({"from": account})
	time.sleep(180)
	assert lottery.recentWinner() == account
	assert lottery.balance() == 0


The above test also does the same process in a little bit different manner.

It is important to know that we should do all of the tests one by one to be able to debug the functions. As you can see, we started our first test with the first stage of the contract.

Final Word on Lottery Project Deployment Using Brownie

Firstly, we have managed to get started with the lottery project and have written the smart contract for it. In addition to that, we have added some dependency contracts like VRFConsumerBase, Openzeppelin, and V3AggregatorInterface.

Secondly, we have managed to write the deploy.py and helpful_scripts.py to interact with the lottery smart contract and deploy the different stages of a lottery such as deploying the lottery, starting it, entering it using different accounts, and ending it. In the end, the winner is going to be awarded some Chainlink tokens.

Thirdly, we have managed to complete the whole lottery project script file so that we can finally compile and deploy the smart contract. In most of the parts of this tutorial, we focused on testing the contract scripts for testing the different functionalities related to the different stages of the contract.

Download this Article in PDF format

web developement

Check Out Our Services

In Arashtad, we're working on 3D games, metaverses, and other types of WebGL and 3D applications with our 3D web development team. However, our services are not limited to these. Back-end developments, front-end developments, 3d modeling, and animations are in our arsenal too.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development

Using Brownie to Switch between Different Networks

In this article, we are going to switch between the different available networks we can connect to, using Brownie. Then, we’ll write a statement in the Deploy.py to connect to the network requested in the terminal by the user (the developer). This kind of script for network management makes it easy for us to connect to any network that we want at any time it is required for testing and other purposes.

How to Switch between Networks

As you know, we have different networks to work with. We have already used some of them for deploying our contracts. Sometimes we need to write our deploy.py file in a way that we can switch between different networks and accounts and the deploy.py must understand which ones are available.

Switch between Different Networks

Before we get started with this task, we can check the keywords when we want to connect to different accounts. To look up the keyword related to any network, we write:

brownie networks list

Result:

Brownie v1.18.1 - Python development framework for Ethereum The following networks are declared: Ethereum ├─Mainnet (Infura): mainnet ├─Ropsten (Infura): ropsten ├─Rinkeby (Infura): rinkeby ├─Goerli (Infura): goerli └─Kovan (Infura): kovan Ethereum Classic ├─Mainnet: etc └─Kotti: kotti Arbitrum └─Mainnet: arbitrum-main Avalanche ├─Mainnet: avax-main └─Testnet: avax-test Aurora ├─Mainnet: aurora-main └─Testnet: aurora-test Binance Smart Chain ├─Testnet: bsc-test └─Mainnet: bsc-main Fantom Opera ├─Testnet: ftm-test └─Mainnet: ftm-main Harmony └─Mainnet (Shard 0): harmony-main Moonbeam └─Mainnet: moonbeam-main Optimistic Ethereum ├─Mainnet: optimism-main └─Kovan: optimism-test Polygon ├─Mainnet (Infura): polygon-main └─Mumbai Testnet (Infura): polygon-test XDai ├─Mainnet: xdai-main └─Testnet: xdai-test Development ├─Ganache-CLI: development ├─Geth Dev: geth-dev ├─Hardhat: hardhat ├─Hardhat (Mainnet Fork): hardhat-fork ├─Ganache-CLI (Mainnet Fork): mainnet-fork ├─Ganache-CLI (BSC-Mainnet Fork): bsc-main-fork ├─Ganache-CLI (FTM-Mainnet Fork): ftm-main-fork ├─Ganache-CLI (Polygon-Mainnet Fork): polygon-main-fork ├─Ganache-CLI (XDai-Mainnet Fork): xdai-main-fork ├─Ganache-CLI (Avax-Mainnet Fork): avax-main-fork └─Ganache-CLI (Aurora-Mainnet Fork): aurora-main-fork

As you can remember, we have used Rinkeby from Infura a number of times and here the keyword for Rinkeby (Infura) is Rinkeby. Here, when we work with Brownie, in order to define the Infura RPC URL on the .env file, we need to write it in a different format:
export WEB3_INFURA_PROJECT_ID=80ca094b614b44b3b647ceb01a2b70d0
Notice that if you write any word other than WEB3_INFURA_PROJECT_ID, you will have a problem working with the Rinkeby or other networks of Infura, because Brownie has some built-in scripts that read from the ID with only this name.

Swtich Between Networks via Network Management in Deploy.py

The next thing we should do when we want to check for networks available for deployment is to add the following function to our deploy.py file:


def get_account():
	if network.show_active() == "development":
		return accounts[0]
	else:
		return accounts.add(config["wallets"]["from_key"])


And whenever we want to define our account, we write:
Account = get_account()
It is also necessary to import the network from Brownie:
from Brownie import network
The above code checks whether we want to use our Ganache CLI test accounts or use any other accounts that we have defined its a private key to our .env file and introduced to Brownie.
In order to connect to Rinkeby account in our Metamask wallet (that we entered its private key in the .env file), and the Rinkeby test network, in the terminal we should type:

brownie run scripts/deploy.py --network rinkeby

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Running 'scripts/deploy.py::main'... Transaction sent: 0xbfe6fb9f152eeda4fc880fc5a5cb6f74b0d73b440ada91e8bef71fb7fccf1ccd Gas price: 1.000000012 gwei Gas limit: 367598 Nonce: 48 SimpleStorage.constructor confirmed Block: 10426128 Gas used: 334180 (90.91%) SimpleStorage deployed at: 0x1570258Ee66a921A3f5fdEA48f5ba54bE657AA8b 0 Transaction sent: 0x0a5d8886732d99e6045eede0958894a033e48225d6818c312491b178195576ed Gas price: 1.000000012 gwei Gas limit: 47842 Nonce: 49 SimpleStorage.store confirmed Block: 10426129 Gas used: 43493 (90.91%) SimpleStorage.store confirmed Block: 10426129 Gas used: 43493 (90.91%) 38

And you can see the transaction related to contract deployment and storing a value inside the contract has been successfully completed on the Rinkeby network. Because we are working with Rinkeby, the transaction is trackable on this link. To be able to track it, simply copy and paste the address of the transaction into the search bar of the Etherscan.

Etherscan

Reading from the Contracts

One of the useful and necessary steps in writing an application related to a smart contract is being able to read from the transaction. The following steps will help you retrieve the different properties of the deployed smart contracts.
First, create a file in the scripts folder and name it read_value.py then write the following code in it.


from brownie import SimpleStorage, accounts, config

def read_contract():
	print(SimpleStorage)

def main():
	read_contract()


The above code, reads the contract deployments and their transactions. Let’s see the result by typing in the terminal:

brownie run scripts/read_value.py --network rinkeby

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Running 'scripts/read_value.py::main'...

The above result shows an array inside which we can retrieve the first member by changing the code to:


from brownie import SimpleStorage, accounts, config

def read_contract():
	print(SimpleStorage[0])

def main():
	read_contract()


brownie run scripts/read_value.py --network rinkeby

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Running 'scripts/read_value.py::main'... 0x1570258Ee66a921A3f5fdEA48f5ba54bE657AA8b

If you take a closer look, you will see that the address given here, is just the same address that we saw on Etherscan (the address of the contract deployment). It is worth knowing that if you want to retrieve the latest address contract that has been deployed, instead of print(SimpleStorage[0]) you can write print (SimpleStorage[-1]).
Notice that Brownie already knows the address and the ABI of our smart contract because it has saved it in a .json file. Also, if we want to retrieve a number again without asking the blockchain for it, we can retrieve it in the read_value.py file by writing:


print(SimpleStorage[-1].retrieve()

Result:

38

Summing Up

In this article, we have managed to write a script that makes it easy to switch between different networks such as Testnet (like Rinkeby), Mainnet, and local (like Ganache CLI) for the users and the developers. With this script written in the deploy.py, we can choose the network at the time of running Brownie by simply calling the network’s name in the terminal.

Download this Article in PDF format

web developement

Check Out Our Services

In Arashtad, we’re working on 3D games, metaverses, and other types of WebGL and 3D applications with our 3D web development team. However, our services are not limited to these. Back-end developments, front-end developments, 3d modeling, and animations are in our arsenal too.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development

Testing Deploy.py Script Using Brownie: A Full Analysis

In this post, we will complete testing our deploy.py script using Brownie in more detail so that we can see if there is any problem at all. And if there is, we can analyze it in more detail. This kind of detailed test and code analysis becomes handy, especially when the code is too long and complex that we cannot so easily understand where the unexpected result originates from.

Detailed Testing Deploy.py Using Brownie

For more detailed testing, we can write in the terminal:

brownie test -k

Which in our case will be:

brownie test -k test_updating_storage

And the result will be:

Brownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 2 items / 1 deselected / 1 selected Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py . [100%] =============================================== 1 passed, 1 deselected in 1.85s =============================================== Terminating local RPC client...

And if we write anything wrong in our code, by running:

brownie test --pdb

In the terminal, we will see:

Brownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 2 items Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py F >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def test_deploy(): account = accounts[0] simple_storage = SimpleStorage.deploy({"from": account}) starting_value = simple_storage.retrieve() expected = 38 > assert starting_value == expected E assert 0 == 38 tests/test_simple_storage.py:10: AssertionError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB post_mortem (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage/tests/test_simple_storage.py(10)test_deploy() -> assert starting_value == expected

This shows that in the first function we mistakenly expected the stored value to be 38 which was 0 at first. We can also do some sort of debugging next to PDB in the terminal for example we can write:

(Pdb) expected >> 38

Which shows 38 as the number stored in expected, or:

(Pdb) simple_storage.retrieve() >>0

By understanding the difference between the expected number and the retrieved one, we will be able to debug the code.

(Pdb) simple_storage

The better way to fully understand the problems and be able to find it, is to type in the terminal:

brownie test -s Brownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /home/mohamad/.local/pipx/venvs/eth-brownie/bin/python cachedir: .pytest_cache hypothesis profile 'brownie-verbose' -> verbosity=2, deadline=None, max_examples=50, stateful_step_count=10, report_multiple_bugs=False, database=DirectoryBasedExampleDatabase(PosixPath('/home/mohamad/.brownie/hypothesis')) rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 2 items Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py::test_deploy FAILED tests/test_simple_storage.py::test_updating_storage PASSED ========================================================== FAILURES =========================================================== _________________________________________________________ test_deploy _________________________________________________________ def test_deploy(): account = accounts[0] simple_storage = SimpleStorage.deploy({"from": account}) starting_value = simple_storage.retrieve() expected = 38 > assert starting_value == expected E assert 0 == 38 E +0 E -38 tests/test_simple_storage.py:10: AssertionError =================================================== short test summary info =================================================== FAILED tests/test_simple_storage.py::test_deploy - assert 0 == 38 ================================================= 1 failed, 1 passed in 1.94s ================================================= Terminating local RPC client…

As you can see in the result, the code and its variables are fully analyzed.

Fixing the Code

Now if we fix our code and change the expected in the first function to 0:


from brownie import SimpleStorage, accounts
def test_deploy():

	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	starting_value = simple_storage.retrieve()
	expected = 0
	assert starting_value == expected

def test_updating_storage():

	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	expected = 38
	simple_storage.store(expected,{"from":account})
	assert expected == simple_storage.retrieve()
	



And test our deployment again:

brownie test -s

We will see:

rownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /home/mohamad/.local/pipx/venvs/eth-brownie/bin/python cachedir: .pytest_cache hypothesis profile 'brownie-verbose' -> verbosity=2, deadline=None, max_examples=50, stateful_step_count=10, report_multiple_bugs=False, database=DirectoryBasedExampleDatabase(PosixPath('/home/mohamad/.brownie/hypothesis')) rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 2 items Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py::test_deploy PASSED tests/test_simple_storage.py::test_updating_storage PASSED ====================================================== 2 passed in 2.08s ====================================================== Terminating local RPC client...

Brownie test uses the functions in the Pytest module and you can use more functions than mentioned here to be able to have all different kinds of analysis on your smart contract deployment.

What We Achieved through Testing Deploy.py Using Brownie

In this article, we’ve completed testing the deployments of the smart contracts (deploy.py script) using Brownie in more detail, so that we can see if there is any problem at all and if there is, we can analyze it in more detail. This kind of detailed test and code analysis becomes handy, especially when the code is too long and complex that we cannot so easily understand where the unexpected result originates from.

Download this Article in PDF format

metaverse

What Do We Do in Arashtad?

3D websites, 3D games, metaverses, and other types of WebGL and 3D applications are what we develop in our 3D web development team.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Request a Quote
ThreeJS Development

How to Interact with Smart Contracts Using Brownie

After installing and working with Ganache CLI, we are going to interact with simple storage smart contracts Using Brownie. Meaning that we want to store and retrieve a value within the contract. After doing this you will see how simple and efficient Brownie is compared to other methods in python. We are also going to use testing modules to test the functionality of the contract and its deployment.

Using Brownie to Interact with Smart Contracts

In the previous section of this tutorial, we learned how to deploy our simple storage smart contract. In this one, we are going to interact with it, meaning that we want to store a value inside the contract and retrieve it using Brownie. After doing this you will see how simply and efficiently Using Brownie to interact with smart contracts can get compared to other methods in python.

Retrieving the Stored Data:

So, to follow our deploy.py script, we have:


from Brownie import accounts, config, SimpleStorage
def deploy_simple_storage():

	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	stored_value = simple_storage.retrieve()
	print(stored_value)
	transaction = simple_storage.store(38,{"from": account})
	transaction.wait(1)
	updated_stored_value = simple_storage.retrieve()
	print(updated_stored_value)

def main():
	deploy_simple_storage()


In the above code, we first try to retrieve the stored value inside the contract. Then, we store a number in it and again retrieve the stored value. By running this code using:

brownie run scripts/deploy.py

We have:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... Running 'scripts/deploy.py::main'... Transaction sent: 0xbbd21a1abc42f0d21f4651b71cddadddecf6ba99af3a31a140434950c7e36876 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0 SimpleStorage.constructor confirmed Block: 1 Gas used: 334180 (2.78%) SimpleStorage deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87 0 Transaction sent: 0x6bd512176120555da2d2682b4dd0256ccb8060a5c63d678d65ff4b554ffd7477 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 1 SimpleStorage.store confirmed Block: 2 Gas used: 41393 (0.34%) SimpleStorage.store confirmed Block: 2 Gas used: 41393 (0.34%) 38 Terminating local RPC client…

As you can see, 2 blocks have been hashed at the end. And at first, we have nothing stored. So, the first number that has been retrieved is 0, and then after storing the number 38, we have it as the result of the retrieve.

Testing Smart Contracts Using Brownie

One of the important steps in developing a smart contract is to test our contract. Because in the main deployment of the transaction in a real-world smart contract if anything goes wrong, like spending infinitely extra ETH or mistakes like this, there will be no way to compensate for it. The tests folder is located in the main directory and is created by Brownie. We create a file named test_simple_storage.py and write:


from Brownie import SimpleStorage, accounts
def test_deploy():
	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	starting_value = simple_storage.retrieve()
	expected = 0
	assert starting_value == expected


The above script does the same as deploy.py with the difference that it tests whether the output is the expected value or not. This will help us to understand the existing bugs without spending ETH.

brownie test

Result:

Brownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 1 item Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py . [100%] ====================================================== 1 passed in 2.17s ====================================================== Terminating local RPC client…

def test_deploy():
	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	starting_value = simple_storage.retrieve()
	expected = 38
	assert starting_value == expected


brownie test Brownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 1 item Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py F [100%] ========================================================== FAILURES =========================================================== _________________________________________________________ test_deploy _________________________________________________________ def test_deploy(): account = accounts[0] simple_storage = SimpleStorage.deploy({"from": account}) starting_value = simple_storage.retrieve() expected = 38 > assert starting_value == expected E assert 0 == 38 tests/test_simple_storage.py:10: AssertionError =================================================== short test summary info =================================================== FAILED tests/test_simple_storage.py::test_deploy - assert 0 == 38 ====================================================== 1 failed in 1.83s ====================================================== Terminating local RPC client… *****

As you can see, it shows failure because we haven’t stored any value yet and the expected number should be 0 instead of 38. Now, let’s define another function for storing the expected number, retrieving and putting it into the test:


from Brownie import SimpleStorage, accounts
def test_deploy():

	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	starting_value = simple_storage.retrieve()
	expected = 38
	assert starting_value == expected

def test_updating_storage():

	account = accounts[0]
	simple_storage = SimpleStorage.deploy({"from": account})
	expected = 38
	simple_storage.store(expected,{"from":account})
	assert expected == simple_storage.retrieve()


brownie test

Result:

Brownie v1.18.1 - Python development framework for Ethereum ===================================================== test session starts ===================================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/Desktop/solidity/Solidity and web3 codes/brownie_simple_storage plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 2 items Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... tests/test_simple_storage.py F. [100%] ========================================================== FAILURES =========================================================== _________________________________________________________ test_deploy _________________________________________________________ def test_deploy(): account = accounts[0] simple_storage = SimpleStorage.deploy({"from": account}) starting_value = simple_storage.retrieve() expected = 38 > assert starting_value == expected E assert 0 == 38 tests/test_simple_storage.py:10: AssertionError =================================================== short test summary info =================================================== FAILED tests/test_simple_storage.py::test_deploy - assert 0 == 38 ================================================= 1 failed, 1 passed in 2.07s ================================================= Terminating local RPC client…

As you can see, one test failed and the other passed. The expected result was not 38 at first but, after storing it as expected, it passed.

Last Thought on Using Brownie to Interact with Smart Contracts

In this article, we have managed to retrieve the data we stored using the deploy.py python script. We have also created test.py files in the test directory to be able to see if the smart contract and the deploy.py work correctly. The Brownie module comes with a test package called pytest that makes the developers capable of testing any part of their script using the Brownie test command.

Download this Article in PDF format

metaverse

Care to Know About Metaverse?

In Arashtad, we are providing custom services on 3d developments such as 3d websites, 3d models, metaverses and all 3d applications.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development

Using Ganache CLI with Brownie to Deploy A Smart Contract

In this article, after installing Brownie, we are going to install the Ganache CLI (Command Line Interface) and use it with Brownie. The Ganache CLI is the same as Ganache IDE with the difference that it automatically runs without the need to open the Ganache IDE. At the end end of this tutorial, we will be able to deploy our smart contracts on top of Ganache CLI simulated blockchain.

Installing Ganache:

To continue our journey with Brownie, we need to make sure Ganache CLI has been installed successfully. To make sure that it has, and also see its virtual accounts, in your console (terminal) write:

ganache

And it should give you the test accounts like this:


ganache v7.0.3 (@ganache/cli: 0.1.4, @ganache/core: 0.1.4)
Starting RPC server

Available Accounts
==================
(0) 0x960F41C52ffAef71fFef2fFBC3C0EA5Dc5748086 (1000 ETH)
(1) 0x39Cf64532C1126c6C9Fa0a02338389d1eb861A12 (1000 ETH)
(2) 0xA1E47c46425e24565666ED350e1edEf6532f348c (1000 ETH)
(3) 0xE441bb58b5369DB428a979fbE77Df2d3Da1F91d4 (1000 ETH)
(4) 0x4498662A9691cfef775d3D212f5938905B5de70E (1000 ETH)
(5) 0x4Da709036c65CB81bF74970945f21Ac2e42e9cA9 (1000 ETH)
(6) 0x32872e4dDB4c4876e7B1359013696344fd976C22 (1000 ETH)
(7) 0x6aadaB117c9f26726D5e23b9A0fdC475F3BEd181 (1000 ETH)
(8) 0xCf763def60f3247CE5bEF18BAfC080dA3573595b (1000 ETH)
(9) 0x3a6AFe48C0889820aa587E9Dd45D1B7d8E4e5039 (1000 ETH)

Private Keys
==================
(0) 0x1093c2edf08326559ffbaccfe2a5c5826d2bee9b42fc71c51ad8d989100a3a31
(1) 0x4ebeaf4a4bb07a875255d1f4627146dcc69c930cc380701e1f338d1975240135
(2) 0xa25ad60447f26ad265962660c88bb04ff671f3a55c0f59985530088dad82c9c8
(3) 0x706fce79c758f53fc9336507a35d66ec285c8f52147a6f7676a0e89925f21d0c
(4) 0xd92dc461456414fa41af48ca8806a50031bdc4d587b8e49b1a3f501790f4b853
(5) 0x0b467e91bb9fef30c51077d9a7ba4b9d0797fe46f7fc1cff202751d9bfcf0bb0
(6) 0xaa05bda4d14e2fd3cac04174feeca371567b8f990acc6fab001f0b3317680d50
(7) 0x3a263493e9763bb5b7f069cd51cbca836298831d59b7f57125c937f8ef76dd82
(8) 0x4d6edbb1e7d4283cdf7cc8ce802ef72bcb7b684a51ab7098834e48a638bcfae5
(9) 0x5cb8a425439d9db86c6a58438fb5a6fa425ab60c675d9fd90bdc14cf093bf33c

HD Wallet
==================
Mnemonic:      patrol juice song save pretty combine dish table sock robust glow bunker
Base HD Path:  m/44'/60'/0'/0/{account_index}

Default Gas Price
==================
2000000000

BlockGas Limit
==================
30000000

Call Gas Limit
==================
50000000

Chain Id
==================
1337

RPC Listening on 127.0.0.1:8545


If you don’t get the above results or you get:

ganache is not recognized …

You might need to install it and that shows that Ganache CLI has not yet been installed. Notice that on Linux, instead of writing:

npm install –-global ganache

You should add sudo at the beginning of the command. Otherwise, it will throw an error saying that the node modules folder is not in usr/local/lib directory. So for Linux, we install the Ganache CLI like this:

sudo npm install –global ganache

Also, before installing the Ganache CLI, you need to make sure you have the node installed on your OS. You can check it by typing:

node -v

Result:

v16.14.2

Deploying the Contract Using Ganache CLI:

After that, we make sure that all the dependencies have been installed, we can continue with Brownie scripts on deploy.py:
First, run Brownie:

brownie

Then, fetch one of the Ganache accounts using the following code in the deploy.py file:


from brownie import accounts

def deploy_simple_storage():
	account = accounts[0]
	print(account)

def main():
	deploy_simple_storage()


We will run the project by the following command in the terminal (In the directory of the project’s folder):

brownie run scripts/deploy.py

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Attached to local RPC client listening at '127.0.0.1:8545'... Running 'scripts/deploy.py::main'... 0x960F41C52ffAef71fFef2fFBC3C0EA5Dc5748086

Using Environment Variables for the Private Key:

If you can remember from the previous tutorial, we added the Metamask account to Brownie and the terminal asked for the private key. Now, if we do not want to enter the password every time and make the process run automatically without asking the user for a private key or password, we go after the environment variables again. But, this time is a little a bit different. Like before, we create a .env file and paste our private key:


export PRIVATE_KEY= “here goes your private key”

Then, we create a file called brownie-config.yaml and paste the following code:


dotenv: .env
wallets:
	from_key: ${PRIVATE_KEY}


Then, we modify our deploy.py file as below:


from brownie import accounts, config

def deploy_simple_storage():
	account = accounts.add(config["wallets"]["from_key"])
	print(account)

def main():
	deploy_simple_storage()



And if we run it by the following command in the terminal:

brownie run scripts/deploy.py

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Attached to local RPC client listening at '127.0.0.1:8545'... Running 'scripts/deploy.py::main'... 0x25E681EE76469E4cF846567b772e94e082907117

Now to actually deploy the simple storage contract, we import the SimpleStorage in the deploy.py file and also add the contract.deploy({}) line to the first function:


def deploy_simple_storage():


from brownie import accounts, config, SimpleStorage

def deploy_simple_storage():
	account = accounts.add(config["wallets"]["from_key"])
	SimpleStorage.deploy({"from": account})

def main():
	deploy_simple_storage()


And

brownie run scripts/deploy.py

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... Running 'scripts/deploy.py::main'... Transaction sent: 0x1d70d1a30b0266da050d696c14b749bb5e34425159817e5f6e66b1e615221685 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0 SimpleStorage.constructor confirmed Block: 1 Gas used: 334180 (2.78%) SimpleStorage deployed at: 0x647Dc9EE9731d35b9031A9ca6c664D6b75d14385 Terminating local RPC client…

Deploying the Contract with the Ganache CLI Accounts

You can also test this with the Ganache accounts:


from brownie import accounts, config, SimpleStorage

def deploy_simple_storage():
	account = accounts[0]
	SimpleStorage.deploy({"from": account})

def main():
	deploy_simple_storage()


Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieSimpleStorageProject is the active project. Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --server.port 8545 --miner.blockGasLimit 12000000 --wallet.totalAccounts 10 --hardfork istanbul --wallet.mnemonic brownie'... Running 'scripts/deploy.py::main'... Transaction sent: 0xbbd21a1abc42f0d21f4651b71cddadddecf6ba99af3a31a140434950c7e36876 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0 SimpleStorage.constructor confirmed Block: 1 Gas used: 334180 (2.78%) SimpleStorage deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87 Terminating local RPC client…

Great! Now we have managed to deploy our contract using Brownie. The Brownie adventure however is goes on in our other articles.

Summing Up

In this article, we have managed to install the Ganache CLI, to be able to automatically use it whenever we want to test our deployment in a simulated blockchain. We have also considered a way to include our private key somewhere safe (in the .env file) out of the deploy.py script file. In the end, we deployed our contract using the Ganache CLI.

Download this Article in PDF format

3d websites

Care to Know Us More?

In Arashtad, we have gathered a professional team of developers who are working in fields such as 3D websites, 3D games, metaverses, and other types of WebGL and 3D applications.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Request a Quote
ThreeJS Development

How to Install and Use Brownie to Deploy A Smart Contract

In this article, we are going to install and use Brownie as a much simpler tool to deploy Solidity smart contracts using python scripts. Throughout this tutorial, we will install Brownie, create a Brownie project folder and deploy the simple storage smart contract. You will see that the whole process is done much easier and faster with Brownie rather than python web3.py scripts.

Install and Use Brownie to Deploy A Smart Contract

We have learned how to deploy our smart contracts using python web3 tools, but if you look at the scripts you will see that we have taken a very long way to get there. However, there is an easier and more efficient way to deploy our contracts in python and that is Brownie. Brownie is the most common smart contract development platform built based on python. It is used by DeFi giants like Curve DAO, Yearn Finance, and Badger DAO. Brownie relies on web3.py. That’s why we started our first python tutorials with web3.

Install Brownie

To install Brownie, we need to first install pipx. Pipx will automatically create a virtual environment and after install Brownie using it, makes it available on the terminal. To install pipx on your terminal, use:

python3 -m pip install --user pipx

And on Linux, you should also install the following:

apt install python3.8-venv

And then:

python3 -m pipx ensurepath

And now, we can install Brownie:

pipx install eth-brownie

And, upgrade it:

pipx upgrade eth-brownie

Creating a Brownie project:

If you want to make sure that Brownie has been successfully installed open another terminal and type: brownie –version

Now, that Brownie has been installed, let’s create our first project using it:

mkdir brownie_simple_storage

And then to create our files automatically:

brownie init

And you will be able to see that a number of files have been created afterward.

Build File:

Keeps track of low-level stuff like the .json file of the contract, deployments, and interfaces.

Contracts File:

Where we write our contract.

Interfaces File:

Where we store our interface tools to make it easier to work with the blockchain And other folders which we talk about later. So, inside our contracts folder, we make a file called SimpleStorage.sol:

cd contracts touch SimpleStorage.sol

And copy and paste the recent SimpleStorage contract that we wrote on Solidity tutorials.


// SPDX-License-Identifier: MIT

pragma solidity >= 0.6.0 < 0.9.0;

contract SimpleStorage {

    uint256 Salary;
    struct Employees {
	uint256 Salary;
	string name;
    }

    Employees[] public employee;
    mapping(string => uint256) public nameToSalary;

    function store(uint256 _Salary) public {
	Salary = _Salary;
    }

    function retrieve() public view returns (uint256){
	return Salary;
    }

    function addPerson(string memory _name, uint256 _Salary) public {
	employee.push(Employees(_Salary, _name));
	nameToSalary[_name] = _Salary;
    }
}


now if you go back to the main folder and type: brownie compile

You will see that the project has been easily compiled without the need of any scripts. And also if you go to build/contracts , you will be able to see the .json file with the name of the contract SimpleStorage.json. And if you can remember from the previous tutorials, the .json file of the contract contains data like ABI, bytecode, opcode, and so on.

Deploying the Contract Using Brownie

Now, it is time to deploy this contract using Brownie. So to do that we go to the scripts folder and create a file called deploy.py. And before we start writing our scripts, let’s review our python code on web3. We needed ABI and bytecode which Brownie has automatically provided out of our smart contract, we also needed RPC URI that we copied from either Ganache or Infura. The Brownie here uses Ganache-cli and you need to make sure you have it installed if you want to deploy your contracts. The following code should be written in deploy.py.


from brownie import accounts

def deploy_simple_storage():
    account = accounts[0]
    print (account)

def main():
    print("Hello World!")


And it is run by typing:

brownie run /scripts/deploy.py

In the terminal, we will see that we are given an account address with its private key.
There is also another way to have an account on Brownie and that is to use our Metamask wallet.
To do that, in the terminal we write:

brownie accounts new MyAccount

And you will see that the terminal asks you to enter your private key:

Now, you can copy and paste your account private key to introduce it to Brownie and be able to use it afterward. Then, you enter a password:

SUCCESS: A new account '0x25E681EE76469E4cF846567b772e94e082907117' has been generated with the id 'MyAccount'

You will see the above result on the terminal. And it tells you that you can use the ‘MyAccount’ id instead of writing your private key.
You can write in console:

brownie accounts list

Result:

Found 1 account: └─MyAccount: 0x25E681EE76469E4cF846567b772e94e082907117

And, if you want to retrieve your private key in the future, we write in our deploy.py:


from brownie import accounts

def deploy_simple_storage():
    account = accounts.load("MyAccount")
    print(account)

def main():
    print("Hello World!")


After running this code by writing:

brownie run /scripts/deploy.py

We will see that the terminal asks us about our password So that we can finally access our private key. Notice that this a safer way than using a .env file, but still using environment variables is a safe way to protect your private key.
We can also delete our account using this command:

brownie accounts delete MyAccount

Result:

SUCCESS: Account 'MyAccount' has been deleted

Last Thought

So, up to now, we have learned about some of the good features and capabilities of Brownie and we have managed to deploy our smart contract much easier than with python web3 tools, this time using Brownie. However, managing the installed Brownie to deploy a Simple Storage contract is the least we can do. So, We’re going to cover the next stages in our next articles.

Download this Article in PDF format

web developement

Check Out Our Services

In Arashtad, we’re working on 3D games, metaverses, and other types of WebGL and 3D applications with our 3D web development team. However, our services are not limited to these. Back-end developments, front-end developments, 3d modeling, and animations are in our arsenal too.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development

Working with Crowdfunding on Etherscan Using Brownie and Solidity

In this tutorial, we are going to implement the crowdfunding project folder and compile it. Then we are going to interact with the compiled and deployed crowdfunding contract on the Etherscan. There are a number of scripts that we are going to work on. They are helpful_scripts.py, deploy.py, brownie_config.yaml, Fundme.sol, and .env file.

Crowdfunding on Etherscan: the Essentials

If you have read the solidity smart contracts tutorial, you can remember how we wrote the FundMe.sol contract and how we deployed it using Remix IDE. As you know, Remix IDE is just for learning solidity and beginners and we need some other deployment tools such as Node.js or Python web3, or brownie to be able to run it in a real-world application. To set up the Fundme.sol contract inside Brownie, we take the following steps:

1. Create a directory folder for the project:

mkdir brownie_fund_me

2. In the terminal:

brownie init

3. Inside the contracts folder, create a file called FundMe.sol and paste the FundMe smart contract that we wrote earlier in it.

Fundme.sol:


// SPDX-License-Identifier: MIT

pragma solidity >= 0.6.6 < 0.7.0;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
import "@chainlink/contracts/src/v0.6/vendor/SafeMathChainlink.sol";

contract FundMe {
	using SafeMathChainlink for uint256;
	mapping(address => uint256) public addressToAmountFunded;
	address[] public funders;
	address public owner;

	constructor() public {
		owner = msg.sender;
	}

	function fund() public payable {
		uint256 minimumUSD = 50 * 10 ** 18;	
		require(getConversionRate(msg.value) >= minimumUSD, "You need to spend 	more ETH!");
		addressToAmountFunded[msg.sender] += msg.value;
		funders.push(msg.sender);
	}

	function getVersion() public view returns (uint256){
		AggregatorV3Interface priceFeed = 		
		AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
		return priceFeed.version();
	}

	function getPrice() public view returns(uint256){
		AggregatorV3Interface priceFeed = AggregatorV3Interface
			(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
		(,int256 answer,,,) = priceFeed.latestRoundData();
		return uint256(answer * 10000000000);
	}

	function getConversionRate(uint256 ethAmount) public view returns (uint256){
		uint256 ethPrice = getPrice();
		uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
		return ethAmountInUsd;
	}

	modifier onlyOwner {	
		require(msg.sender == owner);
		_;
	}

	function withdraw() payable onlyOwner public {
		msg.sender.transfer(address(this).balance);
		for (uint256 funderIndex=0; funderIndex < funders.length; funderIndex++){
			address funder = funders[funderIndex];
			addressToAmountFunded[funder] = 0;
		}
		funders = new address[](0);
	}
}


Deploy.py

4. In the scripts folder of the directory, create a deploy.py file with the following code , so that you can interact with the smart contract:


from brownie import FundMe
from scripts.helpful_scripts import get_account

def deploy_fund_me():
	account = get_account()
	fund_me = FundMe.deploy({"from":account})
	print(f"Contract deployed to {fund_me.address}")

def main():
	deploy_fund_me()


Helpful_scripts.py

5. Create another file named helpful_scripts.py and paste the below codes in it:

from brownie import network, config, accounts

def get_account():
	if network.show_active() == "development":
	return accounts[0]
else:
	return accounts.add(config["wallets"]["from_key"])


This piece of code helps deploy.py find the account it should connect to. In order to make it possible for deploy.py to import this file, you should create another file called __init__.py.

Brownie-config.yaml

6. In the main directory create a file named brownie-config.yaml and paste the below scripts in it.


dependencies:
# - @
	- smartcontractkit/[email protected]
compiler:
	solc:
		remappings:
			- '@chainlink=smartcontractkit/[email protected]'

dotenv: .env
wallets:
	from_key: ${PRIVATE_KEY}


We have defined the chainlink smart contract kit as a dependency to be able to use it afterwards. We have also defined using environment variables and reading from the private key.

.env file

7. Create a .env file and paste your private key and Infura Rinkeby id inside of it.


export PRIVATE_KEY=
export WEB3_INFURA_PROJECT_ID=


8. Now that all files are set up, in your terminal write:

brownie compile

Result:

Brownie v1.18.1 - Python development framework for Ethereum Downloading from https://solc-bin.ethereum.org/linux-amd64/solc-linux-amd64-v0.6.12+commit.27d51765 100%|█████████████████████████████████████████████████████████████████████████████████████| 9.47M/9.47M [00:46<00:00, 204kiB/s] solc 0.6.12 successfully installed at: /home/mohamad/.solcx/solc-v0.6.12 Compiling contracts... Solc version: 0.6.12 Optimizer: Enabled Runs: 200 EVM Version: Istanbul Generating build data... - smartcontractkit/[email protected]/AggregatorV3Interface - smartcontractkit/[email protected]/SafeMathChainlink - FundMe Project has been compiled. Build artifacts saved at /home/mohamad/brownie_fund_me/build/contracts

Deploying the Crowdfunding Smart Contract:

9. The final step is to deploy the smart contract:

brownie run scripts/deploy.py --network rinkeby

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieFundMeProject is the active project. Running 'scripts/deploy.py::main'... Transaction sent: 0xaffb135e4da5e5d52df7fb852194c47459f93614ad6dfdf098890c16c9138d58 Gas price: 1.000000019 gwei Gas limit: 396322 Nonce: 50 FundMe.constructor confirmed Block: 10428096 Gas used: 360293 (90.91%) FundMe deployed at: 0xD13F9dA0C0AFA03B5115F2007863Ab87404fDA5d Contract deployed to 0xD13F9dA0C0AFA03B5115F2007863Ab87404fDA5d

Now, we can go to Rinkeby Etherscan again and track our deployment. In the contract section, we will see:

crowdfunding on Etherscan

So far so good! But we have a problem here. We are not yet able to interact with the smart contract. To do this manually, you can click on verify and publish. On the new page, you enter the contract address, compiler type and its license and press continue.

crowdfunding on Etherscan

We can enter the solidity FundMe.sol contract code in the space given. Also do not forget to toggle optimization from no to yes.

crowdfunding on Etherscan

We can copy and paste our smart contract. But there is a problem, Etherscan will not be able to identify chainlink AggregatorV3Interface.sol. We have a solution for this in the next section of our tutorial.

Crowdfuning on Etherscan: Getting An API Key

In this section, we are going to get an API key from etherscan.io and paste it into the .env file to be able to keep track of our crowdfunding smart contract on the Rinkeby chain Etherscan. Also, we're going to interact with the fund me smart contract with a simple-to-use interface. As you recall, we had a problem and it was that we couldn’t paste our code inside the box related to smart contracts because Etherscan could not identify the chainlink Aggregator. But, there is a solution for that.
First, we should head over to etherscan.io and sign up for an account and then in the More tab Developers section click on API documentation:

crowdfunding on Etherscan

Then, on the left hand side bar, click on API keys:

crowdfunding on Etherscan

And add an API with the name verify_brownie:

crowdfunding on Etherscan

After adding an API key, copy it:

crowdfunding on Etherscan

And paste it into the .env file like this:


export ETHERSCAN_TOKEN=

Also, in the deploy.py file, change:


fund_me = FundMe.deploy({"from":account})

To


fund_me = FundMe.deploy({"from":account},publish_source=True)

To be able to publish our code on Rinkeby Etherscan.
Now, it is time to run our code once more:

brownie run scripts/deploy.py --network rinkeby

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieFundMeProject is the active project. Running 'scripts/deploy.py::main'... Transaction sent: 0xd0bced9c51f3c79e3bda4a150600159480c49ab8c7e7fb57b92112b3ee4efc80 Gas price: 1.000000013 gwei Gas limit: 396322 Nonce: 51 FundMe.constructor confirmed Block: 10428922 Gas used: 360293 (90.91%) FundMe deployed at: 0xC9bA69ceb32E132a6cf32138534EB32647D87d7a Waiting for https://api-rinkeby.etherscan.io/api to process contract... Verification submitted successfully. Waiting for result... Verification complete. Result: Pass - Verified Contract deployed to 0xC9bA69ceb32E132a6cf32138534EB32647D87d7a

Interacting on Rinkeby Etherscan:

Now, if we go to Rinkeby Etherscan and paste the address of the contract (given in the terminal) in the search bar, we will be able to see that the contract tab is checked:

smart contract

If you go to the contracts section, you will be able to see that the FundMe.sol is published with its chainlink dependency code. Also, ABI of the contract is published in another box.

smart contract

If we try posting the contract and press any of the keys related to public data, we will be able to retrieve them.

smart contract

Also, if we go to write the contract and click on connect to web3, we can connect our Metamask wallet to Etherscan and fund the project using the test ethers in the Rinkeby account.

smart contract

smart contract

You can enter 1 in the box under fund and press the Write button and confirm the Metamask pop-up:

smart contract

Once, the transaction has been confirmed, we can withdraw that 1 ether back into our account:

smart contract

By doing the exact same process for the Mainnet account, we will be able to write and deploy a real-world smart contract!

Conclusion

In this article, we have managed to create and organize the crowdfunding project folder and compile it. Then, we started the interaction with the compiled and deployed the crowdfunding contract using the Etherscan. There are a number of scripts that we have worked on such as helpful_scripts.py, deploy.py, and brownie_config.yaml, Fundme.sol and .env file.
Finally, we have managed to completely interact with the Fundme.sol smart contract using the address we have got in the terminal at the time we de-ployed the Fundme.sol contract. In this interaction which is on the Rinkeby chain (Ethereum test network), we can fund the contract and withdraw the funds as the admin.

Download this Article in PDF format

metaverse

Care to Know About Metaverse?

In Arashtad, we are providing custom services on 3d developments such as 3d websites, 3d models, metaverses and all 3d applications.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development

Crowdfunding Smart Contract on Mainnet and Other Networks: A Perfect Tutorial

In this tutorial, we are going to write the complete scripts for the crowdfunding smart contract and organize the files related to the deployment of the smart contract in their own folders. We are also going to create a .env file to keep the private key and Infura project ID somewhere safe. In addition to that, the config file is going to help us categorize different networks with their account addresses.

Crowdfunding Smart Contract: Where to Start?

To start writing scripts for crowdfunding smart contracts, we are going to modify the code in a way that it will be possible to work with different networks such as Ganache CLI, development, Rinkeby, Mainnet, and so on. The folders and files remain the same and we only modify some of the scripts and add some other files to the project. Follow the steps and read the explanations to understand why we do what we do:

1. Inside the contracts folder, create another folder and name it test. And inside of it create a file called, MockV3Aggregator.sol then copy and paste this link URL into it:

The reason we do this, is to be able to use it when we are working with Ganache CLI and other networks as well.

2. We need to also modify FundMe.sol just a bit to be able to enter the eth_to_usd address for all the networks:


// SPDX-License-Identifier: MIT

pragma solidity ^ 0.6.6;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
import "@chainlink/contracts/src/v0.6/vendor/SafeMathChainlink.sol";

contract FundMe {

	using SafeMathChainlink for uint256;
	mapping(address => uint256) public addressToAmountFunded;
	address[] public funders;
	address public owner;
	AggregatorV3Interface public priceFeed;

	constructor(address _priceFeed) public {
		priceFeed = AggregatorV3Interface(_priceFeed);
		owner = msg.sender;
	}

	function fund() public payable {
		uint256 minimumUSD = 50 * 10**18;
		require(getConversionRate(msg.value) >= minimumUSD,"You need to spend more ETH!");
		addressToAmountFunded[msg.sender] += msg.value;
		funders.push(msg.sender);
	}

	function getVersion() public view returns (uint256) {
		return priceFeed.version();
	}

	function getPrice() public view returns (uint256) {
		(, int256 answer, , , ) = priceFeed.latestRoundData();
		return uint256(answer * 10000000000);
	}


	function getConversionRate(uint256 ethAmount) public view returns (uint256) {
		uint256 ethPrice = getPrice();
		uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
		return ethAmountInUsd;
	}

	function getEntranceFee() public view returns (uint256) {
		uint256 minimumUSD = 50 * 10**18;
		uint256 price = getPrice();
		uint256 precision = 1 * 10**18;
		return ((minimumUSD * precision) / price) + 1;
	}

	modifier onlyOwner() {
		require(msg.sender == owner);
		_;
	}

	function withdraw() public payable onlyOwner {
		msg.sender.transfer(address(this).balance);
		for (uint256 funderIndex = 0;funderIndex < funders.length;funderIndex++){
			address funder = funders[funderIndex];
			addressToAmountFunded[funder] = 0;
		}
		funders = new address[](0);
	}
}


Notice that in the above contract, we have defined AggregatorV3Interface public priceFeed and also a constructor for it as priceFeed = AggregatorV3Interface(_priceFeed). And later in the get price instead of Rinkeby eth_to_usd address code we have used this priceFeed variable to fetch ETH price.

3. Also, in the brownie-config.yaml, we enter this networks declaration:


networks:
	default: development
	rinkeby:
		eth_usd_price_feed: '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e'
		verify: True
	mainnet-fork-dev:
		eth_usd_price_feed: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419'
		verify: False
	development:
		verify: False
	ganache-local:
		verify: False


Here, we define all kinds of price feeds related to Mainnet and Rinkeby. Also, define development and ganache-local networks. Next to every network name, we define whether it is going to be verified for publishing or not.

4. In the helpful_scripts.py, we write:


from brownie import network, config, accounts, MockV3Aggregator
from web3 import Web3

FORKED_LOCAL_ENVIRONMENTS = ["mainnet-fork", "mainnet-fork-dev"]
LOCAL_BLOCKCHAIN_ENVIRONMENTS = ["development", "ganache-local"]

DECIMALS = 8
STARTING_PRICE = 200000000000

def get_account():
	if (network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS or network.show_active() in 																		 
        		FORKED_LOCAL_ENVIRONMENTS):
		return accounts[0]
	else:
		return accounts.add(config["wallets"]["from_key"])

def deploy_mocks():
	print(f"The active network is {network.show_active()}")
	print("Deploying Mocks...")
	if len(MockV3Aggregator) <= 0:
		MockV3Aggregator.deploy(DECIMALS, STARTING_PRICE, {"from": get_account()})
		print("Mocks Deployed!")


In this helpful_scripts.py file, we have defined all networks and helped the deploy.py choose the proper account according to the network that is currently active. We have also defined another function for the deploy_mocks.py file, which is going to be explained later.

4. Inside the scripts folder, create a file named deploy_mocks.py and paste the below scripts in it. Use this script if you want to deploy mocks to a Testnet:


from brownie import ( MockV3Aggregator, network,)
from scripts.helpful_scripts import (get_account,)

DECIMALS = 8
INITIAL_VALUE = 200000000000

def deploy_mocks():

	print(f"The active network is {network.show_active()}")
	print("Deploying Mocks...")
	account = get_account()
	MockV3Aggregator.deploy(DECIMALS, INITIAL_VALUE, {"from": account})
	print("Mocks Deployed!")

def main():
	deploy_mocks()


5. Inside the scripts folder create a new file called, fund_and_withdraw.py and paste these scripts in it:


from brownie import FundMe
from scripts.helpful_scripts import get_account

def fund():
	fund_me = FundMe[-1]
	account = get_account()
	entrance_fee = fund_me.getEntranceFee()
	print(entrance_fee)
	print(f"The current entry fee is {entrance_fee}")
	print("Funding")
	fund_me.fund({"from": account, "value": entrance_fee})

def withdraw():
	fund_me = FundMe[-1]
	account = get_account()
	fund_me.withdraw({"from": account})

def main():
	fund()
	withdraw()


Using the above script, we can interact with our own contract without the need to use the Etherscan interface for our contract. We can fund the contract and withdraw from it.

6. And in the end, it is finally the time to modify deploy.py:


from brownie import FundMe, MockV3Aggregator, network, config
from scripts.helpful_scripts import (
	get_account,
	deploy_mocks,
	LOCAL_BLOCKCHAIN_ENVIRONMENTS,
	)

def deploy_fund_me():
	account = get_account()
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		price_feed_address = config["networks"][network.show_active()]["eth_usd_price_feed"]
	else:
		deploy_mocks()
		price_feed_address = MockV3Aggregator[-1].address

	fund_me = FundMe.deploy( price_feed_address,{"from": account},publish_source=config["networks"]
		[network.show_active()].get("verify"),)
	print(f"Contract deployed to {fund_me.address}")
	return fund_me

def main():
	deploy_fund_me()


The above deploy.py script which has been integrated to work with different networks, first checks which network we are in. Then, it fetches the price feed. And then, it deploys the contract using the price feed address, an account that is chosen according to the active network, and the verification state-related whether we want to publish the contract or not that has been declared in brownie-config.yaml. And then we finally deploy the contract. In the next part of this tutorial, we are going to run this project and see if it works properly.

Python web3 tools

Need More Python Web3 Understanding?

If you're having hard time understanding what's going on in this article, you probably missed our article explaining how to deploy a smart contract using Python web3 tools. By reading it, this article will become much easier.

Arashtad Serivces
We can help you on 3d applications and blockchain developement
Let Us Know What You Need
Blockchain Development

Crowdfunding Smart Contract: Testing the Scripts

In this article, we are going to write the test scripts related to the deployment of the Fundme.sol smart contract we wrote earlier in the previous article. This testing process is going to be applied on the Ganache simulated blockchain which is local. Ganache is the best network that can be used for testing.
For running and testing our deployment, we need to create a test_fund_me.py file inside the test folder and paste the following code in it:


from scripts.helpful_scripts import get_account, LOCAL_BLOCKCHAIN_ENVIRONMENTS
from scripts.deploy import deploy_fund_me
from brownie import network, accounts, exceptions
import pytest

def test_can_fund_and_withdraw():
	account = get_account()
	fund_me = deploy_fund_me()
	entrance_fee = fund_me.getEntranceFee() + 100
	tx = fund_me.fund({"from": account, "value": entrance_fee})
	tx.wait(1)
	assert fund_me.addressToAmountFunded(account.address) == entrance_fee
	tx2 = fund_me.withdraw({"from": account})
	tx2.wait(1)
	assert fund_me.addressToAmountFunded(account.address) == 0

	def test_only_owner_can_withdraw():
	if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
		pytest.skip("only for local testing")
		fund_me = deploy_fund_me()
		bad_actor = accounts.add()
		with pytest.raises(exceptions.VirtualMachineError):
			fund_me.withdraw({"from": bad_actor})



Also, do not forget to install pytest.by:

pip install pytest

Now, if we want to compile our project, in the console we write:

brownie compile

The result should be something like this:

Brownie v1.18.1 - Python development framework for Ethereum Compiling contracts... Solc version: 0.6.12 Optimizer: Enabled Runs: 200 EVM Version: Istanbul Generating build data... - smartcontractkit/[email protected]/AggregatorInterface - smartcontractkit/[email protected]/AggregatorV2V3Interface - smartcontractkit/[email protected]/AggregatorV3Interface - MockV3Aggregator Project has been compiled. Build artifacts saved at/home/mohamad/brownie_fund_me/build/contracts

And now, to discover all of the brownie networks lists, we type:

brownie networks list

Result:

Brownie v1.18.1 - Python development framework for Ethereum The following networks are declared: Ethereum ├─Mainnet (Infura): mainnet ├─Ropsten (Infura): ropsten ├─Rinkeby (Infura): rinkeby ├─Goerli (Infura): goerli └─Kovan (Infura): kovan Ethereum Classic ├─Mainnet: etc └─Kotti: kotti Arbitrum └─Mainnet: arbitrum-main Avalanche ├─Mainnet: avax-main └─Testnet: avax-test Aurora ├─Mainnet: aurora-main └─Testnet: aurora-test Binance Smart Chain ├─Testnet: bsc-test └─Mainnet: bsc-main Fantom Opera ├─Testnet: ftm-test └─Mainnet: ftm-main Harmony └─Mainnet (Shard 0): harmony-main Moonbeam └─Mainnet: moonbeam-main Optimistic Ethereum ├─Mainnet: optimism-main └─Kovan: optimism-test Polygon ├─Mainnet (Infura): polygon-main └─Mumbai Testnet (Infura): polygon-test XDai ├─Mainnet: xdai-main └─Testnet: xdai-test Development ├─Ganache-CLI: development ├─Geth Dev: geth-dev ├─Hardhat: hardhat ├─Hardhat (Mainnet Fork): hardhat-fork ├─Ganache-CLI (Mainnet Fork): mainnet-fork ├─Ganache-CLI (BSC-Mainnet Fork): bsc-main-fork ├─Ganache-CLI (FTM-Mainnet Fork): ftm-main-fork ├─Ganache-CLI (Polygon-Mainnet Fork): polygon-main-fork ├─Ganache-CLI (XDai-Mainnet Fork): xdai-main-fork ├─Ganache-CLI (Avax-Mainnet Fork): avax-main-fork └─Ganache-CLI (Aurora-Mainnet Fork): aurora-main-fork

Now, we should add Ganache to Ethereum networks, to be able to keep track of our transactions inside the deployments folder. To do that, according to our host address and chainid, we write:

brownie networks add Ethereum ganache-local host=http://127.0.0.1:7545 chainid=5777

Result:

Brownie v1.18.1 - Python development framework for Ethereum SUCCESS: A new network 'ganache-local' has been added └─ganache-local ├─id: ganache-local ├─chainid: 5777 └─host: http://127.0.0.1:7545

And it has successfully been added. Now to make sure, again we write in the console:

brownie networks list

Result:

Brownie v1.18.1 - Python development framework for Ethereum The following networks are declared: Ethereum ├─Mainnet (Infura): mainnet ├─Ropsten (Infura): ropsten ├─Rinkeby (Infura): rinkeby ├─Goerli (Infura): goerli ├─Kovan (Infura): kovan └─ganache-local: ganache-local ………………………..

We can see that ganache-local has been added to the Ethereum networks list.
Now, in order to run our deploy.py on ganache-local:

brownie run scripts/deploy.py --network ganache-local result: Brownie v1.18.1 - Python development framework for Ethereum Compiling contracts... Solc version: 0.6.12 Optimizer: Enabled Runs: 200 EVM Version: Istanbul Generating build data... - smartcontractkit/[email protected]/AggregatorV3Interface - smartcontractkit/[email protected]/SafeMathChainlink - FundMe BrownieFundMeProject is the active project. Running 'scripts/deploy.py::main'... The active network is ganache-local Deploying Mocks... Mocks Deployed! Transaction sent: 0xd9abd015320007e15ba8ab3e33b64e685986e0b277a0d5f9bab41cf8449f72ee Gas price: 20.0 gwei Gas limit: 446315 Nonce: 1 FundMe.constructor confirmed Block: 2 Gas used: 405741 (90.91%) FundMe deployed at: 0xEd48d45970A4CB5541fffCf3a5ed5cB061435E1A Contract deployed to 0xEd48d45970A4CB5541fffCf3a5ed5cB061435E1A

And we can track the transaction on Ganache.

crowdfunding smart contract

Also, see the records on build/deployments folder (Notice the 5777 as the chain id and the corresponding .json files).

crowdfunding smart contract

The rest of our code execution will be continued in the next part.

Crowdfunding Smart Contract: Running A Specific Network

In this tutorial, we try to connect to Mainnet-fork dev to complete the list of networks that we can connect to. We also try to execute the fund and withdraw transactions and put it to the test. If we want to use a development network other than the persistent one, we can copy the HTTP address from alchemy.io and the Ganache CLI to be able to connect to a development network other than the Infura host node.

We continue our crowdfunding deployment execution here.
The other networks can be run with:

brownie run scripts/deploy.py --network

Also, as declared in brownie-config.yaml, the default network is development which is Ganache, so by running:

brownie run scripts/deploy.py

We will get:

Brownie v1.18.1 - Python development framework for Ethereum BrownieFundMeProject is the active project. Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... Running 'scripts/deploy.py::main'... The active network is development Deploying Mocks... Transaction sent: 0xb7fcc22911055aa0e06bb329d0b366065e4c01e37827c19975048cb8324cb581 Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0 MockV3Aggregator.constructor confirmed Block: 1 Gas used: 430659 (3.59%) MockV3Aggregator deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87 Mocks Deployed! Transaction sent: 0x32cc5492fa66ebb172a52a3a8c83ae6ed230a28738034bd62ce52de3acb71cae Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 1 FundMe.constructor confirmed Block: 2 Gas used: 405741 (3.38%) FundMe deployed at: 0x602C71e4DAC47a042Ee7f46E0aee17F94A3bA0B6 Contract deployed to 0x602C71e4DAC47a042Ee7f46E0aee17F94A3bA0B6 Terminating local RPC client…

Executing fund and withdraw

Now, it is time to interact with our contract by funding and withdrawing using the scripts/fund_and_withdraw.py, So to do that, in the terminal, we type:

brownie run scripts/fund_and_withdraw.py --network ganache-local

Result:

Brownie v1.18.1 - Python development framework for Ethereum BrownieFundMeProject is the active project. Running 'scripts/fund_and_withdraw.py::main'... 25000000000000001 The current entry fee is 25000000000000001 Funding Transaction sent: 0x6372f7d40f64544b802675b25e8a7c0f6ae70ee6fc56868600bc2941812c0eb2 Gas price: 20.0 gwei Gas limit: 99258 Nonce: 2 FundMe.fund confirmed Block: 3 Gas used: 90235 (90.91%) Transaction sent: 0xf5a0409e0a852cc9f3c4965619d6183013321ca6010f96537172b51159672ba3 Gas price: 20.0 gwei Gas limit: 76963 Nonce: 3 FundMe.withdraw confirmed Block: 4 Gas used: 24967 (32.44%)

And we can see that both fund and withdraw transactions have been successfully executed. If we look at the Ganache transactions, we will be able to see the track of the transactions:

crowdfunding smart contract

It is now time to test the project by running the following command in the terminal:

brownie test

Result:

Brownie v1.18.1 - Python development framework for Ethereum ============================================= test session starts ============================================== platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: /home/mohamad/brownie_fund_me plugins: eth-brownie-1.18.1, web3-5.27.0, xdist-1.34.0, forked-1.4.0, hypothesis-6.27.3 collected 2 items Launching 'ganache-cli --chain.vmErrorsOnRPCResponse true --wallet.totalAccounts 10 --hardfork istanbul --miner.blockGasLimit 12000000 --wallet.mnemonic brownie --server.port 8545'... tests/test_fund_me.py .. [100%] ============================================== 2 passed in 2.59s =============================================== Terminating local RPC client...

And we see that the tests are passed, which shows both funds and withdraw functions work correctly and also only the owner can withdraw.

Adding a Development Network

Now, it is time to add a development network instead of a persistent network and we use a host node other than Infura. To do that, we should head over to the alchemy.io link and sign up or log in.

Arashtad smart contract

There are some steps that you need to take. And after you enter your profile, press the create app button:

Arashtad smart contract

Then, create a Mainnnet development Ethereum app (enter any name you like in the 2 boxes).

Arashtad smart contract

Then, enter the app and click on the view key on top of the page.

Arashtad smart contract

Copy the HTTP address and paste it into the fork section of the below command:

brownie networks add development mainnet-fork-dev cmd=ganache-cli host=http://127.0.0.1 fork=https://eth-mainnet.alchemyapi.io/v2/pGl-uSqPKVYCu4IpI1bjpK6dzxmLImD4 accounts=10 mnemonic=brownie port=8545

After you hit enter, the result will be:

Brownie v1.18.1 - Python development framework for Ethereum SUCCESS: A new network 'mainnet-fork-dev' has been added └─mainnet-fork-dev ├─id: mainnet-fork-dev ├─cmd: ganache-cli ├─cmd_settings: {'fork': 'https://eth-mainnet.alchemyapi.io/v2/pGl-uSqPKVYCu4IpI1bjpK6dzxmLImD4', 'accounts': 10, 'mnemonic': 'brownie', 'port': 8545} └─host: http://127.0.0.1

And there we go. We have successfully added the Mainnet fork dev network. Congratulations! You have finally completed the crowdfunding project that can be run on different networks.

Conclusion:

In this tutorial, we have worked on the different scripts that we need to set up the crowdfunding project using Brownie. If you have read our article on writing the Fundme.sol smart contract in Remix IDE, you must be familiar with how the structure of the smart contract. Here, to make scripts more organized, we have added other python files such as helpful_scripts.py to manage the accounts from different networks, deploy_mocks.py, fund_and_withdraw.py, and also brownie_config.yaml to categorize the networks and the necessary data related to them.
In this article, we have written the test scripts necessary to test the deployment of the smart contract. For the testing, we have used the Ganache simulated blockchain which is local and thus the best option for applying different tests in the smart contracts.
In this article, we have managed to connect to Mainnet fork dev to complete the list of networks that we can connect to. We also have also tried to execute the fund and withdraw transactions and put it to the test. If we want to use a development network other than the persistent one, we can copy the HTTP address from alchemy.io and the Ganache CLI to be able to connect to a development network other than the Infura host node.

Download this Article in PDF format

metaverse

We Are Working on Metaverses

Metaverses, 3d modeling, 3d application, 3d websites, and even animations are just some of our vase expertise.

Arashtad Serivces
Tell us about your ideas
Fill in the Form
Blockchain Development

Introduction to Brownie-mix: A Boilerplate for Important Smart Contract Projects

In this article, we are going to get familiar with Brownie-mix as a boilerplate for blockchain projects in python. Using this boilerplate with the Brownie bake command will help you have so many of the dependency contracts provided for that specific project. There is also a complete brownie_config.yaml file provided with a complete list of networks. Some ready python files such as helpful_scripts.py and deploy_mocks.py are provided for you.

Introduction to the Brownie-mix

When we were examining how to deploy a smart contract using python web3 tools among our previous articles, we showed how to use Brownie. It is highly recommended that before you start this tutorial, be familiar with web3 python tools and also solidity language.
So far, we have coded most of our smart contract deployments using Brownie by starting like this in the terminal:

brownie init

And then some folders would have been created and then the rest of the project. But the hard part was that we needed to copy a lot of dependencies, such as the VRFConsumer.sol and other smart contracts, brownie-config.yaml file, helpful_scripts.py, deploy_mocks.py, and a lot of other useful scripts that we need to rewrite every time we created the project.
A Brownie-mix helps us cover this hard task and provides easy boilerplates for every type of project like DAO, NFTs, Token, ChainLink, and so on.

Brownie-mix

To Use Brownie-mix: Starting the Brownie Template

In order to use Brownie-mix boilerplates, we should first write the following command in the terminal:

brownie bake

In our case, for example, we want chainlink-mix (using which we have managed to write and deploy our contracts, so far).

brownie bake chainlink-mix
cd chainlink

And you will see that all the folders and files alongside all the necessary routine codes are provided for you in your directory called chainlink.

Brownie-mix

Modifying brownie_config.yaml

Notice that in the brownie-config.yaml, you should modify some parts:


# exclude SafeMath when calculating test coverage
# https://eth-brownie.readthedocs.io/en/v1.10.3/config.html#exclude_paths
reports:
	exclude_contracts:
		- SafeMath
dependencies:
	- smartcontractkit/[email protected]
	- OpenZeppelin/[email protected]
compiler:
	solc:
		remappings:
			- "@chainlink=smartcontractkit/[email protected]"
			- "@openzeppelin=OpenZeppelin/[email protected]"
# automatically fetch contract sources from Etherscan
autofetch_sources: True
# Uncomment to use the .env file
# dotenv: .env
# set a custom mnemonic for the development network
networks:
   default: development
   development:
		keyhash:"0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4"
		fee: 100000000000000000
		jobId: "29fa9aa13bf1468788b7cc4a500a45b8"
		update_interval: 60
		verify: False
   kovan:
	vrf_coordinator: "0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9"
	link_token: "0xa36085F69e2889c224210F603D836748e7dC0088"
	keyhash:"0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4"
	fee: 100000000000000000
	oracle: "0xc57b33452b4f7bb189bb5afae9cc4aba1f7a4fd8"
	jobId: "d5270d1c311941d0b08bead21fea7747"
	eth_usd_price_feed: "0x9326BFA02ADD2366b30bacB125260Af641031331"
	# Change to True if you have an Etherscan API key and want to verify
	verify: True
	update_interval: 60
   ganache:
	keyhash:"0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4"
	fee: 100000000000000000
	jobId: "29fa9aa13bf1468788b7cc4a500a45b8"
	update_interval: 60
	verify: False
	rinkeby:
	vrf_coordinator: "0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B"
	link_token: "0x01be23585060835e02b77ef475b0cc51aa1e0709"
	keyhash:"0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311"
	fee: 100000000000000000
	oracle: "0xc57b33452b4f7bb189bb5afae9cc4aba1f7a4fd8"
	jobId: "6b88e0402e5d415eb946e528b8e0c7ba"
	eth_usd_price_feed: "0x8A753747A1Fa494EC906cE90E9f37563A8AF630e"
	# Change to True if you have an Etherscan API key and want to verify
	verify: False
   fuji:
	link_token: "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846"
	fee: 100000000000000000
	oracle: "0xcc80934eaf22b2c8dbf7a69e8e0d356a7cac5754"
	jobId: "5ca4fa9b2d64462290abfbda84e38cf4"
   mumbai:
	eth_usd_price_feed: "0x0715A7794a1dc8e42615F059dD6e406A6594651A"
	link_token: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB"
	vrf_coordinator: "0x8C7382F9D8f56b33781fE506E897a4F1e2d17255"
	keyhash:"0x6e75b569a01ef56d18cab6a8e71e6600d6ce853834d4a5748b720d06f878b3a4"
	fee: 1000000000000000000
   binance:
	# link_token: ??
	eth_usd_price_feed: "0x9ef1B8c0E4F7dc8bF5719Ea496883DC6401d5b2e"
   binance-fork:
	eth_usd_price_feed: "0x9ef1B8c0E4F7dc8bF5719Ea496883DC6401d5b2e"
   mainnet-fork:
	eth_usd_price_feed: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419"
   matic-fork:
	eth_usd_price_feed: "0xF9680D99D6C9589e2a93a78A04A279e509205945"
wallets:
	from_key: ${PRIVATE_KEY}
	from_mnemonic: ${MNEMONIC}



Notice that if you use mnemonic, you should use accounts.from_mnemonic to be able to use from_mnemonic and if you use the private key, you should use accounts.add instead.
Also, make sure to uncomment the dotenv: .env if you want to keep your private data somewhere safe.
By using the above .yaml file, you can use any networks that you want and be sure that there is nothing else needed to add to this file. In the contracts folder, you will also be able to see some useful contracts that work as a dependency. The most important folder is the scripts, inside which we have helpful_scripts.py, deploy_mocks.py, and some useful scripts that will help using them in your main deploy.py file.

Brownie-mix

As you can see, some of the important and useful functions of useful_scripts.py are as follows:


get_account

get_contract

fund_with_link

deploy_mocks


Using the above functions, you can write your test files and deploy.py file much easier.

Final Word on Brownie-mix

In this article, we have introduced Brownie-mixes as a boilerplate (template) for blockchain projects and provided guides on how to quickly modify them so that you can run your desired project instantly. These projects could be creating an ERC-20 token, NFT, Aave protocol, Chainlink, and so on.

Download this Article in PDF format

metaverse

Care to Know About Metaverse?

In Arashtad, we are providing custom services on 3d developments such as 3d websites, 3d models, metaverses and all 3d applications.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Tell Us What You Need
Blockchain Development