mirror of
https://github.com/chaintool-py/eth-erc20.git
synced 2025-04-22 04:41:02 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5dfd3db2d3 | ||
|
54759c3428 | ||
|
637080c464 | ||
|
2fb7143956 | ||
|
d1e5b84cfb | ||
|
4f2d47778c | ||
|
46e827390e | ||
|
f237bd8d82 | ||
|
e449ea647c | ||
|
13ddb5577a | ||
|
b5d3338f07 | ||
|
0937ecb8cd | ||
|
99e82dd3bf | ||
|
04f9b5868b | ||
|
d7fb3f232c | ||
|
ea7c106fa4 | ||
|
273cf23f21 | ||
|
15b6b4fa46 | ||
|
db30d4aaf4 | ||
|
7ab3cd14f5 | ||
|
fc9fa18e1c | ||
|
d218d93fba |
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@ gmon.out
|
|||||||
*.egg-info
|
*.egg-info
|
||||||
.venv/
|
.venv/
|
||||||
.idea
|
.idea
|
||||||
|
solidity/*.json
|
||||||
|
solidity/*.bin
|
||||||
|
@ -1,3 +1,33 @@
|
|||||||
|
* 0.7.5
|
||||||
|
- Factor out token publish method in unittest fixture
|
||||||
|
* 0.7.4
|
||||||
|
- Add missing unittest module to python package
|
||||||
|
* 0.7.3
|
||||||
|
- Add ERC5679Ext20 fills
|
||||||
|
* 0.7.2
|
||||||
|
- Update python classifiers
|
||||||
|
- Upgrade to beta
|
||||||
|
* 0.7.1
|
||||||
|
- Upgrade deps
|
||||||
|
* 0.7.0
|
||||||
|
- Implement proper burner interface method
|
||||||
|
* 0.6.2
|
||||||
|
- Add Giftable Token contract metadata
|
||||||
|
* 0.6.1
|
||||||
|
- Add explicit burn (reduces supply) to giftable token
|
||||||
|
* 0.6.0
|
||||||
|
- Add token expiry to giftable token
|
||||||
|
* 0.5.6
|
||||||
|
- Remove name and symbol lookup
|
||||||
|
- Remove decimals lookup for raw output
|
||||||
|
* 0.5.5
|
||||||
|
- Implement chainlib-gen for giftable token
|
||||||
|
* 0.5.4
|
||||||
|
- Enable setting sender address on contract read calls
|
||||||
|
* 0.5.3
|
||||||
|
- Fix giftable token cli commands
|
||||||
|
* 0.5.2
|
||||||
|
- Add block height to balance call
|
||||||
* 0.5.1
|
* 0.5.1
|
||||||
- Change license to AGPL3 and copyright waived to public domain
|
- Change license to AGPL3 and copyright waived to public domain
|
||||||
* 0.5.0
|
* 0.5.0
|
||||||
|
@ -1 +1 @@
|
|||||||
include **/data/ERC20.json **/data/GiftableToken.json **/data/GiftableToken.bin *requirements.txt CHANGELOG LICENSE WAIVER WAIVER.asc
|
include **/data/ERC20.json **/data/GiftableToken.json **/data/GiftableToken.bin *requirements.txt CHANGELOG LICENSE WAIVER WAIVER.asc **/data/.chainlib
|
||||||
|
@ -65,7 +65,7 @@ logg = logging.getLogger()
|
|||||||
|
|
||||||
arg_flags = ArgFlag()
|
arg_flags = ArgFlag()
|
||||||
arg = Arg(arg_flags)
|
arg = Arg(arg_flags)
|
||||||
flags = arg_flags.STD_READ | arg_flags.EXEC
|
flags = arg_flags.STD_READ | arg_flags.EXEC | arg_flags.SENDER
|
||||||
|
|
||||||
argparser = chainlib.eth.cli.ArgumentParser()
|
argparser = chainlib.eth.cli.ArgumentParser()
|
||||||
argparser = process_args(argparser, arg, flags)
|
argparser = process_args(argparser, arg, flags)
|
||||||
@ -87,33 +87,28 @@ logg.debug('settings loaded:\n{}'.format(settings))
|
|||||||
def main():
|
def main():
|
||||||
token_address = settings.get('EXEC')
|
token_address = settings.get('EXEC')
|
||||||
conn = settings.get('CONN')
|
conn = settings.get('CONN')
|
||||||
|
sender_address = settings.get('SENDER_ADDRESS')
|
||||||
g = ERC20(
|
g = ERC20(
|
||||||
chain_spec=settings.get('CHAIN_SPEC'),
|
chain_spec=settings.get('CHAIN_SPEC'),
|
||||||
gas_oracle=settings.get('GAS_ORACLE'),
|
gas_oracle=settings.get('GAS_ORACLE'),
|
||||||
)
|
)
|
||||||
|
|
||||||
# determine decimals
|
# determine decimals
|
||||||
decimals_o = g.decimals(token_address)
|
if not config.get('_RAW'):
|
||||||
|
decimals_o = g.decimals(token_address, sender_address=sender_address)
|
||||||
r = conn.do(decimals_o)
|
r = conn.do(decimals_o)
|
||||||
decimals = int(strip_0x(r), 16)
|
decimals = int(strip_0x(r), 16)
|
||||||
logg.info('decimals {}'.format(decimals))
|
logg.info('decimals {}'.format(decimals))
|
||||||
|
|
||||||
name_o = g.name(token_address)
|
|
||||||
r = conn.do(name_o)
|
|
||||||
token_name = g.parse_name(r)
|
|
||||||
logg.info('name {}'.format(token_name))
|
|
||||||
|
|
||||||
symbol_o = g.symbol(token_address)
|
|
||||||
r = conn.do(symbol_o)
|
|
||||||
token_symbol = g.parse_symbol(r)
|
|
||||||
logg.info('symbol {}'.format(token_symbol))
|
|
||||||
|
|
||||||
# get balance
|
# get balance
|
||||||
balance_o = g.balance(token_address, settings.get('RECIPIENT'))
|
balance_o = g.balance(token_address, settings.get('RECIPIENT'), sender_address=sender_address)
|
||||||
r = conn.do(balance_o)
|
r = conn.do(balance_o)
|
||||||
|
|
||||||
hx = strip_0x(r)
|
hx = strip_0x(r)
|
||||||
balance_value = int(hx, 16)
|
balance_value = int(hx, 16)
|
||||||
|
if config.get('_RAW'):
|
||||||
|
logg.debug('balance {} = {}'.format(even(hx), balance_value))
|
||||||
|
else:
|
||||||
logg.debug('balance {} = {} decimals {}'.format(even(hx), balance_value, decimals))
|
logg.debug('balance {} = {} decimals {}'.format(even(hx), balance_value, decimals))
|
||||||
|
|
||||||
balance_str = str(balance_value)
|
balance_str = str(balance_value)
|
||||||
|
@ -74,7 +74,7 @@ def process_config_local(config, arg, args, flags):
|
|||||||
|
|
||||||
arg_flags = ArgFlag()
|
arg_flags = ArgFlag()
|
||||||
arg = Arg(arg_flags)
|
arg = Arg(arg_flags)
|
||||||
flags = arg_flags.STD_READ | arg_flags.EXEC | arg_flags.TAB
|
flags = arg_flags.STD_READ | arg_flags.EXEC | arg_flags.TAB | arg_flags.SENDER
|
||||||
|
|
||||||
argparser = chainlib.eth.cli.ArgumentParser()
|
argparser = chainlib.eth.cli.ArgumentParser()
|
||||||
argparser = process_args(argparser, arg, flags)
|
argparser = process_args(argparser, arg, flags)
|
||||||
@ -96,6 +96,7 @@ logg.debug('settings loaded:\n{}'.format(settings))
|
|||||||
def main():
|
def main():
|
||||||
token_address = config.get('_CONTRACT')
|
token_address = config.get('_CONTRACT')
|
||||||
conn = settings.get('CONN')
|
conn = settings.get('CONN')
|
||||||
|
sender_address = settings.get('SENDER_ADDRESS')
|
||||||
g = ERC20(
|
g = ERC20(
|
||||||
chain_spec=settings.get('CHAIN_SPEC'),
|
chain_spec=settings.get('CHAIN_SPEC'),
|
||||||
gas_oracle=settings.get('GAS_ORACLE'),
|
gas_oracle=settings.get('GAS_ORACLE'),
|
||||||
@ -104,7 +105,7 @@ def main():
|
|||||||
outkeys = config.get('_OUTARG')
|
outkeys = config.get('_OUTARG')
|
||||||
|
|
||||||
if not outkeys or 'address' in outkeys:
|
if not outkeys or 'address' in outkeys:
|
||||||
name_o = g.name(token_address)
|
name_o = g.name(token_address, sender_address=sender_address)
|
||||||
r = conn.do(name_o)
|
r = conn.do(name_o)
|
||||||
token_name = g.parse_name(r)
|
token_name = g.parse_name(r)
|
||||||
s = ''
|
s = ''
|
||||||
@ -114,7 +115,7 @@ def main():
|
|||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
if not outkeys or 'symbol' in outkeys:
|
if not outkeys or 'symbol' in outkeys:
|
||||||
symbol_o = g.symbol(token_address)
|
symbol_o = g.symbol(token_address, sender_address=sender_address)
|
||||||
r = conn.do(symbol_o)
|
r = conn.do(symbol_o)
|
||||||
token_symbol = g.parse_symbol(r)
|
token_symbol = g.parse_symbol(r)
|
||||||
s = ''
|
s = ''
|
||||||
@ -124,7 +125,7 @@ def main():
|
|||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
if not outkeys or 'decimals' in outkeys:
|
if not outkeys or 'decimals' in outkeys:
|
||||||
decimals_o = g.decimals(token_address)
|
decimals_o = g.decimals(token_address, sender_address=sender_address)
|
||||||
r = conn.do(decimals_o)
|
r = conn.do(decimals_o)
|
||||||
decimals = int(strip_0x(r), 16)
|
decimals = int(strip_0x(r), 16)
|
||||||
s = ''
|
s = ''
|
||||||
@ -134,7 +135,7 @@ def main():
|
|||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
if not outkeys or 'supply' in outkeys:
|
if not outkeys or 'supply' in outkeys:
|
||||||
supply_o = g.total_supply(token_address)
|
supply_o = g.total_supply(token_address, sender_address=sender_address)
|
||||||
r = conn.do(supply_o)
|
r = conn.do(supply_o)
|
||||||
supply = int(strip_0x(r), 16)
|
supply = int(strip_0x(r), 16)
|
||||||
s = ''
|
s = ''
|
||||||
|
1
python/eth_erc20/unittest/__init__.py
Normal file
1
python/eth_erc20/unittest/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .base import *
|
169
python/eth_erc20/unittest/base.py
Normal file
169
python/eth_erc20/unittest/base.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.eth.unittest.ethtester import EthTesterCase
|
||||||
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
|
from chainlib.eth.gas import OverrideGasOracle
|
||||||
|
from chainlib.eth.tx import (
|
||||||
|
transaction,
|
||||||
|
receipt,
|
||||||
|
)
|
||||||
|
from hexathon import strip_0x
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from eth_erc20 import ERC20
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInterface:
|
||||||
|
|
||||||
|
def test_balance(self):
|
||||||
|
c = ERC20(self.chain_spec)
|
||||||
|
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||||
|
r = self.conn.do(o)
|
||||||
|
balance = ERC20.parse_balance(r)
|
||||||
|
self.assertEqual(self.initial_supply, balance)
|
||||||
|
|
||||||
|
|
||||||
|
def test_supply(self):
|
||||||
|
c = ERC20(self.chain_spec)
|
||||||
|
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.conn.do(o)
|
||||||
|
supply = ERC20.parse_total_supply(r)
|
||||||
|
self.assertEqual(self.initial_supply, supply)
|
||||||
|
|
||||||
|
|
||||||
|
def test_name(self):
|
||||||
|
c = ERC20(self.chain_spec)
|
||||||
|
o = c.name(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.conn.do(o)
|
||||||
|
name = ERC20.parse_name(r)
|
||||||
|
self.assertEqual(self.name, name)
|
||||||
|
|
||||||
|
|
||||||
|
def test_symbol(self):
|
||||||
|
c = ERC20(self.chain_spec)
|
||||||
|
o = c.symbol(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.conn.do(o)
|
||||||
|
symbol = ERC20.parse_symbol(r)
|
||||||
|
self.assertEqual(self.symbol, symbol)
|
||||||
|
|
||||||
|
|
||||||
|
def test_direct_transfer(self):
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
||||||
|
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
||||||
|
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
|
|
||||||
|
(tx_hash, o) = c.transfer(self.address, self.accounts[0], self.accounts[1], 1000)
|
||||||
|
self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, self.initial_supply - 1000)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, 1000)
|
||||||
|
|
||||||
|
o = transaction(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
data = c.parse_transfer_request(r['data'])
|
||||||
|
self.assertEqual(data[0], strip_0x(self.accounts[1]))
|
||||||
|
self.assertEqual(data[1], 1000)
|
||||||
|
|
||||||
|
|
||||||
|
def test_transfer_from(self):
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
||||||
|
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
||||||
|
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
|
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 1000)
|
||||||
|
self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
o = c.allowance(self.address, self.accounts[0], self.accounts[1], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
allowance = c.parse_allowance(r)
|
||||||
|
self.assertEqual(allowance, 1000)
|
||||||
|
|
||||||
|
o = transaction(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
data = c.parse_approve_request(r['data'])
|
||||||
|
self.assertEqual(data[0], strip_0x(self.accounts[1]))
|
||||||
|
self.assertEqual(data[1], 1000)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn)
|
||||||
|
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
|
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1001)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
||||||
|
|
||||||
|
o = transaction(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
data = c.parse_transfer_from_request(r['data'])
|
||||||
|
self.assertEqual(data[0], strip_0x(self.accounts[0]))
|
||||||
|
self.assertEqual(data[1], strip_0x(self.accounts[2]))
|
||||||
|
self.assertEqual(data[2], 1001)
|
||||||
|
|
||||||
|
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1000)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, self.initial_supply - 1000)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, 1000)
|
||||||
|
|
||||||
|
|
||||||
|
def test_revoke_approve(self):
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
||||||
|
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
||||||
|
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
|
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 1000)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 999)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
||||||
|
|
||||||
|
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 0)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
o = c.allowance(self.address, self.accounts[0], self.accounts[1], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
allowance = c.parse_allowance(r)
|
||||||
|
self.assertEqual(allowance, 0)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn)
|
||||||
|
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
||||||
|
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
@ -1 +1,4 @@
|
|||||||
from .factory import GiftableToken
|
from .factory import GiftableToken
|
||||||
|
from .factory import bytecode
|
||||||
|
from .factory import create
|
||||||
|
from .factory import args
|
||||||
|
0
python/giftable_erc20_token/data/.chainlib
Normal file
0
python/giftable_erc20_token/data/.chainlib
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
python/giftable_erc20_token/data/StaticToken.bin
Normal file
1
python/giftable_erc20_token/data/StaticToken.bin
Normal file
File diff suppressed because one or more lines are too long
1
python/giftable_erc20_token/data/StaticToken.json
Normal file
1
python/giftable_erc20_token/data/StaticToken.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_supply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
|
@ -0,0 +1 @@
|
|||||||
|
{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_supply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"StaticToken.sol":"StaticToken"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"StaticToken.sol":{"keccak256":"0xfb467ccf3465a3f2c8ad23bc4933f7e1ac1964d7ee1aa185e72e4698b015b5b3","license":"AGPL-3.0-or-later","urls":["bzz-raw://6c2230ba6ddadf062f5f3391f2a852da8617fbefd0c0511d9bc372c6c66c6424","dweb:/ipfs/QmWSYF3WxmKyDEHp3NZMWjE6Yu7mY6XVB6tuTeFKpjxL9P"]}},"version":1}
|
@ -12,28 +12,43 @@ from chainlib.eth.contract import (
|
|||||||
ABIContractEncoder,
|
ABIContractEncoder,
|
||||||
ABIContractType,
|
ABIContractType,
|
||||||
)
|
)
|
||||||
|
from chainlib.eth.constant import ZERO_ADDRESS
|
||||||
|
from chainlib.jsonrpc import JSONRPCRequest
|
||||||
|
from hexathon import add_0x
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from giftable_erc20_token.data import data_dir
|
from giftable_erc20_token.data import data_dir
|
||||||
|
from eth_erc20 import ERC20
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GiftableToken(TxFactory):
|
class GiftableToken(ERC20):
|
||||||
|
|
||||||
__abi = None
|
__abi = None
|
||||||
__bytecode = None
|
__bytecode = None
|
||||||
|
|
||||||
def constructor(self, sender_address, name, symbol, decimals, tx_format=TxFormat.JSONRPC):
|
def constructor(self, sender_address, name, symbol, decimals, expire=0, tx_format=TxFormat.JSONRPC, version=None):
|
||||||
code = GiftableToken.bytecode()
|
code = self.cargs(name, symbol, decimals, expire=expire)
|
||||||
|
tx = self.template(sender_address, None, use_nonce=True)
|
||||||
|
tx = self.set_code(tx, code)
|
||||||
|
return self.finalize(tx, tx_format)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cargs(name, symbol, decimals, expire=0, version=None):
|
||||||
|
if expire == None:
|
||||||
|
expire = 0
|
||||||
|
code = GiftableToken.bytecode(version=version)
|
||||||
enc = ABIContractEncoder()
|
enc = ABIContractEncoder()
|
||||||
enc.string(name)
|
enc.string(name)
|
||||||
enc.string(symbol)
|
enc.string(symbol)
|
||||||
enc.uint256(decimals)
|
enc.uint256(decimals)
|
||||||
code += enc.get()
|
enc.uint256(expire)
|
||||||
tx = self.template(sender_address, None, use_nonce=True)
|
args = enc.get()
|
||||||
tx = self.set_code(tx, code)
|
code += args
|
||||||
return self.finalize(tx, tx_format)
|
logg.debug('constructor code: ' + args)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -51,7 +66,7 @@ class GiftableToken(TxFactory):
|
|||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def bytecode():
|
def bytecode(version=None):
|
||||||
if GiftableToken.__bytecode == None:
|
if GiftableToken.__bytecode == None:
|
||||||
f = open(os.path.join(data_dir, 'GiftableToken.bin'))
|
f = open(os.path.join(data_dir, 'GiftableToken.bin'))
|
||||||
GiftableToken.__bytecode = f.read()
|
GiftableToken.__bytecode = f.read()
|
||||||
@ -97,3 +112,59 @@ class GiftableToken(TxFactory):
|
|||||||
return tx
|
return tx
|
||||||
|
|
||||||
|
|
||||||
|
def burn(self, contract_address, sender_address, value, tx_format=TxFormat.JSONRPC):
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('burn')
|
||||||
|
enc.typ(ABIContractType.UINT256)
|
||||||
|
enc.uint256(value)
|
||||||
|
data = enc.get()
|
||||||
|
tx = self.template(sender_address, contract_address, use_nonce=True)
|
||||||
|
tx = self.set_code(tx, data)
|
||||||
|
tx = self.finalize(tx, tx_format)
|
||||||
|
return tx
|
||||||
|
|
||||||
|
|
||||||
|
def burned(self, contract_address, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||||
|
j = JSONRPCRequest(id_generator)
|
||||||
|
o = j.template()
|
||||||
|
o['method'] = 'eth_call'
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('totalBurned')
|
||||||
|
data = add_0x(enc.get())
|
||||||
|
tx = self.template(sender_address, contract_address)
|
||||||
|
tx = self.set_code(tx, data)
|
||||||
|
o['params'].append(self.normalize(tx))
|
||||||
|
o['params'].append('latest')
|
||||||
|
o = j.finalize(o)
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def total_minted(self, contract_address, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||||
|
j = JSONRPCRequest(id_generator)
|
||||||
|
o = j.template()
|
||||||
|
o['method'] = 'eth_call'
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('totalMinted')
|
||||||
|
data = add_0x(enc.get())
|
||||||
|
tx = self.template(sender_address, contract_address)
|
||||||
|
tx = self.set_code(tx, data)
|
||||||
|
o['params'].append(self.normalize(tx))
|
||||||
|
o['params'].append('latest')
|
||||||
|
o = j.finalize(o)
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def bytecode(**kwargs):
|
||||||
|
return GiftableToken.bytecode(version=kwargs.get('version'))
|
||||||
|
|
||||||
|
|
||||||
|
def create(**kwargs):
|
||||||
|
return GiftableToken.cargs(kwargs['name'], kwargs['symbol'], kwargs['decimals'], expire=kwargs.get('expire'), version=kwargs.get('version'))
|
||||||
|
|
||||||
|
|
||||||
|
def args(v):
|
||||||
|
if v == 'create':
|
||||||
|
return (['name', 'symbol', 'decimals'], ['expire', 'version'],)
|
||||||
|
elif v == 'default' or v == 'bytecode':
|
||||||
|
return ([], ['version'],)
|
||||||
|
raise ValueError('unknown command: ' + v)
|
||||||
|
@ -45,6 +45,7 @@ def process_config_local(config, arg, args, flags):
|
|||||||
config.add(args.token_name, '_TOKEN_NAME', False)
|
config.add(args.token_name, '_TOKEN_NAME', False)
|
||||||
config.add(args.token_symbol, '_TOKEN_SYMBOL', False)
|
config.add(args.token_symbol, '_TOKEN_SYMBOL', False)
|
||||||
config.add(args.token_decimals, '_TOKEN_DECIMALS', False)
|
config.add(args.token_decimals, '_TOKEN_DECIMALS', False)
|
||||||
|
config.add(args.token_expire, '_TOKEN_EXPIRE', False)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ argparser = process_args(argparser, arg, flags)
|
|||||||
argparser.add_argument('--name', dest='token_name', required=True, type=str, help='Token name')
|
argparser.add_argument('--name', dest='token_name', required=True, type=str, help='Token name')
|
||||||
argparser.add_argument('--symbol', dest='token_symbol', required=True, type=str, help='Token symbol')
|
argparser.add_argument('--symbol', dest='token_symbol', required=True, type=str, help='Token symbol')
|
||||||
argparser.add_argument('--decimals', dest='token_decimals', default=18, type=int, help='Token decimals')
|
argparser.add_argument('--decimals', dest='token_decimals', default=18, type=int, help='Token decimals')
|
||||||
|
argparser.add_argument('--expire', dest='token_expire', default=0, type=int, help='Token expiry timestamp (after which token cannot be traded)')
|
||||||
args = argparser.parse_args()
|
args = argparser.parse_args()
|
||||||
|
|
||||||
logg = process_log(args, logg)
|
logg = process_log(args, logg)
|
||||||
@ -87,6 +89,7 @@ def main():
|
|||||||
config.get('_TOKEN_NAME'),
|
config.get('_TOKEN_NAME'),
|
||||||
config.get('_TOKEN_SYMBOL'),
|
config.get('_TOKEN_SYMBOL'),
|
||||||
config.get('_TOKEN_DECIMALS'),
|
config.get('_TOKEN_DECIMALS'),
|
||||||
|
expire=config.get('_TOKEN_EXPIRE'),
|
||||||
)
|
)
|
||||||
if settings.get('RPC_SEND'):
|
if settings.get('RPC_SEND'):
|
||||||
conn.do(o)
|
conn.do(o)
|
1
python/giftable_erc20_token/unittest/__init__.py
Normal file
1
python/giftable_erc20_token/unittest/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .base import *
|
59
python/giftable_erc20_token/unittest/base.py
Normal file
59
python/giftable_erc20_token/unittest/base.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.eth.unittest.ethtester import EthTesterCase
|
||||||
|
from chainlib.connection import RPCConnection
|
||||||
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
|
from chainlib.eth.tx import receipt
|
||||||
|
from chainlib.eth.address import to_checksum_address
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from giftable_erc20_token import GiftableToken
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGiftableToken(EthTesterCase):
|
||||||
|
|
||||||
|
expire = 0
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestGiftableToken, self).setUp()
|
||||||
|
self.conn = RPCConnection.connect(self.chain_spec, 'default')
|
||||||
|
|
||||||
|
address = self.publish_giftable_token('Foo Token', 'FOO', 16, expire=self.expire)
|
||||||
|
self.address = to_checksum_address(address)
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
self.initial_supply = 1 << 40
|
||||||
|
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], self.initial_supply)
|
||||||
|
r = self.conn.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.conn.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
|
||||||
|
def publish_giftable_token(self, name, symbol, decimals=16, expire=None):
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
self.symbol = name
|
||||||
|
self.name = symbol
|
||||||
|
self.decimals = decimals
|
||||||
|
(tx_hash, o) = c.constructor(self.accounts[0], self.name, self.symbol, self.decimals, expire=expire)
|
||||||
|
self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
address = r['contract_address']
|
||||||
|
logg.debug('published on address {} with hash {}'.format(address, tx_hash))
|
||||||
|
return address
|
||||||
|
|
||||||
|
|
||||||
|
class TestGiftableExpireToken(TestGiftableToken):
|
||||||
|
|
||||||
|
expire = int(time.time()) + 100000
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestGiftableExpireToken, self).setUp()
|
@ -1,3 +1,4 @@
|
|||||||
confini~=0.6.1
|
confini~=0.6.1
|
||||||
chainlib-eth~=0.4.2
|
chainlib-eth~=0.6.0
|
||||||
|
chainlib~=0.5.0
|
||||||
potaahto~=0.1.1
|
potaahto~=0.1.1
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
export PYTHONPATH=${PYTHONPATH}:.
|
||||||
|
>&2 echo "using pythonpath $PYTHONPATH"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
set -x
|
set -x
|
||||||
for f in `ls tests/*.py`; do
|
for f in `ls tests/*.py`; do
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = eth-erc20
|
name = eth-erc20
|
||||||
version = 0.5.3
|
version = 0.9.0
|
||||||
description = ERC20 interface and simple contract with deployment script that lets any address mint and gift itself tokens.
|
description = ERC20 interface and simple contract with deployment script that lets any address mint and gift itself tokens.
|
||||||
author = Louis Holbrook
|
author = Louis Holbrook
|
||||||
author_email = dev@holbrook.no
|
author_email = dev@holbrook.no
|
||||||
url = https://git.defalsify.org/eth-erc20.git
|
url = https://git.defalsify.org/eth-erc20
|
||||||
keywords =
|
keywords =
|
||||||
dlt
|
dlt
|
||||||
ethereum
|
ethereum
|
||||||
@ -14,11 +14,12 @@ keywords =
|
|||||||
classifiers =
|
classifiers =
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Operating System :: OS Independent
|
Operating System :: OS Independent
|
||||||
Development Status :: 3 - Alpha
|
Development Status :: 4 - Beta
|
||||||
Environment :: No Input/Output (Daemon)
|
Environment :: Console
|
||||||
Intended Audience :: Developers
|
Intended Audience :: Developers
|
||||||
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
||||||
Topic :: Internet
|
Topic :: Internet
|
||||||
|
Topic :: Software Development :: Libraries
|
||||||
#Topic :: Blockchain :: EVM
|
#Topic :: Blockchain :: EVM
|
||||||
license = AGPLv3+
|
license = AGPLv3+
|
||||||
licence_files =
|
licence_files =
|
||||||
@ -26,14 +27,16 @@ licence_files =
|
|||||||
|
|
||||||
[options]
|
[options]
|
||||||
include_package_data = True
|
include_package_data = True
|
||||||
python_requires = >= 3.7
|
python_requires = >= 3.8
|
||||||
packages =
|
packages =
|
||||||
giftable_erc20_token
|
giftable_erc20_token
|
||||||
giftable_erc20_token.runnable
|
giftable_erc20_token.runnable
|
||||||
|
giftable_erc20_token.unittest
|
||||||
giftable_erc20_token.data
|
giftable_erc20_token.data
|
||||||
eth_erc20
|
eth_erc20
|
||||||
eth_erc20.data
|
eth_erc20.data
|
||||||
eth_erc20.runnable
|
eth_erc20.runnable
|
||||||
|
eth_erc20.unittest
|
||||||
static_token.data
|
static_token.data
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
@ -46,9 +49,8 @@ packages =
|
|||||||
|
|
||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
giftable-token-deploy = giftable_erc20_token.runnable.deploy:main
|
giftable-token-publish = giftable_erc20_token.runnable.publish:main
|
||||||
giftable-token-gift = giftable_erc20_token.runnable.gift:main
|
giftable-token-gift = giftable_erc20_token.runnable.gift:main
|
||||||
giftable-token-minter = giftable_erc20_token.runnable.minter:main
|
|
||||||
erc20-transfer = eth_erc20.runnable.transfer:main
|
erc20-transfer = eth_erc20.runnable.transfer:main
|
||||||
erc20-balance = eth_erc20.runnable.balance:main
|
erc20-balance = eth_erc20.runnable.balance:main
|
||||||
erc20-info = eth_erc20.runnable.info:main
|
erc20-info = eth_erc20.runnable.info:main
|
||||||
|
1
python/static_token/__init__.py
Normal file
1
python/static_token/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .factory import *
|
File diff suppressed because one or more lines are too long
3
python/static_token/data/__init__.py
Normal file
3
python/static_token/data/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
data_dir = os.path.realpath(os.path.dirname(__file__))
|
60
python/static_token/factory.py
Normal file
60
python/static_token/factory.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.eth.tx import (
|
||||||
|
TxFactory,
|
||||||
|
TxFormat,
|
||||||
|
)
|
||||||
|
from chainlib.hash import keccak256_string_to_hex
|
||||||
|
from chainlib.eth.contract import (
|
||||||
|
ABIContractEncoder,
|
||||||
|
ABIContractType,
|
||||||
|
)
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from static_token.data import data_dir
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class StaticToken(TxFactory):
|
||||||
|
|
||||||
|
__abi = None
|
||||||
|
__bytecode = None
|
||||||
|
|
||||||
|
def constructor(self, sender_address, name, symbol, decimals, supply, tx_format=TxFormat.JSONRPC):
|
||||||
|
code = StaticToken.bytecode()
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.string(name)
|
||||||
|
enc.string(symbol)
|
||||||
|
enc.uint256(decimals)
|
||||||
|
enc.uint256(supply)
|
||||||
|
code += enc.get()
|
||||||
|
tx = self.template(sender_address, None, use_nonce=True)
|
||||||
|
tx = self.set_code(tx, code)
|
||||||
|
return self.finalize(tx, tx_format)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def gas(code=None):
|
||||||
|
return 2000000
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def abi():
|
||||||
|
if StaticToken.__abi == None:
|
||||||
|
f = open(os.path.join(data_dir, 'StaticToken.json'), 'r')
|
||||||
|
StaticToken.__abi = json.load(f)
|
||||||
|
f.close()
|
||||||
|
return StaticToken.__abi
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def bytecode():
|
||||||
|
if StaticToken.__bytecode == None:
|
||||||
|
f = open(os.path.join(data_dir, 'StaticToken.bin'))
|
||||||
|
StaticToken.__bytecode = f.read()
|
||||||
|
f.close()
|
||||||
|
return StaticToken.__bytecode
|
1
python/static_token/unittest/__init__.py
Normal file
1
python/static_token/unittest/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .base import TestStaticToken
|
36
python/static_token/unittest/base.py
Normal file
36
python/static_token/unittest/base.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.eth.unittest.ethtester import EthTesterCase
|
||||||
|
from chainlib.connection import RPCConnection
|
||||||
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
|
from chainlib.eth.tx import receipt
|
||||||
|
from chainlib.eth.address import to_checksum_address
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from static_token import StaticToken
|
||||||
|
from eth_erc20.unittest import TestInterface
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestStaticToken(EthTesterCase, TestInterface):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestStaticToken, self).setUp()
|
||||||
|
self.conn = RPCConnection.connect(self.chain_spec, 'default')
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
||||||
|
c = StaticToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
self.symbol = 'FOO'
|
||||||
|
self.name = 'Foo Token'
|
||||||
|
self.decimals = 16
|
||||||
|
self.initial_supply = 1 << 24
|
||||||
|
(tx_hash, o) = c.constructor(self.accounts[0], self.name, self.symbol, self.decimals, self.initial_supply)
|
||||||
|
self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
self.address = to_checksum_address(r['contract_address'])
|
||||||
|
logg.debug('published statictoken on address {} with hash {}'.format(self.address, tx_hash))
|
@ -1,3 +1,3 @@
|
|||||||
eth_tester==0.5.0b3
|
eth_tester==0.10.0b4
|
||||||
py-evm==0.3.0a20
|
py-evm==0.10.0b4
|
||||||
pytest==6.0.1
|
pytest==6.0.1
|
||||||
|
@ -1,200 +1,13 @@
|
|||||||
# standard imports
|
# import logging
|
||||||
import logging
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
# external imports
|
|
||||||
from chainlib.eth.unittest.ethtester import EthTesterCase
|
|
||||||
from chainlib.connection import RPCConnection
|
|
||||||
from chainlib.eth.nonce import RPCNonceOracle
|
|
||||||
from chainlib.eth.gas import OverrideGasOracle
|
|
||||||
from chainlib.eth.tx import (
|
|
||||||
transaction,
|
|
||||||
receipt,
|
|
||||||
)
|
|
||||||
from chainlib.eth.address import to_checksum_address
|
|
||||||
from hexathon import strip_0x
|
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from giftable_erc20_token import GiftableToken
|
from giftable_erc20_token.unittest import TestGiftableToken
|
||||||
from eth_erc20 import ERC20
|
from eth_erc20.unittest import TestInterface
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
logg = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class TestToken(EthTesterCase):
|
class TestBasic(TestGiftableToken, TestInterface):
|
||||||
|
pass
|
||||||
def setUp(self):
|
|
||||||
super(TestToken, self).setUp()
|
|
||||||
self.conn = RPCConnection.connect(self.chain_spec, 'default')
|
|
||||||
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
|
||||||
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
|
||||||
self.symbol = 'FOO'
|
|
||||||
self.name = 'Foo Token'
|
|
||||||
self.decimals = 16
|
|
||||||
(tx_hash, o) = c.constructor(self.accounts[0], self.name, self.symbol, self.decimals)
|
|
||||||
r = self.conn.do(o)
|
|
||||||
logg.debug('deployed with hash {}'.format(r))
|
|
||||||
|
|
||||||
o = receipt(r)
|
|
||||||
r = self.conn.do(o)
|
|
||||||
self.address = to_checksum_address(r['contract_address'])
|
|
||||||
|
|
||||||
self.initial_supply = 1 << 40
|
|
||||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], self.initial_supply)
|
|
||||||
r = self.conn.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.conn.do(o)
|
|
||||||
self.assertEqual(r['status'], 1)
|
|
||||||
|
|
||||||
|
|
||||||
def test_balance(self):
|
|
||||||
c = ERC20(self.chain_spec)
|
|
||||||
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
|
||||||
r = self.conn.do(o)
|
|
||||||
balance = ERC20.parse_balance(r)
|
|
||||||
self.assertEqual(self.initial_supply, balance)
|
|
||||||
|
|
||||||
|
|
||||||
def test_supply(self):
|
|
||||||
c = ERC20(self.chain_spec)
|
|
||||||
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
|
||||||
r = self.conn.do(o)
|
|
||||||
supply = ERC20.parse_total_supply(r)
|
|
||||||
self.assertEqual(self.initial_supply, supply)
|
|
||||||
|
|
||||||
|
|
||||||
def test_name(self):
|
|
||||||
c = ERC20(self.chain_spec)
|
|
||||||
o = c.name(self.address, sender_address=self.accounts[0])
|
|
||||||
r = self.conn.do(o)
|
|
||||||
name = ERC20.parse_name(r)
|
|
||||||
self.assertEqual(self.name, name)
|
|
||||||
|
|
||||||
|
|
||||||
def test_symbol(self):
|
|
||||||
c = ERC20(self.chain_spec)
|
|
||||||
o = c.symbol(self.address, sender_address=self.accounts[0])
|
|
||||||
r = self.conn.do(o)
|
|
||||||
symbol = ERC20.parse_symbol(r)
|
|
||||||
self.assertEqual(self.symbol, symbol)
|
|
||||||
|
|
||||||
|
|
||||||
def test_transfer(self):
|
|
||||||
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
|
||||||
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
|
||||||
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
|
||||||
(tx_hash, o) = c.transfer(self.address, self.accounts[0], self.accounts[1], 1000)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 1)
|
|
||||||
|
|
||||||
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
balance = c.parse_balance(r)
|
|
||||||
self.assertEqual(balance, self.initial_supply - 1000)
|
|
||||||
|
|
||||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
balance = c.parse_balance(r)
|
|
||||||
self.assertEqual(balance, 1000)
|
|
||||||
|
|
||||||
o = transaction(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
data = c.parse_transfer_request(r['data'])
|
|
||||||
self.assertEqual(data[0], strip_0x(self.accounts[1]))
|
|
||||||
self.assertEqual(data[1], 1000)
|
|
||||||
|
|
||||||
|
|
||||||
def test_transfer_from(self):
|
|
||||||
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
|
||||||
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
|
||||||
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
|
||||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 1000)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 1)
|
|
||||||
|
|
||||||
o = c.allowance(self.address, self.accounts[0], self.accounts[1], sender_address=self.accounts[0])
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
allowance = c.parse_allowance(r)
|
|
||||||
self.assertEqual(allowance, 1000)
|
|
||||||
|
|
||||||
o = transaction(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
data = c.parse_approve_request(r['data'])
|
|
||||||
self.assertEqual(data[0], strip_0x(self.accounts[1]))
|
|
||||||
self.assertEqual(data[1], 1000)
|
|
||||||
|
|
||||||
nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn)
|
|
||||||
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
|
||||||
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1001)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 0)
|
|
||||||
|
|
||||||
o = transaction(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
data = c.parse_transfer_from_request(r['data'])
|
|
||||||
self.assertEqual(data[0], strip_0x(self.accounts[0]))
|
|
||||||
self.assertEqual(data[1], strip_0x(self.accounts[2]))
|
|
||||||
self.assertEqual(data[2], 1001)
|
|
||||||
|
|
||||||
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1000)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 1)
|
|
||||||
|
|
||||||
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
balance = c.parse_balance(r)
|
|
||||||
self.assertEqual(balance, self.initial_supply - 1000)
|
|
||||||
|
|
||||||
o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
balance = c.parse_balance(r)
|
|
||||||
self.assertEqual(balance, 1000)
|
|
||||||
|
|
||||||
|
|
||||||
def test_revoke_approve(self):
|
|
||||||
nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
|
|
||||||
gas_oracle = OverrideGasOracle(limit=100000, conn=self.conn)
|
|
||||||
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
|
||||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 1000)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 1)
|
|
||||||
|
|
||||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 999)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 0)
|
|
||||||
|
|
||||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 0)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 1)
|
|
||||||
|
|
||||||
o = c.allowance(self.address, self.accounts[0], self.accounts[1], sender_address=self.accounts[0])
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
allowance = c.parse_allowance(r)
|
|
||||||
self.assertEqual(allowance, 0)
|
|
||||||
|
|
||||||
nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn)
|
|
||||||
c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle)
|
|
||||||
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
o = receipt(tx_hash)
|
|
||||||
r = self.rpc.do(o)
|
|
||||||
self.assertEqual(r['status'], 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
123
python/tests/test_giftable.py
Normal file
123
python/tests/test_giftable.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.eth.constant import ZERO_ADDRESS
|
||||||
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
|
from chainlib.eth.tx import receipt
|
||||||
|
from chainlib.eth.block import (
|
||||||
|
block_latest,
|
||||||
|
block_by_number,
|
||||||
|
)
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from giftable_erc20_token import GiftableToken
|
||||||
|
from giftable_erc20_token.unittest import TestGiftableExpireToken
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
testdir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExpire(TestGiftableExpireToken):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestExpire, self).setUp()
|
||||||
|
|
||||||
|
|
||||||
|
def test_expires(self):
|
||||||
|
mint_amount = self.initial_supply
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
|
||||||
|
(tx_hash, o) = c.transfer(self.address, self.accounts[0], self.accounts[1], int(mint_amount / 2))
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
self.backend.time_travel(self.expire + 60)
|
||||||
|
o = block_latest()
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = block_by_number(r)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertGreaterEqual(r['timestamp'], self.expire)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
(tx_hash, o) = c.transfer(self.address, self.accounts[0], self.accounts[1], 1)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
(tx_hash, o) = c.transfer(self.address, self.accounts[1], self.accounts[0], 1)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, int(mint_amount / 2))
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance += c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, mint_amount)
|
||||||
|
|
||||||
|
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
supply = c.parse_balance(r)
|
||||||
|
self.assertEqual(supply, mint_amount)
|
||||||
|
|
||||||
|
|
||||||
|
def test_burn(self):
|
||||||
|
mint_amount = self.initial_supply
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
(tx_hash, o) = c.burn(self.address, self.accounts[1], int(mint_amount / 4))
|
||||||
|
self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
(tx_hash, o) = c.burn(self.address, self.accounts[0], int(mint_amount / 4))
|
||||||
|
self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
o = c.burned(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
burned = c.parse_balance(r)
|
||||||
|
self.assertEqual(burned, int(mint_amount / 4))
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, mint_amount - burned)
|
||||||
|
|
||||||
|
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, mint_amount - burned)
|
||||||
|
|
||||||
|
o = c.total_minted(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
self.assertEqual(balance, mint_amount)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
38
python/tests/test_static.py
Normal file
38
python/tests/test_static.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# standard imports
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from chainlib.eth.constant import ZERO_ADDRESS
|
||||||
|
from chainlib.eth.nonce import RPCNonceOracle
|
||||||
|
from chainlib.eth.tx import receipt
|
||||||
|
from chainlib.eth.block import (
|
||||||
|
block_latest,
|
||||||
|
block_by_number,
|
||||||
|
)
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from static_token import StaticToken
|
||||||
|
from static_token.unittest import TestStaticToken
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
testdir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExpire(TestStaticToken):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestExpire, self).setUp()
|
||||||
|
|
||||||
|
|
||||||
|
def test_static_interface(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,12 +1,15 @@
|
|||||||
pragma solidity >=0.6.11;
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
// File-Version: 2
|
// File-Version: 3
|
||||||
|
|
||||||
contract GiftableToken {
|
contract GiftableToken {
|
||||||
|
|
||||||
|
// Implements EIP173
|
||||||
address public owner;
|
address public owner;
|
||||||
mapping(address => bool) minters;
|
|
||||||
|
// Implements Writer
|
||||||
|
mapping(address => bool) writer;
|
||||||
|
|
||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
string public name;
|
string public name;
|
||||||
@ -15,54 +18,117 @@ contract GiftableToken {
|
|||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
uint8 public decimals;
|
uint8 public decimals;
|
||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
uint256 public totalSupply;
|
|
||||||
// Implements ERC20
|
|
||||||
mapping (address => uint256) public balanceOf;
|
mapping (address => uint256) public balanceOf;
|
||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
mapping (address => mapping (address => uint256)) public allowance;
|
mapping (address => mapping (address => uint256)) public allowance;
|
||||||
|
|
||||||
|
// Implements Burner
|
||||||
|
uint256 public totalMinted;
|
||||||
|
// Implements Burner
|
||||||
|
uint256 public totalBurned;
|
||||||
|
|
||||||
|
// Implements Expire
|
||||||
|
uint256 public expires;
|
||||||
|
bool expired;
|
||||||
|
|
||||||
|
// Implements ERC20
|
||||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||||
|
// Implements ERC20
|
||||||
event TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value);
|
event TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value);
|
||||||
|
// Implements ERC20
|
||||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||||
|
|
||||||
|
// Implements Minter
|
||||||
event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value);
|
event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value);
|
||||||
|
|
||||||
constructor(string memory _name, string memory _symbol, uint8 _decimals) public {
|
// Implement Expire
|
||||||
|
event Expired(uint256 _timestamp);
|
||||||
|
|
||||||
|
// Implements Writer
|
||||||
|
event WriterAdded(address _writer);
|
||||||
|
// Implements Writer
|
||||||
|
event WriterRemoved(address _writer);
|
||||||
|
|
||||||
|
// Implements Burner
|
||||||
|
event Burn(uint256 _value);
|
||||||
|
|
||||||
|
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _expireTimestamp) {
|
||||||
owner = msg.sender;
|
owner = msg.sender;
|
||||||
name = _name;
|
name = _name;
|
||||||
symbol = _symbol;
|
symbol = _symbol;
|
||||||
decimals = _decimals;
|
decimals = _decimals;
|
||||||
minters[msg.sender] = true;
|
expires = _expireTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements ERC20
|
||||||
|
function totalSupply() public view returns (uint256) {
|
||||||
|
return totalMinted - totalBurned;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping(address => bool) writers;
|
||||||
|
|
||||||
|
// Implements Minter
|
||||||
function mintTo(address _to, uint256 _value) public returns (bool) {
|
function mintTo(address _to, uint256 _value) public returns (bool) {
|
||||||
require(minters[msg.sender]);
|
require(writers[msg.sender] || msg.sender == owner);
|
||||||
|
|
||||||
balanceOf[_to] += _value;
|
balanceOf[_to] += _value;
|
||||||
totalSupply += _value;
|
totalMinted += _value;
|
||||||
|
|
||||||
emit Mint(msg.sender, _to, _value);
|
emit Mint(msg.sender, _to, _value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMinter(address _minter) public returns (bool) {
|
// Implements Minter
|
||||||
|
// Implements ERC5679Ext20
|
||||||
|
function mint(address _to, uint256 _value, bytes calldata _data) public {
|
||||||
|
_data;
|
||||||
|
mintTo(_to, _value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Writer
|
||||||
|
function addWriter(address _minter) public returns (bool) {
|
||||||
require(msg.sender == owner);
|
require(msg.sender == owner);
|
||||||
|
|
||||||
minters[_minter] = true;
|
writers[_minter] = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeMinter(address _minter) public returns (bool) {
|
// Implements Writer
|
||||||
|
function deleteWriter(address _minter) public returns (bool) {
|
||||||
require(msg.sender == owner || msg.sender == _minter);
|
require(msg.sender == owner || msg.sender == _minter);
|
||||||
|
|
||||||
minters[_minter] = false;
|
writers[_minter] = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements Writer
|
||||||
|
function isWriter(address _minter) public view returns(bool) {
|
||||||
|
return writers[_minter] || _minter == owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Expire
|
||||||
|
function applyExpiry() public returns(uint8) {
|
||||||
|
if (expires == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (expired) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (block.timestamp >= expires) {
|
||||||
|
expired = true;
|
||||||
|
emit Expired(block.timestamp);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
function transfer(address _to, uint256 _value) public returns (bool) {
|
function transfer(address _to, uint256 _value) public returns (bool) {
|
||||||
|
require(applyExpiry() == 0);
|
||||||
require(balanceOf[msg.sender] >= _value);
|
require(balanceOf[msg.sender] >= _value);
|
||||||
balanceOf[msg.sender] -= _value;
|
balanceOf[msg.sender] -= _value;
|
||||||
balanceOf[_to] += _value;
|
balanceOf[_to] += _value;
|
||||||
@ -70,8 +136,34 @@ contract GiftableToken {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements Burner
|
||||||
|
function burn(uint256 _value) public returns (bool) {
|
||||||
|
require(msg.sender == owner, 'ERR_ACCESS');
|
||||||
|
require(balanceOf[msg.sender] >= _value, 'ERR_FUNDS');
|
||||||
|
|
||||||
|
balanceOf[msg.sender] -= _value;
|
||||||
|
totalBurned += _value;
|
||||||
|
|
||||||
|
emit Burn(_value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Burner
|
||||||
|
function burn() public returns(bool) {
|
||||||
|
return burn(balanceOf[msg.sender]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Burner
|
||||||
|
// Implements ERC5679Ext20
|
||||||
|
function burn(address _from, uint256 _value, bytes calldata _data) public {
|
||||||
|
require(msg.sender == _from, 'ERR_NOT_SELF');
|
||||||
|
_data;
|
||||||
|
burn(_value);
|
||||||
|
}
|
||||||
|
|
||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
|
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
|
||||||
|
require(applyExpiry() == 0);
|
||||||
require(allowance[_from][msg.sender] >= _value);
|
require(allowance[_from][msg.sender] >= _value);
|
||||||
require(balanceOf[_from] >= _value);
|
require(balanceOf[_from] >= _value);
|
||||||
allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value;
|
allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value;
|
||||||
@ -83,6 +175,7 @@ contract GiftableToken {
|
|||||||
|
|
||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
function approve(address _spender, uint256 _value) public returns (bool) {
|
function approve(address _spender, uint256 _value) public returns (bool) {
|
||||||
|
require(applyExpiry() == 0);
|
||||||
if (_value > 0) {
|
if (_value > 0) {
|
||||||
require(allowance[msg.sender][_spender] == 0);
|
require(allowance[msg.sender][_spender] == 0);
|
||||||
}
|
}
|
||||||
@ -95,11 +188,12 @@ contract GiftableToken {
|
|||||||
function transferOwnership(address _newOwner) public returns (bool) {
|
function transferOwnership(address _newOwner) public returns (bool) {
|
||||||
require(msg.sender == owner);
|
require(msg.sender == owner);
|
||||||
owner = _newOwner;
|
owner = _newOwner;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements EIP165
|
// Implements EIP165
|
||||||
function supportsInterface(bytes4 _sum) public returns (bool) {
|
function supportsInterface(bytes4 _sum) public pure returns (bool) {
|
||||||
if (_sum == 0xc6bb4b70) { // ERC20
|
if (_sum == 0xb61bc941) { // ERC20
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (_sum == 0x449a52f8) { // Minter
|
if (_sum == 0x449a52f8) { // Minter
|
||||||
@ -111,6 +205,15 @@ contract GiftableToken {
|
|||||||
if (_sum == 0x9493f8b2) { // EIP173
|
if (_sum == 0x9493f8b2) { // EIP173
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (_sum == 0xabe1f1f5) { // Writer
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_sum == 0xb1110c1b) { // Burner
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_sum == 0x841a0e94) { // Expire
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ SOLC = /usr/bin/solc
|
|||||||
|
|
||||||
all:
|
all:
|
||||||
$(SOLC) --bin GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.bin
|
$(SOLC) --bin GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.bin
|
||||||
truncate -s -1 GiftableToken.bin
|
|
||||||
$(SOLC) --abi GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.json
|
$(SOLC) --abi GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.json
|
||||||
|
$(SOLC) --metadata GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.metadata.json
|
||||||
$(SOLC) --bin StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.bin
|
$(SOLC) --bin StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.bin
|
||||||
truncate -s -1 StaticToken.bin
|
|
||||||
$(SOLC) --abi StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.json
|
$(SOLC) --abi StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.json
|
||||||
|
$(SOLC) --metadata StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.metadata.json
|
||||||
|
truncate -s -1 StaticToken.bin
|
||||||
|
truncate -s -1 GiftableToken.bin
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
cp -v Giftable*{json,bin} ../python/giftable_erc20_token/data/
|
cp -v *.json ../python/giftable_erc20_token/data/
|
||||||
cp -v Static*{json,bin} ../python/static_token/data/
|
cp -v *.bin ../python/giftable_erc20_token/data/
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_supply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
|
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_supply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pragma solidity >0.6.11;
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
contract StaticToken {
|
contract StaticToken {
|
||||||
|
|
||||||
@ -20,11 +20,14 @@ contract StaticToken {
|
|||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
mapping (address => mapping (address => uint256)) public allowance;
|
mapping (address => mapping (address => uint256)) public allowance;
|
||||||
|
|
||||||
|
// Implements ERC20
|
||||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||||
|
// Implements ERC20
|
||||||
event TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value);
|
event TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value);
|
||||||
|
// Implements ERC20
|
||||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||||
|
|
||||||
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _supply) public {
|
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _supply) {
|
||||||
owner = msg.sender;
|
owner = msg.sender;
|
||||||
name = _name;
|
name = _name;
|
||||||
symbol = _symbol;
|
symbol = _symbol;
|
||||||
@ -64,8 +67,8 @@ contract StaticToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implements EIP165
|
// Implements EIP165
|
||||||
function supportsInterface(bytes4 _sum) public returns (bool) {
|
function supportsInterface(bytes4 _sum) public pure returns (bool) {
|
||||||
if (_sum == 0xc6bb4b70) { // ERC20
|
if (_sum == 0xb61bc941) { // ERC20
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (_sum == 0x01ffc9a7) { // EIP165
|
if (_sum == 0x01ffc9a7) { // EIP165
|
||||||
|
Loading…
Reference in New Issue
Block a user