見出し画像

web3.py, py-solc-xからOpenZeppelinを活用したSmartContractを呼び出す

はじめに

@yutakikuchi_です。
下記URLでtryしたweb3.pyとpy-solc-xでの独自コントラクトのDeployに追加して、OpenZeppelinを用いたNFT化のための準備を行います。※ただし、この作業だけではNFT化、mintはできないので、注意をしてください。NFTを作るための独自のコントラクトをDeployするまでの作業になります。また事前にweb3.pyとpy-solc-xをinstallする必要があるため、下記のURLに記載された手順を先に実行する必要があります。

やること

はじめに、OpenZeppelinを自身のlocal環境にinstallし、NFT化のためのSmartContractをsolファイルで記載します。記載されたSolファイルをPythonからpy-solc-xからcompile_sourceして実行できるようにします。

$ node -v
v17.0.1

$ solc --version                                                                                   (git)-[sample_function]
solc, the solidity compiler commandline interface
Version: 0.8.11+commit.d7f03943.Darwin.appleclang

$ npm install --prefix=./ @openzeppelin/contracts

次に、OpenZeppelinを活用したSolidityによる独自コントラクトを書きます。ERC-721というToken規格に準拠します。ERC-721については下記のETHのDocに詳細内容が書かれています。

ERC721PresetMinterPauserAutoIdのメソッド内の引数にコントラクト名、シンボル名、トークンの元となるURIでその後に自動的に連番が付与されるものになります。

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Ab3 is ERC721PresetMinterPauserAutoId {
   constructor()
       public
       ERC721PresetMinterPauserAutoId(
           "NFT from ab3.vision", // コントラクト名
           "NFT ab3", // シンボル名
           "https://example.com/token/" //連番が末尾に付与される元となるURI
       )
   {}
}

上のSolidityをnft.solというファイル名で保存し、Pythonからcompileして使います。compileにはpy-solc-xを活用します。

import sys
import time
import pprint

from web3.providers.eth_tester import EthereumTesterProvider
from web3 import Web3
from eth_tester import PyEVMBackend
from solcx import compile_source, compile_files

def compile_source_file(file_path):
 with open(file_path, 'r') as f:
   source = f.read()
 compiled_sol = compile_source(source,
   output_values=['abi', 'bin'],
   solc_version='0.8.11',
   base_path='node_modules'
 )    
 return compiled_sol

def deploy_contract(w3, contract_interface):
 tx_hash = w3.eth.contract(
     abi=contract_interface['abi'],
     bytecode=contract_interface['bin']).constructor().transact()
 address = w3.eth.get_transaction_receipt(tx_hash)['contractAddress']
 return address

if __name__ == '__main__':
 
 w3 = Web3(EthereumTesterProvider(PyEVMBackend()))
 pprint.pprint(w3.eth.accounts)
 compiled_sol = compile_source_file('nft.sol')
 contract_id = next(iter(compiled_sol))
 contract_interface = compiled_sol[contract_id]
 
 address = deploy_contract(w3, contract_interface)
 print(f'Deployed {contract_id} to: {address}\n')

 contract = w3.eth.contract(address=address, abi=contract_interface["abi"], bytecode=contract_interface["bin"])
 pprint.pprint(contract.all_functions())
 gas_estimate = contract.constructor().estimateGas()
 tx_hash = contract.constructor().transact()

 if gas_estimate < 10000000:
     print("Sending transaction to NFT\n")
     receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
     print("Transaction receipt mined:")
     pprint.pprint(dict(receipt))
     print("\nWas transaction successful?")
     pprint.pprint(receipt["status"])
 else:
     print("Gas cost exceeds 10000000")

注意が必要なこととしてpythonからSolidityをcompileする、compile_sourceにて、compilerのsolc_versionとbase_pathを指定しないとうまく動きません。上ではsolc_versionは0.8.11base_pathはnode_modulesがinstallされているdirectoryを指定しています。openzeppelinのinstallをlocaldirectoryに指定したので、ここではnode_modulesとだけ記載しています。solcのversionはsolc --versionなどで確認してください。

下記は実行の結果です。長いので途中を省略しています。上でcompileしたSolidity上の利用できる関数をall_functions()で取得して表示しているので、pythonからcontract.functions.<function名>で引数を渡してSolidityの関数を実行することが可能です。

['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
'0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69',
'0x1efF47bc3a10a45D4B230B5d10E37751FE6AA718',
'0xe1AB8145F7E55DC933d51a18c793F901A3A0b276',
'0xE57bFE9F44b819898F47BF37E5AF72a0783e1141',
'0xd41c057fd1c78805AAC12B0A94a405c0461A6FBb',
'0xF1F6619B38A98d6De0800F1DefC0a6399eB6d30C',
'0xF7Edc8FA1eCc32967F827C9043FcAe6ba73afA5c',
'0x4CCeBa2d7D2B4fdcE4304d3e09a1fea9fbEb1528']

Deployed <stdin>:Ab3 to: 0xF2E246BB76DF876Cef8b38ae84130F4F55De395b

[<Function DEFAULT_ADMIN_ROLE()>,
<Function MINTER_ROLE()>,
<Function PAUSER_ROLE()>,
<Function approve(address,uint256)>,
...
<Function totalSupply()>,
<Function transferFrom(address,address,uint256)>,
<Function unpause()>]
Sending transaction to NFT

Transaction receipt mined:
{'blockHash': HexBytes('0xfc96885003a60aad7038ba67a5bf4b1236f29fc78d0c69d371dcc995a139d082'),
'blockNumber': 2,
'contractAddress': '0x2946259E0334f33A064106302415aD3391BeD384',
'cumulativeGasUsed': 4364373,
'effectiveGasPrice': 1000000000,
'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'gasUsed': 4364373,
'logs': [AttributeDict({'type': 'mined', 'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x98b64c0e7631254d3f1de93f79a140ec8034ea6a3c2e704063e4ae8f8b52a4d7'), 'blockHash': HexBytes('0xfc96885003a60aad7038ba67a5bf4b1236f29fc78d0c69d371dcc995a139d082'), 'blockNumber': 2, 'address': '0x2946259E0334f33A064106302415aD3391BeD384', 'data': '0x', 'topics': [HexBytes('0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), HexBytes('0x0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf'), HexBytes('0x0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf')]}),
         AttributeDict({'type': 'mined', 'logIndex': 1, 'transactionIndex': 0, 'transactionHash': HexBytes('0x98b64c0e7631254d3f1de93f79a140ec8034ea6a3c2e704063e4ae8f8b52a4d7'), 'blockHash': HexBytes('0xfc96885003a60aad7038ba67a5bf4b1236f29fc78d0c69d371dcc995a139d082'), 'blockNumber': 2, 'address': '0x2946259E0334f33A064106302415aD3391BeD384', 'data': '0x', 'topics': [HexBytes('0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d'), HexBytes('0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6'), HexBytes('0x0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf'), HexBytes('0x0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf')]}),
         AttributeDict({'type': 'mined', 'logIndex': 2, 'transactionIndex': 0, 'transactionHash': HexBytes('0x98b64c0e7631254d3f1de93f79a140ec8034ea6a3c2e704063e4ae8f8b52a4d7'), 'blockHash': HexBytes('0xfc96885003a60aad7038ba67a5bf4b1236f29fc78d0c69d371dcc995a139d082'), 'blockNumber': 2, 'address': '0x2946259E0334f33A064106302415aD3391BeD384', 'data': '0x', 'topics': [HexBytes('0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d'), HexBytes('0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a'), HexBytes('0x0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf'), HexBytes('0x0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf')]})],
'state_root': b'\x01',
'status': 1,
'to': '',
'transactionHash': HexBytes('0x98b64c0e7631254d3f1de93f79a140ec8034ea6a3c2e704063e4ae8f8b52a4d7'),
'transactionIndex': 0,
'type': '0x2'}

Was transaction successful?
1


この記事が気に入ったらサポートをしてみませんか?