create-token.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2021 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. import sys,os,json,re
  19. from subprocess import run,PIPE
  20. from mmgen.common import *
  21. from mmgen.obj import CoinAddr,is_coin_addr
  22. decimals = 18
  23. supply = 10**26
  24. name = 'MMGen Token'
  25. symbol = 'MMT'
  26. solc_version_pat = r'0.5.[123]'
  27. opts_data = {
  28. 'text': {
  29. 'desc': 'Create an ERC20 token contract',
  30. 'usage':'[opts] <owner address>',
  31. 'options': """
  32. -h, --help Print this help message
  33. -o, --outdir= d Specify output directory for *.bin files
  34. -d, --decimals=d Number of decimals for the token (default: {d})
  35. -n, --name=n Token name (default: {n})
  36. -t, --supply= t Total supply of the token (default: {t})
  37. -s, --symbol= s Token symbol (default: {s})
  38. -S, --stdout Output data in JSON format to stdout instead of files
  39. -v, --verbose Produce more verbose output
  40. """
  41. },
  42. 'code': {
  43. 'options': lambda s: s.format(
  44. d=decimals,
  45. n=name,
  46. s=symbol,
  47. t=supply)
  48. }
  49. }
  50. cmd_args = opts.init(opts_data)
  51. from mmgen.protocol import init_proto_from_opts
  52. proto = init_proto_from_opts()
  53. assert proto.coin in ('ETH','ETC'),'--coin option must be set to ETH or ETC'
  54. if not len(cmd_args) == 1 or not is_coin_addr(proto,cmd_args[0].lower()):
  55. opts.usage()
  56. owner_addr = '0x' + cmd_args[0]
  57. # ERC Token Standard #20 Interface
  58. # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
  59. code_in = """
  60. pragma solidity >0.5.0 <0.5.4;
  61. contract SafeMath {
  62. function safeAdd(uint a, uint b) public pure returns (uint c) {
  63. c = a + b;
  64. require(c >= a);
  65. }
  66. function safeSub(uint a, uint b) public pure returns (uint c) {
  67. require(b <= a);
  68. c = a - b;
  69. }
  70. function safeMul(uint a, uint b) public pure returns (uint c) {
  71. c = a * b;
  72. require(a == 0 || c / a == b);
  73. }
  74. function safeDiv(uint a, uint b) public pure returns (uint c) {
  75. require(b > 0);
  76. c = a / b;
  77. }
  78. }
  79. contract ERC20Interface {
  80. function totalSupply() public returns (uint);
  81. function balanceOf(address tokenOwner) public returns (uint balance);
  82. function allowance(address tokenOwner, address spender) public returns (uint remaining);
  83. function transfer(address to, uint tokens) public returns (bool success);
  84. function approve(address spender, uint tokens) public returns (bool success);
  85. function transferFrom(address from, address to, uint tokens) public returns (bool success);
  86. event Transfer(address indexed from, address indexed to, uint tokens);
  87. event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
  88. }
  89. contract Owned {
  90. address public owner;
  91. address public newOwner;
  92. event OwnershipTransferred(address indexed _from, address indexed _to);
  93. constructor() public {
  94. owner = msg.sender;
  95. }
  96. modifier onlyOwner {
  97. require(msg.sender == owner);
  98. _;
  99. }
  100. function transferOwnership(address _newOwner) public onlyOwner {
  101. newOwner = _newOwner;
  102. }
  103. function acceptOwnership() public {
  104. require(msg.sender == newOwner);
  105. emit OwnershipTransferred(owner, newOwner);
  106. owner = newOwner;
  107. newOwner = address(0);
  108. }
  109. }
  110. // ----------------------------------------------------------------------------
  111. // ERC20 Token, with the addition of symbol, name and decimals and assisted
  112. // token transfers
  113. // ----------------------------------------------------------------------------
  114. contract Token is ERC20Interface, Owned, SafeMath {
  115. string public symbol;
  116. string public name;
  117. uint8 public decimals;
  118. uint public _totalSupply;
  119. mapping(address => uint) balances;
  120. mapping(address => mapping(address => uint)) allowed;
  121. constructor() public {
  122. symbol = "<SYMBOL>";
  123. name = "<NAME>";
  124. decimals = <DECIMALS>;
  125. _totalSupply = <SUPPLY>;
  126. balances[<OWNER_ADDR>] = _totalSupply;
  127. emit Transfer(address(0), <OWNER_ADDR>, _totalSupply);
  128. }
  129. function totalSupply() public returns (uint) {
  130. return _totalSupply - balances[address(0)];
  131. }
  132. function balanceOf(address tokenOwner) public returns (uint balance) {
  133. return balances[tokenOwner];
  134. }
  135. function transfer(address to, uint tokens) public returns (bool success) {
  136. balances[msg.sender] = safeSub(balances[msg.sender], tokens);
  137. balances[to] = safeAdd(balances[to], tokens);
  138. emit Transfer(msg.sender, to, tokens);
  139. return true;
  140. }
  141. function approve(address spender, uint tokens) public returns (bool success) {
  142. allowed[msg.sender][spender] = tokens;
  143. emit Approval(msg.sender, spender, tokens);
  144. return true;
  145. }
  146. function transferFrom(address from, address to, uint tokens) public returns (bool success) {
  147. balances[from] = safeSub(balances[from], tokens);
  148. allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens);
  149. balances[to] = safeAdd(balances[to], tokens);
  150. emit Transfer(from, to, tokens);
  151. return true;
  152. }
  153. function allowance(address tokenOwner, address spender) public returns (uint remaining) {
  154. return allowed[tokenOwner][spender];
  155. }
  156. // Owner can transfer out any accidentally sent ERC20 tokens
  157. function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
  158. return ERC20Interface(tokenAddress).transfer(owner, tokens);
  159. }
  160. }
  161. """
  162. def create_src(code):
  163. for k in ('decimals','supply','name','symbol','owner_addr'):
  164. if hasattr(opt,k) and getattr(opt,k): globals()[k] = getattr(opt,k)
  165. code = code.replace('<{}>'.format(k.upper()),str(globals()[k]))
  166. return code
  167. def check_version():
  168. res = run(['solc','--version'],stdout=PIPE).stdout.decode()
  169. ver = re.search(r'Version:\s*(.*)',res).group(1)
  170. msg(f'Installed solc version: {ver}')
  171. if not re.search(r'{}\b'.format(solc_version_pat),ver):
  172. ydie(1,f'Incorrect Solidity compiler version (need version {solc_version_pat})')
  173. def compile_code(code):
  174. check_version()
  175. cmd = ['solc','--optimize','--bin','--overwrite']
  176. if not opt.stdout:
  177. cmd += ['--output-dir', opt.outdir or '.']
  178. cmd += ['-']
  179. msg(f"Executing: {' '.join(cmd)}")
  180. cp = run(cmd,input=code.encode(),stdout=PIPE,stderr=PIPE)
  181. out = cp.stdout.decode().replace('\r','')
  182. err = cp.stderr.decode().replace('\r','').strip()
  183. if cp.returncode != 0:
  184. rmsg('Solidity compiler produced the following error:')
  185. msg(err)
  186. rdie(2,f'Solidity compiler exited with error (return val: {cp.returncode})')
  187. if err:
  188. ymsg('Solidity compiler produced the following warning:')
  189. msg(err)
  190. if opt.stdout:
  191. o = out.split('\n')
  192. return {k:o[i+2] for k in ('SafeMath','Owned','Token') for i in range(len(o)) if k in o[i]}
  193. else:
  194. vmsg(out)
  195. src = create_src(code_in)
  196. out = compile_code(src)
  197. if opt.stdout:
  198. print(json.dumps(out))
  199. msg('Contract successfully compiled')