見出し画像

web3.pyとpy-solc-xでの独自SmartContractのDeploy

はじめに

@yutakikuchi_です
web3.pyを利用したpolygon networkに接続する例を下記のnoteに書きましたが、web3.pyに追加してsolcx、ETHのEthereumTesterProviderを利用したSmartContractのDeploy実装をメモしておきます。

py-solc-xとは

solc自体はSolidity compilerであり、py-solc-xはpythonのwrapperとなります。使い方は上のURL、もしくはweb3.pyのreadthedocsページを見てもらうのが良いかと思います。

py-solc-xによるSmartContract

繰り返しになりますが、EthereumTesterProviderを利用してのSmartContractを記載します。まずはpy-solc-xのpip installです。また、サンプルページに書かれているEthereumTesterProviderを利用のためのmoduleである、eth_tester、py-evmもあわせてinstallします。

尚、2022年02月現在のsolcの最新versionは0.8.11のようで、solcをnpmでinstallします。https://docs.soliditylang.org/en/v0.8.11/

$ python -V                                                                                                                    (git)-[main]
Python 3.7.4
$ pip install py-solc-x
...
Successfully installed py-solc-x-1.1.1 semantic-version-2.8.5
$ pip install eth_tester py-evm                    
$ npm install -g solc@0.8.11

Deployのサンプルスクリプト

まずはPythonファイルです。この例にどれほどの意味があるかは、僕自身も良くわからないところではありますが、solファイルの中のcontract StoreVarで定義されているsetVarに255という値をセットして、それをContractとしたtransactionをtoAddressに対して実行しているようです。また gas_estimate = store_var_contract.functions.setVar(255).estimateGas() でガス代を事前に見積をしているようで、gas_estimate < 100000の場合にtransactionを送って、その結果を出力しています。

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

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")    
 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()))
 contract_source_path = 'contract.sol'
 compiled_sol = compile_source_file('contract.sol')
 contract_id, contract_interface = compiled_sol.popitem()

 address = deploy_contract(w3, contract_interface)
 print(f'Deployed {contract_id} to: {address}\n')

 store_var_contract = w3.eth.contract(address=address, abi=contract_interface["abi"])
 gas_estimate = store_var_contract.functions.setVar(255).estimateGas()
 print(f'Gas estimate to transact with setVar: {gas_estimate}')

 if gas_estimate < 100000:
     print("Sending transaction to setVar(255)\n")
     tx_hash = store_var_contract.functions.setVar(255).transact()
     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 100000")

次にSolidityファイルです。​下記ページに記載されているようにSolidityをcontract.solファイルへ記載し、元のpythonからはfile readしてからcompileすると良いと思います。

https://web3py.readthedocs.io/en/stable/examples.html#working-with-contracts
https://web3py.readthedocs.io/en/latest/contracts.html
https://solcx.readthedocs.io/en/stable/using-the-compiler.html​

contract StoreVar {

   uint8 public _myVar;
   event MyEvent(uint indexed _var);

   function setVar(uint8 _var) public {
       _myVar = _var;
       emit MyEvent(_var);
   }

   function getVar() public view returns (uint8) {
       return _myVar;
   }
}

​上記pythonファイルを実行すると、下記のような出力結果が得られます。new contractsによるDeployは下記を参考にしています。https://github.com/ethereum/web3.py/blob/master/docs/examples.rst#deploying-new-contracts

Deployed <stdin>:StoreVar to: 0xF2E246BB76DF876Cef8b38ae84130F4F55De395b

Gas estimate to transact with setVar: 50480
Sending transaction to setVar(255)

Transaction receipt mined:
{'blockHash': HexBytes('0xa0938f23145fa9c6a9f273d8160bd83c2ad1527520c57d9f65ddfdd6f1086769'),
'blockNumber': 2,
'contractAddress': None,
'cumulativeGasUsed': 44966,
'effectiveGasPrice': 1000000000,
'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'gasUsed': 44966,
'logs': [AttributeDict({'type': 'mined', 'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x1bb6e9660c46d5b40247ca1ce7ea85bbba93c56dfd19380c0cc3433abc33af4d'), 'blockHash': HexBytes('0xa0938f23145fa9c6a9f273d8160bd83c2ad1527520c57d9f65ddfdd6f1086769'), 'blockNumber': 2, 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', 'data': '0x', 'topics': [HexBytes('0x6c2b4666ba8da5a95717621d879a77de725f3d816709b9cbe9f059b8f875e284'), HexBytes('0x00000000000000000000000000000000000000000000000000000000000000ff')]})],
'state_root': b'\x01',
'status': 1,
'to': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'transactionHash': HexBytes('0x1bb6e9660c46d5b40247ca1ce7ea85bbba93c56dfd19380c0cc3433abc33af4d'),
'transactionIndex': 0,
'type': '0x2'}

Was transaction successful?
1

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