Part 3: Installing a Package¶
Introduction¶
In this tutorial we will be creating our own mintable ERC20 token. However, instead of writing our own ERC20 implementation we’ll be taking advantage of an existing implementation through the use of populus’s package management features.
Setting up the project folder¶
Create a new directory for your project and run $ populus project init
to
populate the initial project structure.
$ populus init
Wrote default populus configuration to `./populus.json`.<Paste>
Created Directory: ./contracts
Created Example Contract: ./contracts/Greeter.sol
Created Directory: ./tests
Created Example Tests: ./tests/test_greeter.py
Now, delete the ./contracts/Greeter.sol
and ./tests/test_greeter.py
files as we won’t be using the Greeter contracts in this tutorial.
Once you’ve removed those files create a new solidity source file
./contracts/MintableToken.sol
and paste in the following solidity source
code.
pragma solidity ^0.4.0;
import {owned} from "example-package-owned/contracts/owned.sol";
import {StandardToken} from "example-package-standard-token/contracts/StandardToken.sol";
contract MintableToken is StandardToken(0), owned {
function mint(address who, uint value) public onlyowner returns (bool) {
balances[who] += value;
totalSupply += value;
Transfer(0x0, who, value);
return true;
}
}
If you are familiar with solidity, the two import statements should stand out to you. These two statements will currently cause an error during compilation. Let’s see.
$ populus compile
============ Compiling ==============
> Loading source files from: ./contracts
Traceback (most recent call last):
...
> command: `solc --optimize --combined-json bin,bin-runtime,abi,devdoc,userdoc contracts/MintableToken.sol`
> return code: `1`
> stderr:
> stdout:
contracts/MintableToken.sol:3:1: Error: Source "example-package-owned/contracts/owned.sol" not found: File not found.
import {owned} from "example-package-owned/contracts/owned.sol";
^--------------------------------------------------------------^
contracts/MintableToken.sol:4:1: Error: Source "example-package-standard-token/contracts/StandardToken.sol" not found: File not found.
import {StandardToken} from "example-package-standard-token/contracts/StandardToken.sol";
^---------------------------------------------------------------------------------------^
The solidity compiler clearly gets angry that we’re trying to import files that
don’t exist. In order to install these file and make solidity happy we’ll
first need to generate a package manifest using the $ populus package init
command.
You will be presented with an interactive prompt to populus various pieces of
project information. There will now be a new file in the root of your project
named ethpm.json
that should look something like this.
{
"authors": [
"Piper Merriam <pipermerriam@gmail.com>"
],
"description": "Mintable ERC20 token contract",
"keywords": [
"ERC20",
"tokens"
],
"license": "MIT",
"links": {},
"manifest_version": "1",
"package_name": "mintable-standard-token",
"version": "1.0.0"
}
Now we are ready to install some dependencies using the $ populus package
install
command. We want to install both the example-package-owned
and
example-package-standard-token
packages.
$ populus package install example-package-owned example-package-standard-token
Installed Packages: owned, standard-token
If you look in your project directory you should also see a new folder
./installed_packages
.
$ tree .
.
├── contracts
│ └── MintableToken.sol
├── ethpm.json
├── installed_packages
│ ├── example-package-owned
│ │ ├── build_identifier.lock
│ │ ├── contracts
│ │ │ └── owned.sol
│ │ ├── install_identifier.lock
│ │ ├── installed_packages
│ │ └── lock.json
│ └── example-package-standard-token
│ ├── build_identifier.lock
│ ├── contracts
│ │ ├── AbstractToken.sol
│ │ └── StandardToken.sol
│ ├── install_identifier.lock
│ ├── installed_packages
│ └── lock.json
├── populus.json
└── tests
9 directories, 12 files
And if you look in your ethpm.json
file you should see two dependencies.
{
"authors": [
"Piper Merriam <pipermerriam@gmail.com>"
],
"dependencies": {
"example-package-owned": "1.0.0",
"example-package-standard-token": "1.0.0"
},
"description": "Mintable ERC20 token contract",
"keywords": [
"ERC20",
"tokens"
],
"license": "MIT",
"links": {},
"manifest_version": "1",
"package_name": "mintable-token",
"version": "1.0.0"
}
Now, we can try to compile our project again and everything should work.
$ populus compile
============ Compiling ==============
> Loading source files from: ./contracts
> Found 1 contract source files
- contracts/MintableToken.sol
> Compiled 4 contracts
- MintableToken
- StandardToken
- Token
- owned
> Wrote compiled assets to: ./build/contracts.json/contracts.json
Lets go ahead and write a quick test for our new minting functionality. Add
the following test code to a new file ./tests/test_token_minting.py
import pytest
def test_minting_tokens(chain, accounts):
provider = chain.provider
mintable_token, deploy_txn_hash = provider.get_or_deploy_contract(
'MintableToken',
deploy_kwargs={"_totalSupply": 0},
)
assert mintable_token.call().balanceOf(accounts[0]) == 0
assert mintable_token.call().balanceOf(accounts[1]) == 0
assert mintable_token.call().totalSupply() == 0
chain.wait.for_receipt(mintable_token.transact().mint(
who=accounts[0],
value=12345,
))
chain.wait.for_receipt(mintable_token.transact().mint(
who=accounts[1],
value=54321,
))
assert mintable_token.call().balanceOf(accounts[0]) == 12345
assert mintable_token.call().balanceOf(accounts[1]) == 54321
assert mintable_token.call().totalSupply() == 66666
def test_only_owner_can_mint(chain, accounts):
provider = chain.provider
mintable_token, deploy_txn_hash = provider.get_or_deploy_contract(
'MintableToken',
deploy_kwargs={"_totalSupply": 0},
)
with pytest.raises(Exception):
mintable_token.transact({'from': accounts[1]}).mint(
who=accounts[0],
value=12345,
)
And you can the tests with the py.test
command.
$ py.test tests/
========================= test session starts ========================
platform darwin -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0
rootdir: /Users/piper/sites/scratch/populus-tutorial-3, inifile:
plugins: populus-1.5.0
collected 2 items
tests/test_token_minting.py ..
======================= 2 passed in 0.74 seconds =====================
Fin.