commit 685314c0fc0929b162890bb8927337413905e269
parent 0bc71c3815e9626428f5dbab45079f5d25429680
Author: lash <dev@holbrook.no>
Date: Mon, 19 Dec 2022 09:31:09 +0000
Add makefiles, python packaging
Diffstat:
27 files changed, 658 insertions(+), 563 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,10 @@
__pycache__
*.pyc
-egg-info/*
+*.egg-info*
solidity/*.bin
solidity/*.json
+python/dist
+js/dist
+js/node_modules
+package-lock.json
+js/settings.json
diff --git a/Makefile b/Makefile
@@ -0,0 +1,15 @@
+all: solidity js python
+
+.PHONY: js python solidity
+
+js:
+ make -C js
+
+python:
+ make -C python
+
+solidity:
+ make -C solidity install
+
+test:
+ make -C python test
diff --git a/js/Makefile b/js/Makefile
@@ -0,0 +1,3 @@
+all:
+ npm install
+ npm run build
diff --git a/js/manual_test_browser.js b/js/manual_test_browser.js
@@ -173,6 +173,7 @@ async function uiCreateToken() {
async function run(w3, generated_session) {
session = generated_session;
+ console.debug('running with session', session);
session.contentGateway = new Wala('http://localhost:8001');
const account = document.getElementById('data_account');
let s = document.createElement('span');
diff --git a/js/src/engine.js b/js/src/engine.js
@@ -26,7 +26,6 @@ async function startSession(w3, config, session, runner) {
session.name = await session.contract.methods.name().call({from: session.account});
session.symbol = await session.contract.methods.symbol().call({from: session.account});
session.supply = await session.contract.methods.totalSupply().call({from: session.account});
- console.debug('session', session);
runner(w3, session);
}
@@ -45,7 +44,6 @@ async function getTokens(w3, session, callback) {
} catch(e) {
break;
};
- console.debug('found token', token);
i++;
}
}
@@ -60,7 +58,6 @@ async function allocateToken(session, tokenId, amount) {
async function mintToken(session, tokenId, batch, recipient) {
const w3 = new Web3();
const address = await w3.utils.toChecksumAddress(recipient);
- console.log('address', address, recipient);
session.contract.methods.mintFromBatchTo(address, '0x' + tokenId, batch).send({
from: session.account,
value: 0,
@@ -116,7 +113,6 @@ async function isMintAvailable(session, tokenId, batch) {
}
async function toToken(session, tokenId, tokenContent) {
- console.log('process token content', tokenContent);
if (tokenId.substring(0, 2) == '0x') {
tokenId = tokenId.substring(2);
}
@@ -126,7 +122,6 @@ async function toToken(session, tokenId, tokenContent) {
}
const v = parseInt(tokenContent.substring(0, 2), 16);
- console.debug('vvv', v);
let data = {
tokenId: tokenId,
minted: false,
@@ -180,7 +175,6 @@ async function toToken(session, tokenId, tokenContent) {
}
async function getTokenChainData(session, tokenId) {
- console.log('query for', tokenId);
const v = await session.contract.methods.mintedToken('0x' + tokenId).call({from: session.account});
const mintedToken = await toToken(session, tokenId, v);
diff --git a/python/MANIFEST.in b/python/MANIFEST.in
@@ -0,0 +1 @@
+include craft_nft/data/* *requirements.txt
diff --git a/python/Makefile b/python/Makefile
@@ -0,0 +1,2 @@
+all:
+ python setup.py sdist
diff --git a/python/craft_nft/__init__.py b/python/craft_nft/__init__.py
@@ -0,0 +1,2 @@
+# local imports
+from .nft import CraftNFT
diff --git a/python/eth_craft_nft/data/CraftNFT.bin b/python/craft_nft/data/CraftNFT.bin
diff --git a/python/eth_craft_nft/data/CraftNFT.json b/python/craft_nft/data/CraftNFT.json
diff --git a/python/eth_craft_nft/error.py b/python/craft_nft/error.py
diff --git a/python/craft_nft/eth.py b/python/craft_nft/eth.py
@@ -0,0 +1,8 @@
+# standard imports
+import aenum
+
+# external imports
+from chainlib.eth.contract import ABIContractType
+
+aenum.extend_enum(ABIContractType, 'UINT48', 'uint48')
+
diff --git a/python/craft_nft/nft.py b/python/craft_nft/nft.py
@@ -0,0 +1,276 @@
+# standard imports
+import os
+import logging
+
+# external imports
+from chainlib.eth.tx import TxFormat
+from eth_erc721 import ERC721
+from hexathon import add_0x
+from hexathon import strip_0x
+from chainlib.eth.contract import ABIContractEncoder
+from chainlib.eth.contract import ABIContractDecoder
+from chainlib.eth.contract import abi_decode_single
+from chainlib.jsonrpc import JSONRPCRequest
+from chainlib.eth.constant import ZERO_ADDRESS
+from chainlib.eth.constant import ZERO_CONTENT
+from chainlib.eth.address import to_checksum_address
+
+# local imports
+from .error import InvalidBatchError
+from .eth import ABIContractType
+
+moddir = os.path.dirname(__file__)
+datadir = os.path.join(moddir, 'data')
+
+INVALID_BATCH = (2**256)-1
+
+logg = logging.getLogger(__name__)
+
+class TokenSpec:
+
+ def __init__(self, count, cursor):
+ self.count = count
+ self.cursor = cursor
+
+
+ def __str__(self):
+ return '{} / {}'.format(self.cursor, self.count)
+
+
+class MintedToken:
+
+ def __init__(self, owner_address=ZERO_ADDRESS, token_id=None, batched=False, minted=False):
+ self.minted = minted
+ self.batched = batched
+ self.owner = owner_address
+ self.index = 0
+ self.batch = 0
+ self.token_id = token_id
+
+
+ def __str__(self):
+ owner = to_checksum_address(self.owner)
+ if self.batched:
+ return '{} owned {}'.format(
+ self.token_id,
+ owner,
+ )
+ return '{} batch {} index {} owned by {}'.format(
+ self.token_id,
+ self.batch,
+ self.index,
+ owner,
+ )
+
+
+class CraftNFT(ERC721):
+
+ __abi = None
+ __bytecode = None
+
+ @staticmethod
+ def abi():
+ if CraftNFT.__abi == None:
+ f = open(os.path.join(datadir, 'CraftNFT.json'), 'r')
+ CraftNFT.__abi = json.load(f)
+ f.close()
+ return CraftNFT.__abi
+
+
+ @staticmethod
+ def bytecode():
+ if CraftNFT.__bytecode == None:
+ f = open(os.path.join(datadir, 'CraftNFT.bin'))
+ CraftNFT.__bytecode = f.read()
+ f.close()
+ return CraftNFT.__bytecode
+
+
+ @staticmethod
+ def gas(code=None):
+ return 4000000
+
+
+ def constructor(self, sender_address, name, symbol, tx_format=TxFormat.JSONRPC):
+ code = CraftNFT.bytecode()
+ enc = ABIContractEncoder()
+ enc.string(name)
+ enc.string(symbol)
+ code += enc.get()
+ tx = self.template(sender_address, None, use_nonce=True)
+ tx = self.set_code(tx, code)
+ return self.finalize(tx, tx_format)
+
+
+ def allocate(self, contract_address, sender_address, token_id, amount=0, tx_format=TxFormat.JSONRPC):
+ enc = ABIContractEncoder()
+ enc.method('allocate')
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.UINT48)
+ enc.bytes32(token_id)
+ enc.uintn(amount, 48)
+ 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 token_at(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None):
+ j = JSONRPCRequest(id_generator)
+ o = j.template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('tokens')
+ enc.typ(ABIContractType.UINT256)
+ enc.uint256(idx)
+ 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 batch_of(self, conn, contract_address, token_id, super_index, sender_address=ZERO_ADDRESS, id_generator=None):
+ i = 0
+ c = 0
+
+ while True:
+ o = self.get_token_spec(contract_address, token_id, i, sender_address=sender_address)
+ try:
+ r = conn.do(o)
+ except:
+ break
+ spec = self.parse_token_spec(r)
+ c += spec.count
+ if super_index < c:
+ return i
+ i += 1
+
+ raise ValueError(super_index)
+
+
+ def get_token_spec(self, contract_address, token_id, batch, sender_address=ZERO_ADDRESS, id_generator=None):
+ j = JSONRPCRequest(id_generator)
+ o = j.template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('token')
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.UINT256)
+ enc.bytes32(token_id)
+ enc.uint256(batch)
+ 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 get_token(self, contract_address, token_id, sender_address=ZERO_ADDRESS, id_generator=None):
+ j = JSONRPCRequest(id_generator)
+ o = j.template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('mintedToken')
+ enc.typ(ABIContractType.BYTES32)
+ enc.bytes32(token_id)
+ 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 get_digest(self, contract_address, token_id, sender_address=ZERO_ADDRESS, id_generator=None):
+ j = JSONRPCRequest(id_generator)
+ o = j.template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('getDigest')
+ enc.typ(ABIContractType.BYTES32)
+ enc.bytes32(token_id)
+ 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 mint_to(self, contract_address, sender_address, recipient, token_id, batch, index=None, tx_format=TxFormat.JSONRPC):
+ enc = ABIContractEncoder()
+
+ if index != None:
+ enc.method('mintExactFromBatchTo')
+ enc.typ(ABIContractType.ADDRESS)
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.UINT16)
+ enc.typ(ABIContractType.UINT48)
+ enc.address(recipient)
+ enc.bytes32(token_id)
+ enc.uintn(batch, 16)
+ enc.uintn(index, 48)
+ data = enc.get()
+ else:
+ enc.method('mintFromBatchTo')
+ enc.typ(ABIContractType.ADDRESS)
+ enc.typ(ABIContractType.BYTES32)
+ enc.typ(ABIContractType.UINT16)
+ enc.address(recipient)
+ enc.bytes32(token_id)
+ enc.uintn(batch, 16)
+ 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 to_index_id(self, token_id, batch, index):
+
+
+ @classmethod
+ def parse_batch_of(self, v):
+ r = abi_decode_single(ABIContractType.UINT256, v)
+ if r == INVALID_BATCH:
+ raise InvalidBatchError()
+ return r
+
+
+ @classmethod
+ def parse_token_spec(self, v):
+ v = strip_0x(v)
+ d = ABIContractDecoder()
+ d.typ(ABIContractType.UINT48)
+ d.typ(ABIContractType.UINT48)
+ d.val(v[:64])
+ d.val(v[64:128])
+ r = d.decode()
+ return TokenSpec(r[0], r[1])
+
+ @classmethod
+ def parse_token(self, v, token_id):
+ v = strip_0x(v)
+ if v == strip_0x(ZERO_CONTENT):
+ return MintedToken()
+
+ token_id = strip_0x(token_id)
+ c = v[:2]
+ addr = v[24:]
+ if int(c, 16) & 0x40 > 0:
+ return MintedToken(addr, token_id=token_id, batched=True, minted=True)
+
+ o = MintedToken(addr, minted=True)
+ o.batch = int(token_id[48:52], 16)
+ o.index = int(token_id[52:64], 16)
+ o.token_id = token_id[:48] + v[2:18]
+ return o
diff --git a/python/craft_nft/runnable/dump.py b/python/craft_nft/runnable/dump.py
@@ -0,0 +1,143 @@
+"""Deploys badge NFT
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+"""
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# standard imports
+import sys
+import os
+import json
+import argparse
+import logging
+import time
+from enum import Enum
+
+# external imports
+import chainlib.eth.cli
+from chainlib.chain import ChainSpec
+from chainlib.eth.constant import ZERO_ADDRESS
+from chainlib.settings import ChainSettings
+from chainlib.eth.settings import process_settings
+from chainlib.eth.cli.arg import Arg
+from chainlib.eth.cli.arg import ArgFlag
+from chainlib.eth.cli.arg import process_args
+from chainlib.eth.cli.log import process_log
+from chainlib.eth.cli.config import Config
+from chainlib.eth.cli.config import process_config
+from hexathon import strip_0x
+from hexathon import add_0x
+
+# local imports
+from craft_nft import CraftNFT
+
+logg = logging.getLogger()
+
+def process_config_local(config, arg, args, flags):
+ contract = None
+ try:
+ contract = config.get('_EXEC_ADDRESS')
+ except KeyError:
+ pass
+
+ if contract == None:
+ address = config.get('_POSARG')
+ if address:
+ contract = add_0x(address)
+ else:
+ contract = stdin_arg()
+
+ config.add(contract, '_CONTRACT', False)
+
+ config.add(100000, '_FEE_LIMIT', True)
+ return config
+
+
+arg_flags = ArgFlag()
+arg = Arg(arg_flags)
+
+flags = arg_flags.STD_READ | arg_flags.EXEC | arg_flags.TAB
+
+argparser = chainlib.eth.cli.ArgumentParser()
+argparser = process_args(argparser, arg, flags)
+argparser.add_argument('contract_address', type=str, help='Token contract address (may also be specified by -e)')
+args = argparser.parse_args()
+
+logg = process_log(args, logg)
+
+config = Config()
+config = process_config(config, arg, args, flags, positional_name='contract_address')
+config = process_config_local(config, arg, args, flags)
+logg.debug('config loaded:\n{}'.format(config))
+
+settings = ChainSettings()
+settings = process_settings(settings, config)
+logg.debug('settings loaded:\n{}'.format(settings))
+
+
+def render_token_batches(c, conn, token_address, token_id, w=sys.stdout):
+ i = 0
+ while True:
+ o = c.get_token_spec(token_address, token_id, i)
+ r = None
+ try:
+ r = conn.do(o)
+ except:
+ break
+ spec = c.parse_token_spec(r)
+
+ for j in range(spec.cursor):
+ cursor_hex = j.to_bytes(6, byteorder='big').hex()
+ batch_hex = i.to_bytes(2, byteorder='big').hex()
+ token_id_indexed = token_id[:48] + batch_hex + cursor_hex
+ render_token_mint(c, conn, token_address, token_id_indexed, w=w)
+
+ i += 1
+
+
+def render_token_mint(c, conn, token_address, token_id, w=sys.stdout):
+ o = c.get_token(token_address, token_id)
+ r = conn.do(o)
+ token = c.parse_token(r, token_id)
+ if token.minted:
+ w.write('token {}\n'.format(token))
+
+
+def render_token(c, conn, token_address, token_id, w=sys.stdout):
+ token_id = strip_0x(token_id)
+ o = c.get_token_spec(token_address, token_id, 0)
+ r = conn.do(o)
+ spec = c.parse_token_spec(r)
+ if spec.count > 0:
+ return render_token_batches(c, conn, token_address, token_id, w=sys.stdout)
+
+ return render_token_mint(c, conn, token_address, token_id, w=w)
+
+
+def main():
+ token_address = config.get('_CONTRACT')
+ conn = settings.get('CONN')
+ c = CraftNFT(
+ chain_spec=settings.get('CHAIN_SPEC'),
+ gas_oracle=settings.get('GAS_ORACLE'),
+ )
+
+ outkeys = config.get('_OUTARG')
+
+ i = 0
+ while True:
+ o = c.token_at(token_address, i)
+ r = None
+ try:
+ r = conn.do(o)
+ except:
+ break
+ render_token(c, conn, token_address, r)
+ i += 1
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/craft_nft/runnable/publish.py b/python/craft_nft/runnable/publish.py
@@ -0,0 +1,127 @@
+"""Deploys badge NFT
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+"""
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# standard imports
+import sys
+import os
+import json
+import argparse
+import logging
+import time
+from enum import Enum
+
+# external imports
+import chainlib.eth.cli
+from chainlib.chain import ChainSpec
+from chainlib.eth.constant import ZERO_ADDRESS
+from chainlib.settings import ChainSettings
+from chainlib.eth.settings import process_settings
+from chainlib.eth.cli.arg import Arg
+from chainlib.eth.cli.arg import ArgFlag
+from chainlib.eth.cli.arg import process_args
+from chainlib.eth.cli.log import process_log
+from chainlib.eth.cli.config import Config
+from chainlib.eth.cli.config import process_config
+
+# local imports
+from craft_nft import CraftNFT
+
+logg = logging.getLogger()
+
+
+def process_config_local(config, arg, args, flags):
+ config.add(args.name, '_TOKEN_NAME', False)
+ config.add(args.symbol, '_TOKEN_SYMBOL', False)
+ return config
+
+
+arg_flags = ArgFlag()
+arg = Arg(arg_flags)
+flags = arg_flags.STD_WRITE | arg_flags.WALLET | arg_flags.CREATE | arg_flags.VALUE | arg_flags.TAB
+
+argparser = chainlib.eth.cli.ArgumentParser()
+argparser.add_argument('--name', type=str, required=True, help='Token name')
+argparser.add_argument('--symbol', type=str, required=True, help='Token symbol')
+argparser = process_args(argparser, arg, flags)
+args = argparser.parse_args(sys.argv[1:])
+
+logg = process_log(args, logg)
+
+config = Config()
+config = process_config(config, arg, args, flags)
+config = process_config_local(config, arg, args, flags)
+logg.debug('config loaded:\n{}'.format(config))
+
+settings = ChainSettings()
+settings = process_settings(settings, config)
+logg.debug('settings loaded:\n{}'.format(settings))
+
+
+#arg_flags = chainlib.eth.cli.argflag_std_write
+#argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
+#argparser.add_argument('--name', dest='token_name', type=str, help='Token name')
+#argparser.add_argument('--symbol', dest='token_symbol', type=str, help='Token symbol')
+#args = argparser.parse_args()
+#
+#extra_args = {
+# 'token_name': None,
+# 'token_symbol': None,
+# }
+#config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=CraftNFT.gas())
+
+#wallet = chainlib.eth.cli.Wallet()
+#wallet.from_config(config)
+
+#rpc = chainlib.eth.cli.Rpc(wallet=wallet)
+#conn = rpc.connect_by_config(config)
+
+#chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
+
+
+def main():
+ #signer = rpc.get_signer()
+ #signer_address = rpc.get_sender_address()
+
+ token_name = config.get('_TOKEN_NAME')
+ token_symbol = config.get('_TOKEN_SYMBOL')
+ conn = settings.get('CONN')
+
+# gas_oracle = rpc.get_gas_oracle()
+# nonce_oracle = rpc.get_nonce_oracle()
+
+ c = CraftNFT(
+ settings.get('CHAIN_SPEC'),
+ signer=settings.get('SIGNER'),
+ gas_oracle=settings.get('FEE_ORACLE'),
+ nonce_oracle=settings.get('NONCE_ORACLE')
+ )
+
+ (tx_hash_hex, o) = c.constructor(
+ settings.get('SENDER_ADDRESS'),
+ token_name,
+ token_symbol,
+ )
+ if config.get('_RPC_SEND'):
+ conn.do(o)
+ if config.true('_WAIT'):
+ r = conn.wait(tx_hash_hex)
+ if r['status'] == 0:
+ sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
+ sys.exit(1)
+ # TODO: pass through translator for keys (evm tester uses underscore instead of camelcase)
+ address = r['contractAddress']
+
+ print(address)
+ else:
+ print(tx_hash_hex)
+ else:
+ print(o)
+
+if __name__ == '__main__':
+ main()
diff --git a/python/eth_craft_nft/__init__.py b/python/eth_craft_nft/__init__.py
@@ -1 +0,0 @@
-from .nft import CraftNFT
diff --git a/python/eth_craft_nft/nft.py b/python/eth_craft_nft/nft.py
@@ -1,276 +0,0 @@
-# standard imports
-import os
-import logging
-
-# external imports
-from chainlib.eth.tx import TxFormat
-from eth_erc721 import ERC721
-from hexathon import add_0x
-from hexathon import strip_0x
-from chainlib.eth.contract import ABIContractEncoder
-from chainlib.eth.contract import ABIContractDecoder
-from chainlib.eth.contract import ABIContractType
-from chainlib.eth.contract import abi_decode_single
-from chainlib.jsonrpc import JSONRPCRequest
-from chainlib.eth.constant import ZERO_ADDRESS
-from chainlib.eth.constant import ZERO_CONTENT
-from chainlib.eth.address import to_checksum_address
-
-# local imports
-from eth_craft_nft.error import InvalidBatchError
-
-moddir = os.path.dirname(__file__)
-datadir = os.path.join(moddir, 'data')
-
-INVALID_BATCH = (2**256)-1
-
-logg = logging.getLogger(__name__)
-
-class TokenSpec:
-
- def __init__(self, count, cursor):
- self.count = count
- self.cursor = cursor
-
-
- def __str__(self):
- return '{} / {}'.format(self.cursor, self.count)
-
-
-class MintedToken:
-
- def __init__(self, owner_address=ZERO_ADDRESS, token_id=None, batched=False, minted=False):
- self.minted = minted
- self.batched = batched
- self.owner = owner_address
- self.index = 0
- self.batch = 0
- self.token_id = token_id
-
-
- def __str__(self):
- owner = to_checksum_address(self.owner)
- if self.batched:
- return '{} owned {}'.format(
- self.token_id,
- owner,
- )
- return '{} batch {} index {} owned by {}'.format(
- self.token_id,
- self.batch,
- self.index,
- owner,
- )
-
-
-class CraftNFT(ERC721):
-
- __abi = None
- __bytecode = None
-
- @staticmethod
- def abi():
- if CraftNFT.__abi == None:
- f = open(os.path.join(datadir, 'CraftNFT.json'), 'r')
- CraftNFT.__abi = json.load(f)
- f.close()
- return CraftNFT.__abi
-
-
- @staticmethod
- def bytecode():
- if CraftNFT.__bytecode == None:
- f = open(os.path.join(datadir, 'CraftNFT.bin'))
- CraftNFT.__bytecode = f.read()
- f.close()
- return CraftNFT.__bytecode
-
-
- @staticmethod
- def gas(code=None):
- return 4000000
-
-
- def constructor(self, sender_address, name, symbol, tx_format=TxFormat.JSONRPC):
- code = CraftNFT.bytecode()
- enc = ABIContractEncoder()
- enc.string(name)
- enc.string(symbol)
- code += enc.get()
- tx = self.template(sender_address, None, use_nonce=True)
- tx = self.set_code(tx, code)
- return self.finalize(tx, tx_format)
-
-
- def allocate(self, contract_address, sender_address, token_id, amount=0, tx_format=TxFormat.JSONRPC):
- enc = ABIContractEncoder()
- enc.method('allocate')
- enc.typ(ABIContractType.BYTES32)
- enc.typ(ABIContractType.UINT48)
- enc.bytes32(token_id)
- enc.uintn(amount, 48)
- 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 token_at(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None):
- j = JSONRPCRequest(id_generator)
- o = j.template()
- o['method'] = 'eth_call'
- enc = ABIContractEncoder()
- enc.method('tokens')
- enc.typ(ABIContractType.UINT256)
- enc.uint256(idx)
- 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 batch_of(self, conn, contract_address, token_id, super_index, sender_address=ZERO_ADDRESS, id_generator=None):
- i = 0
- c = 0
-
- while True:
- o = self.get_token_spec(contract_address, token_id, i, sender_address=sender_address)
- try:
- r = conn.do(o)
- except:
- break
- spec = self.parse_token_spec(r)
- c += spec.count
- if super_index < c:
- return i
- i += 1
-
- raise ValueError(super_index)
-
-
- def get_token_spec(self, contract_address, token_id, batch, sender_address=ZERO_ADDRESS, id_generator=None):
- j = JSONRPCRequest(id_generator)
- o = j.template()
- o['method'] = 'eth_call'
- enc = ABIContractEncoder()
- enc.method('token')
- enc.typ(ABIContractType.BYTES32)
- enc.typ(ABIContractType.UINT256)
- enc.bytes32(token_id)
- enc.uint256(batch)
- 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 get_token(self, contract_address, token_id, sender_address=ZERO_ADDRESS, id_generator=None):
- j = JSONRPCRequest(id_generator)
- o = j.template()
- o['method'] = 'eth_call'
- enc = ABIContractEncoder()
- enc.method('mintedToken')
- enc.typ(ABIContractType.BYTES32)
- enc.bytes32(token_id)
- 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 get_digest(self, contract_address, token_id, sender_address=ZERO_ADDRESS, id_generator=None):
- j = JSONRPCRequest(id_generator)
- o = j.template()
- o['method'] = 'eth_call'
- enc = ABIContractEncoder()
- enc.method('getDigest')
- enc.typ(ABIContractType.BYTES32)
- enc.bytes32(token_id)
- 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 mint_to(self, contract_address, sender_address, recipient, token_id, batch, index=None, tx_format=TxFormat.JSONRPC):
- enc = ABIContractEncoder()
-
- if index != None:
- enc.method('mintExactFromBatchTo')
- enc.typ(ABIContractType.ADDRESS)
- enc.typ(ABIContractType.BYTES32)
- enc.typ(ABIContractType.UINT16)
- enc.typ(ABIContractType.UINT48)
- enc.address(recipient)
- enc.bytes32(token_id)
- enc.uintn(batch, 16)
- enc.uintn(index, 48)
- data = enc.get()
- else:
- enc.method('mintFromBatchTo')
- enc.typ(ABIContractType.ADDRESS)
- enc.typ(ABIContractType.BYTES32)
- enc.typ(ABIContractType.UINT16)
- enc.address(recipient)
- enc.bytes32(token_id)
- enc.uintn(batch, 16)
- 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 to_index_id(self, token_id, batch, index):
-
-
- @classmethod
- def parse_batch_of(self, v):
- r = abi_decode_single(ABIContractType.UINT256, v)
- if r == INVALID_BATCH:
- raise InvalidBatchError()
- return r
-
-
- @classmethod
- def parse_token_spec(self, v):
- v = strip_0x(v)
- d = ABIContractDecoder()
- d.typ(ABIContractType.UINT48)
- d.typ(ABIContractType.UINT48)
- d.val(v[:64])
- d.val(v[64:128])
- r = d.decode()
- return TokenSpec(r[0], r[1])
-
- @classmethod
- def parse_token(self, v, token_id):
- v = strip_0x(v)
- if v == strip_0x(ZERO_CONTENT):
- return MintedToken()
-
- token_id = strip_0x(token_id)
- c = v[:2]
- addr = v[24:]
- if int(c, 16) & 0x40 > 0:
- return MintedToken(addr, token_id=token_id, batched=True, minted=True)
-
- o = MintedToken(addr, minted=True)
- o.batch = int(token_id[48:52], 16)
- o.index = int(token_id[52:64], 16)
- o.token_id = token_id[:48] + v[2:18]
- return o
diff --git a/python/eth_craft_nft/runnable/dump.py b/python/eth_craft_nft/runnable/dump.py
@@ -1,141 +0,0 @@
-"""Deploys badge NFT
-
-.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
-.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
-
-"""
-
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# standard imports
-import sys
-import os
-import json
-import argparse
-import logging
-import time
-from enum import Enum
-
-# external imports
-import chainlib.eth.cli
-from chainlib.chain import ChainSpec
-from chainlib.eth.constant import ZERO_ADDRESS
-from chainlib.settings import ChainSettings
-from chainlib.eth.settings import process_settings
-from chainlib.eth.cli.arg import Arg
-from chainlib.eth.cli.arg import ArgFlag
-from chainlib.eth.cli.arg import process_args
-from chainlib.eth.cli.log import process_log
-from chainlib.eth.cli.config import Config
-from chainlib.eth.cli.config import process_config
-from hexathon import strip_0x
-from hexathon import add_0x
-
-# local imports
-from eth_craft_nft import CraftNFT
-
-logg = logging.getLogger()
-
-def process_config_local(config, arg, args, flags):
- contract = None
- try:
- contract = config.get('_EXEC_ADDRESS')
- except KeyError:
- pass
-
- if contract == None:
- address = config.get('_POSARG')
- if address:
- contract = add_0x(address)
- else:
- contract = stdin_arg()
-
- config.add(contract, '_CONTRACT', False)
- return config
-
-
-arg_flags = ArgFlag()
-arg = Arg(arg_flags)
-
-flags = arg_flags.STD_READ | arg_flags.EXEC | arg_flags.TAB
-
-argparser = chainlib.eth.cli.ArgumentParser()
-argparser = process_args(argparser, arg, flags)
-argparser.add_argument('contract_address', type=str, help='Token contract address (may also be specified by -e)')
-args = argparser.parse_args()
-
-logg = process_log(args, logg)
-
-config = Config()
-config = process_config(config, arg, args, flags, positional_name='contract_address')
-config = process_config_local(config, arg, args, flags)
-logg.debug('config loaded:\n{}'.format(config))
-
-settings = ChainSettings()
-settings = process_settings(settings, config)
-logg.debug('settings loaded:\n{}'.format(settings))
-
-
-def render_token_batches(c, conn, token_address, token_id, w=sys.stdout):
- i = 0
- while True:
- o = c.get_token_spec(token_address, token_id, i)
- r = None
- try:
- r = conn.do(o)
- except:
- break
- spec = c.parse_token_spec(r)
-
- for j in range(spec.cursor):
- cursor_hex = j.to_bytes(6, byteorder='big').hex()
- batch_hex = i.to_bytes(2, byteorder='big').hex()
- token_id_indexed = token_id[:48] + batch_hex + cursor_hex
- render_token_mint(c, conn, token_address, token_id_indexed, w=w)
-
- i += 1
-
-
-def render_token_mint(c, conn, token_address, token_id, w=sys.stdout):
- o = c.get_token(token_address, token_id)
- r = conn.do(o)
- token = c.parse_token(r, token_id)
- if token.minted:
- w.write('token {}\n'.format(token))
-
-
-def render_token(c, conn, token_address, token_id, w=sys.stdout):
- token_id = strip_0x(token_id)
- o = c.get_token_spec(token_address, token_id, 0)
- r = conn.do(o)
- spec = c.parse_token_spec(r)
- if spec.count > 0:
- return render_token_batches(c, conn, token_address, token_id, w=sys.stdout)
-
- return render_token_mint(c, conn, token_address, token_id, w=w)
-
-
-def main():
- token_address = config.get('_CONTRACT')
- conn = settings.get('CONN')
- c = CraftNFT(
- chain_spec=settings.get('CHAIN_SPEC'),
- gas_oracle=settings.get('GAS_ORACLE'),
- )
-
- outkeys = config.get('_OUTARG')
-
- i = 0
- while True:
- o = c.token_at(token_address, i)
- r = None
- try:
- r = conn.do(o)
- except:
- break
- render_token(c, conn, token_address, r)
- i += 1
-
-
-if __name__ == '__main__':
- main()
diff --git a/python/eth_craft_nft/runnable/publish.py b/python/eth_craft_nft/runnable/publish.py
@@ -1,127 +0,0 @@
-"""Deploys badge NFT
-
-.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
-.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
-
-"""
-
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# standard imports
-import sys
-import os
-import json
-import argparse
-import logging
-import time
-from enum import Enum
-
-# external imports
-import chainlib.eth.cli
-from chainlib.chain import ChainSpec
-from chainlib.eth.constant import ZERO_ADDRESS
-from chainlib.settings import ChainSettings
-from chainlib.eth.settings import process_settings
-from chainlib.eth.cli.arg import Arg
-from chainlib.eth.cli.arg import ArgFlag
-from chainlib.eth.cli.arg import process_args
-from chainlib.eth.cli.log import process_log
-from chainlib.eth.cli.config import Config
-from chainlib.eth.cli.config import process_config
-
-# local imports
-from eth_craft_nft import CraftNFT
-
-logg = logging.getLogger()
-
-
-def process_config_local(config, arg, args, flags):
- config.add(args.name, '_TOKEN_NAME', False)
- config.add(args.symbol, '_TOKEN_SYMBOL', False)
- return config
-
-
-arg_flags = ArgFlag()
-arg = Arg(arg_flags)
-flags = arg_flags.STD_WRITE | arg_flags.WALLET | arg_flags.CREATE | arg_flags.VALUE | arg_flags.TAB
-
-argparser = chainlib.eth.cli.ArgumentParser()
-argparser.add_argument('--name', type=str, required=True, help='Token name')
-argparser.add_argument('--symbol', type=str, required=True, help='Token symbol')
-argparser = process_args(argparser, arg, flags)
-args = argparser.parse_args(sys.argv[1:])
-
-logg = process_log(args, logg)
-
-config = Config()
-config = process_config(config, arg, args, flags)
-config = process_config_local(config, arg, args, flags)
-logg.debug('config loaded:\n{}'.format(config))
-
-settings = ChainSettings()
-settings = process_settings(settings, config)
-logg.debug('settings loaded:\n{}'.format(settings))
-
-
-#arg_flags = chainlib.eth.cli.argflag_std_write
-#argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
-#argparser.add_argument('--name', dest='token_name', type=str, help='Token name')
-#argparser.add_argument('--symbol', dest='token_symbol', type=str, help='Token symbol')
-#args = argparser.parse_args()
-#
-#extra_args = {
-# 'token_name': None,
-# 'token_symbol': None,
-# }
-#config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=CraftNFT.gas())
-
-#wallet = chainlib.eth.cli.Wallet()
-#wallet.from_config(config)
-
-#rpc = chainlib.eth.cli.Rpc(wallet=wallet)
-#conn = rpc.connect_by_config(config)
-
-#chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
-
-
-def main():
- #signer = rpc.get_signer()
- #signer_address = rpc.get_sender_address()
-
- token_name = config.get('_TOKEN_NAME')
- token_symbol = config.get('_TOKEN_SYMBOL')
- conn = settings.get('CONN')
-
-# gas_oracle = rpc.get_gas_oracle()
-# nonce_oracle = rpc.get_nonce_oracle()
-
- c = CraftNFT(
- settings.get('CHAIN_SPEC'),
- signer=settings.get('SIGNER'),
- gas_oracle=settings.get('FEE_ORACLE'),
- nonce_oracle=settings.get('NONCE_ORACLE')
- )
-
- (tx_hash_hex, o) = c.constructor(
- settings.get('SENDER_ADDRESS'),
- token_name,
- token_symbol,
- )
- if config.get('_RPC_SEND'):
- conn.do(o)
- if config.true('_WAIT'):
- r = conn.wait(tx_hash_hex)
- if r['status'] == 0:
- sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
- sys.exit(1)
- # TODO: pass through translator for keys (evm tester uses underscore instead of camelcase)
- address = r['contractAddress']
-
- print(address)
- else:
- print(tx_hash_hex)
- else:
- print(o)
-
-if __name__ == '__main__':
- main()
diff --git a/python/requirements.txt b/python/requirements.txt
@@ -1 +1,2 @@
eth-erc721~=0.0.4
+aenum~=3.1.11
diff --git a/python/setup.cfg b/python/setup.cfg
@@ -0,0 +1,37 @@
+[metadata]
+name = craft-nft
+version = 0.0.1
+description = A standalone NFT implementation for real-world arts and crafts assets
+author = Louis Holbrook
+author_email = dev@holbrook.no
+url = https://git.defalslfy.org/craft-nft,git
+keywords =
+ dlt
+ blockchain
+ cryptocurrency
+ ethereum
+classifiers =
+ Programming Language :: Python :: 3
+ Operating System :: OS Independent
+ Development Status :: 3 - Alpha
+ Topic :: Software Development :: Libraries
+ Environment :: Console
+ Intended Audience :: Developers
+ License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
+ Topic :: Internet
+# Topic :: Blockchain :: EVM
+license = AGPLv3+
+licence_files =
+ LICENSE
+
+[options]
+include_package_data = True
+python_requires = >= 3.7
+packages =
+ craft_nft
+ craft_nft.runnable
+
+[options.entry_points]
+console_scripts =
+ craftnft-publish = craft_nft.runnable.publish:main
+ craftnft-dump = craft_nft.runnable.dump:main
diff --git a/python/setup.py b/python/setup.py
@@ -0,0 +1,26 @@
+from setuptools import setup
+import os
+
+
+requirements = []
+f = open('requirements.txt', 'r')
+while True:
+ l = f.readline()
+ if l == '':
+ break
+ requirements.append(l.rstrip())
+f.close()
+
+test_requirements = []
+f = open('test_requirements.txt', 'r')
+while True:
+ l = f.readline()
+ if l == '':
+ break
+ test_requirements.append(l.rstrip())
+f.close()
+
+setup(
+ install_requires=requirements,
+ tests_require=test_requirements,
+ )
diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py
@@ -27,12 +27,12 @@ from hexathon import (
)
from chainlib.eth.tx import TxFormat
from chainlib.eth.contract import ABIContractEncoder
-from chainlib.eth.contract import ABIContractType
# local imports
-from eth_craft_nft import CraftNFT
-from eth_craft_nft.error import InvalidBatchError
+from craft_nft import CraftNFT
+from craft_nft.error import InvalidBatchError
+from craft_nft.eth import ABIContractType
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
diff --git a/python/tests/test_numbered.py b/python/tests/test_numbered.py
@@ -31,8 +31,8 @@ from chainlib.eth.contract import ABIContractType
# local imports
-from eth_craft_nft import CraftNFT
-from eth_craft_nft.error import InvalidBatchError
+from craft_nft import CraftNFT
+from craft_nft.error import InvalidBatchError
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
diff --git a/python/tests/test_spec.py b/python/tests/test_spec.py
@@ -31,8 +31,8 @@ from chainlib.eth.contract import ABIContractType
# local imports
-from eth_craft_nft import CraftNFT
-from eth_craft_nft.error import InvalidBatchError
+from craft_nft import CraftNFT
+from craft_nft.error import InvalidBatchError
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
@@ -76,7 +76,6 @@ class Test(EthTesterCase):
r = self.rpc.do(o)
spec = c.parse_token_spec(r)
self.assertEqual(spec.count, 3)
- self.assertEqual(spec.cumulative_count, 5)
self.assertEqual(spec.cursor, 1)
diff --git a/python/tests/test_supply.py b/python/tests/test_supply.py
@@ -30,8 +30,8 @@ from chainlib.eth.contract import ABIContractType
# local imports
-from eth_craft_nft import CraftNFT
-from eth_craft_nft.error import InvalidBatchError
+from craft_nft import CraftNFT
+from craft_nft.error import InvalidBatchError
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -6,7 +6,7 @@ all:
$(SOLC) --abi CraftNFT.sol --evm-version byzantium | awk 'NR>3' > CraftNFT.json
install-py: all
- cp -v CraftNFT*{json,bin} ../python/eth_craft_nft/data/
+ cp -v CraftNFT*{json,bin} ../python/craft_nft/data/
install-js: all
cp -v CraftNFT*{json,bin} ../js/contract/