mmgen-wallet/scripts/create-token.py
The MMGen Project c3f185e8b0
eliminate use of global vars g.proto, g.coin, g.rpc and others
This patch eliminates nearly all the global variables that changed during the
execution of scripts.  With a few minor exceptions, global vars are now used
only during initialization or reserved for cfg file / cmdline options and other
unchanging values.

The result is a code base that's much more maintainable and extensible and less
error-prone.  The autosigning code, which supports signing of transactions for
multiple protocols and networks, has been greatly simplified.

Doing away with globals required many changes throughout the code base, and
other related (and not so related) changes and cleanups were made along the
way, resulting in an enormous patch.

Additional code changes include:

    - tx.py: complete reorganization of TX classes and use of nesting

    - protocol.py: separation of Regtest and Testnet into distinct subclasses
      with separate address and transaction files and file extensions

    - new module help.py for the help notes, loaded on demand

    - addr.py: rewrite of the address file label parsing code

    - tx.py,tw.py: use of generators to create formatted text

User-visible changes include:

    - importing of addresses for tokens not yet in the user's tracking wallet
      is now performed with the `--token-addr` option instead of `--token`

Testing:

    Testing this patch requires a full run of the test suite as described on the
    Test-Suite wiki page.
2020-05-28 09:53:34 +00:00

226 lines
7.4 KiB
Python
Executable file

#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys,os,json,re
from subprocess import run,PIPE
from mmgen.common import *
from mmgen.obj import CoinAddr,is_coin_addr
decimals = 18
supply = 10**26
name = 'MMGen Token'
symbol = 'MMT'
solc_version_pat = r'0.5.[123]'
opts_data = {
'text': {
'desc': 'Create an ERC20 token contract',
'usage':'[opts] <owner address>',
'options': """
-h, --help Print this help message
-o, --outdir= d Specify output directory for *.bin files
-d, --decimals=d Number of decimals for the token (default: {d})
-n, --name=n Token name (default: {n})
-t, --supply= t Total supply of the token (default: {t})
-s, --symbol= s Token symbol (default: {s})
-S, --stdout Output data in JSON format to stdout instead of files
-v, --verbose Produce more verbose output
"""
},
'code': {
'options': lambda s: s.format(
d=decimals,
n=name,
s=symbol,
t=supply)
}
}
cmd_args = opts.init(opts_data)
from mmgen.protocol import init_proto_from_opts
proto = init_proto_from_opts()
assert proto.coin in ('ETH','ETC'),'--coin option must be set to ETH or ETC'
if not len(cmd_args) == 1 or not is_coin_addr(proto,cmd_args[0].lower()):
opts.usage()
owner_addr = '0x' + cmd_args[0]
# ERC Token Standard #20 Interface
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
code_in = """
pragma solidity >0.5.0 <0.5.4;
contract SafeMath {
function safeAdd(uint a, uint b) public pure returns (uint c) {
c = a + b;
require(c >= a);
}
function safeSub(uint a, uint b) public pure returns (uint c) {
require(b <= a);
c = a - b;
}
function safeMul(uint a, uint b) public pure returns (uint c) {
c = a * b;
require(a == 0 || c / a == b);
}
function safeDiv(uint a, uint b) public pure returns (uint c) {
require(b > 0);
c = a / b;
}
}
contract ERC20Interface {
function totalSupply() public returns (uint);
function balanceOf(address tokenOwner) public returns (uint balance);
function allowance(address tokenOwner, address spender) public returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
contract Owned {
address public owner;
address public newOwner;
event OwnershipTransferred(address indexed _from, address indexed _to);
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address _newOwner) public onlyOwner {
newOwner = _newOwner;
}
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
// ----------------------------------------------------------------------------
// ERC20 Token, with the addition of symbol, name and decimals and assisted
// token transfers
// ----------------------------------------------------------------------------
contract Token is ERC20Interface, Owned, SafeMath {
string public symbol;
string public name;
uint8 public decimals;
uint public _totalSupply;
mapping(address => uint) balances;
mapping(address => mapping(address => uint)) allowed;
constructor() public {
symbol = "<SYMBOL>";
name = "<NAME>";
decimals = <DECIMALS>;
_totalSupply = <SUPPLY>;
balances[<OWNER_ADDR>] = _totalSupply;
emit Transfer(address(0), <OWNER_ADDR>, _totalSupply);
}
function totalSupply() public returns (uint) {
return _totalSupply - balances[address(0)];
}
function balanceOf(address tokenOwner) public returns (uint balance) {
return balances[tokenOwner];
}
function transfer(address to, uint tokens) public returns (bool success) {
balances[msg.sender] = safeSub(balances[msg.sender], tokens);
balances[to] = safeAdd(balances[to], tokens);
emit Transfer(msg.sender, to, tokens);
return true;
}
function approve(address spender, uint tokens) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender, spender, tokens);
return true;
}
function transferFrom(address from, address to, uint tokens) public returns (bool success) {
balances[from] = safeSub(balances[from], tokens);
allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens);
balances[to] = safeAdd(balances[to], tokens);
emit Transfer(from, to, tokens);
return true;
}
function allowance(address tokenOwner, address spender) public returns (uint remaining) {
return allowed[tokenOwner][spender];
}
// Owner can transfer out any accidentally sent ERC20 tokens
function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
return ERC20Interface(tokenAddress).transfer(owner, tokens);
}
}
"""
def create_src(code):
for k in ('decimals','supply','name','symbol','owner_addr'):
if hasattr(opt,k) and getattr(opt,k): globals()[k] = getattr(opt,k)
code = code.replace('<{}>'.format(k.upper()),str(globals()[k]))
return code
def check_version():
res = run(['solc','--version'],stdout=PIPE).stdout.decode()
ver = re.search(r'Version:\s*(.*)',res).group(1)
msg(f'Installed solc version: {ver}')
if not re.search(r'{}\b'.format(solc_version_pat),ver):
ydie(1,f'Incorrect Solidity compiler version (need version {solc_version_pat})')
def compile_code(code):
check_version()
cmd = ['solc','--optimize','--bin','--overwrite']
if not opt.stdout:
cmd += ['--output-dir', opt.outdir or '.']
cmd += ['-']
msg(f"Executing: {' '.join(cmd)}")
cp = run(cmd,input=code.encode(),stdout=PIPE,stderr=PIPE)
out = cp.stdout.decode().replace('\r','')
err = cp.stderr.decode().replace('\r','').strip()
if cp.returncode != 0:
rmsg('Solidity compiler produced the following error:')
msg(err)
rdie(2,f'Solidity compiler exited with error (return val: {cp.returncode})')
if err:
ymsg('Solidity compiler produced the following warning:')
msg(err)
if opt.stdout:
o = out.split('\n')
return {k:o[i+2] for k in ('SafeMath','Owned','Token') for i in range(len(o)) if k in o[i]}
else:
vmsg(out)
src = create_src(code_in)
out = compile_code(src)
if opt.stdout:
print(json.dumps(out))
msg('Contract successfully compiled')