commit 4acd3076355f41a4e5629b6d659f84e9dda5c3e1
parent 462a865ddaf639ac3361223c150abb729ef050a5
Author: lash <dev@holbrook.no>
Date: Sat, 17 Dec 2022 23:13:47 +0000
Add dump tool
Diffstat:
2 files changed, 235 insertions(+), 2 deletions(-)
diff --git a/python/eth_craft_nft/nft.py b/python/eth_craft_nft/nft.py
@@ -1,15 +1,20 @@
# 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
@@ -19,6 +24,44 @@ datadir = os.path.join(moddir, 'data')
INVALID_BATCH = (2**256)-1
+logg = logging.getLogger(__name__)
+
+class TokenSpec:
+
+ def __init__(self, count, cumulative_count, cursor):
+ self.count = count
+ self.cumulative_count = cumulative_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):
@@ -74,6 +117,24 @@ class CraftNFT(ERC721):
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, contract_address, token_id, super_index, start_at=0, max_batches=0, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
@@ -95,7 +156,7 @@ class CraftNFT(ERC721):
return o
- def get_token_spec_raw(self, contract_address, token_id, batch, sender_address=ZERO_ADDRESS, id_generator=None):
+ 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'
@@ -114,7 +175,7 @@ class CraftNFT(ERC721):
return o
- def get_token_raw(self, contract_address, token_id, sender_address=ZERO_ADDRESS, id_generator=None):
+ 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'
@@ -170,3 +231,35 @@ class CraftNFT(ERC721):
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.typ(ABIContractType.UINT48)
+ d.val(v[:64])
+ d.val(v[64:128])
+ d.val(v[128:192])
+ r = d.decode()
+ return TokenSpec(r[0], r[1], r[2])
+
+ @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.index = int(token_id[59:], 16)
+ o.batch = int(token_id[54:59], 16)
+ o.token_id = token_id[:54] + v[2:12]
+ return o
diff --git a/python/eth_craft_nft/runnable/dump.py b/python/eth_craft_nft/runnable/dump.py
@@ -0,0 +1,140 @@
+"""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
+
+# 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(3, byteorder='big').hex()
+ batch_hex = i.to_bytes(3, byteorder='big').hex()
+ token_id_indexed = token_id[:54] + batch_hex[1:] + cursor_hex[1:]
+ 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()