Interaction, compilation, etc. between python's web3 and smart contracts

1. Background

web3.py is a Python library for interacting with Ethereum.
It is commonly found in decentralized applications (dapps), helping to send transactions, interact with smart contracts, read block data, and various other use cases.
The original API was derived from the Web3.js Javascript API, but has since evolved to meet the needs and creature comforts of Python developers.

I need to verify some basic information during contract audit and fuzz testing, so I studied it a bit

pip install web3

web3 development documentationhttps://web3py.readthedocs.io/en/stable/

2. Basic applications
  • Connect to Ethereum test node
    from web3 import Web3
    from web3 import EthereumTesterProvider # 以太网测试程序提供程序
    
    创建区块链链接器 链接到测试节点
    w3 = Web3(EthereumTesterProvider())
    建立web3链接
    w3.is_connected()
    
  • Connect eth node using HTTPProvider and web3
    provider_url = 'https://mainnet.infura.io/v3/3c3793ddeca**********299afb1c2dc6458'
    w3 = Web3(Web3.HTTPProvider(provider_url))
    w3.is_connected()
    
  • Get the latest block information
    latest_block = w3.eth.get_block('latest')
    
  • Verify whether the smart contract address is valid
    is_addr = w3.is_address('0x314ECf414b0987EAf8A3504915******91d24')
    
  • Get how much eth the wallet balance is
    wallet = w3.to_checksum_address('0x314ECf414b0987EAf8A3504915d*****1d24')
    print(w3.eth.get_balance(wallet))
    
  • Convert wei to eth (wei is the smallest unit of eth)
    wei = w3.from_wei(111111111111111111111, 'ether')
    print(wei)
    
3. Blockchain contract interaction
  • ABI 包含输入函数名称变量
    ABI 对于每个智能合约都是唯一的,除非代码完全一样 abi = '[]'
    
    abi = '[{
          
          "inputs":[],"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"},{
          
          "inputs":[{
          
          "internalType":"address","name":"owner","type":"address"},{
          
          "internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{
          
          "internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{
          
          "inputs":[{
          
          "internalType":"address","name":"spender","type":"address"},{
          
          "internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{
          
          "internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{
          
          "inputs":[{
          
          "internalType":"address","name":"account","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":[{
          
          "internalType":"address","name":"spender","type":"address"},{
          
          "internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{
          
          "internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{
          
          "inputs":[{
          
          "internalType":"address","name":"spender","type":"address"},{
          
          "internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{
          
          "internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{
          
          "inputs":[],"name":"name","outputs":[{
          
          "internalType":"string","name":"","type":"string"}],"stateMutability":"view","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":"recipient","type":"address"},{
          
          "internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{
          
          "internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{
          
          "inputs":[{
          
          "internalType":"address","name":"sender","type":"address"},{
          
          "internalType":"address","name":"recipient","type":"address"},{
          
          "internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{
          
          "internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]'
    contract_address = '0xda141e275f46F9Df74b29AA3eCf7fF77Bc6781AB'
    
  • Call contract functions or access contract variables

    例如: contract_instance.functions.someFunction().call()
    
    contract_instance = w3.eth.contract(address=contract_address, abi=abi)
    result = contract_instance.functions.totalSupply().call()
    result = contract_instance.functions.symbol().call()
    print(result)
    
4. Compilation of smart contracts
from solcx import compile_source, compile_standard

# 指定安装某个版本编译器
# install_solc(version='latest')

# 编译合约代码 输出abi、bin 字节码
compile_solidity = compile_source(
    '''
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.18;
    contract HelloWorld {
    
    
        string public message;
        constructor(){
    
    
            message = 'HelloWorld!';
        }
        function setMessage(string memory _message) public{
    
    
            message = _message;
        }
        function sayMessage() public view returns (string memory) {
    
    
            return message;
        }
    }
    ''',
    output_values=['abi', 'bin']
)

# 检索合约接口 compile_solidity.popitem()
contract_id, contract_interface = compile_solidity.popitem()
# print(contract_id)                # 合约name
# print(contract_interface)         # 合约abi、bin
# print(contract_interface['abi'])  # 合约abi
Python interacts with eth smart contracts
# 创建连接到以太坊测试器的Web3实例
w3 = Web3(Web3.EthereumTesterProvider())
# w3 = Web3(Web3.WebsocketProvider)

# 设置默认账户为测试器中的第一个账户
# w3.eth.accounts 是以太坊测试器中已创建的账户列表。通过 w3.eth.default_account = w3.eth.accounts[0],将默认账户设置为测试器中的第一个账户。
# 这意味着当您发送交易或调用合约函数时,如果没有显式指定账户,将默认使用 w3.eth.accounts[0] 作为交易的发送者

w3.eth.default_account = w3.eth.accounts[0]

# 合约的ABI和字节码
abi = contract_interface['abi']
bytecode = contract_interface['bin']

# 创建合约实例
helloworld = w3.eth.contract(abi=abi, bytecode=bytecode)

# 发送合约构造函数的交易
transaction_hash = helloworld.constructor().transact()
# print(transaction_hash)

# 等待交易收据
transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash=transaction_hash)
# print(transaction_receipt)
'''
blockHash: 交易所在区块的哈希值。
blockNumber: 交易所在区块的编号。
contractAddress: 如果交易创建了一个新的合约,该参数表示新合约的地址。如果交易不是创建合约的交易,则为一个空字符串。
cumulativeGasUsed: 该交易之前所有交易的累计消耗的燃气量。
effectiveGasPrice: 交易的有效燃气价格。
from: 发送者(发送交易的账户地址)。
gasUsed: 该交易消耗的燃气量。
logs: 交易产生的日志事件。
state_root: 交易执行后的状态树根哈希。
status: 交易的执行状态,1表示成功,0表示失败。
to: 交易的接收者地址。如果是创建合约的交易,则为一个空字符串。
transactionHash: 交易的哈希值。
transactionIndex: 交易在所在区块的索引位置
# type: 交易类型1表示普通交易,2表示合约创建交易
'''
# # 获取合约对象
# helloworldContract = w3.eth.contract(address=transaction_receipt.contractAddress, abi=abi)
# print(helloworldContract) # 获取合约对象
# # 调用合约中函数sayMessage
# print(helloworldContract.functions.sayMessage().call())
# # 调用合约中函数setMessage,修改一个值,下面获取并没有变化,需要我们重新部署,发起交易
# print(helloworldContract.functions.setMessage('BEY').call())
# # 发现并没有变化
# print(helloworldContract.functions.sayMessage().call())
# # 重新调用合约构造函数的交易
# bye_hash = helloworldContract.functions.setMessage('BEY').transact()
# print(bye_hash)
# # 等待交易收据
# bye_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash=bye_hash)
# print(bye_receipt)
# # 重新构造以后才会修改为bye
# print(helloworldContract.functions.sayMessage().call())
# # print(is_addr)
python compile smart contract
compiled_solidity = compile_standard({
    
    
    "language": "Solidity",
    "sources": {
    
    
        "SimpleNumber.sol": {
    
    
            # "content": contract_file
        }
    },
    "setting": {
    
    
        "outputSelection": {
    
    
            "*": {
    
    "*": ["abi", "metadata", "evm.bytecode", "evm.sourceMap"]}
        }
    }
},
    solc_version='0.8.12'
)

print(compiled_solidity)

# compiled_solidity: 编译后的Solidity结合的结果
# language: Solidity合约的编程语言,这里是"Solidity"
# sources: Solidity源代码文件的字典,这里只有一个源代码文件"SimpleNumber.sol"
# setting: 编辑设置的字典,包选择输出
# outputSelection: 输入选的字典,选包包括ABI、元数据、EVM字节码和EVM源映像
# solc_version: Solidity编译器的版本,这里是"0.8.12"
Deploy smart contracts using python
provider_url = 'https://goerli.infura.io/v3/3c3793ddeca5********fb1c2dc6458'
w3 = Web3(Web3.HTTPProvider(provider_url))
w3.is_connected()

abi = compiled_solidity['contracts']['SimpleNumber.sol']['SimpleNumber']['abi']
bytecode = compiled_solidity['contracts']['SimpleNumber.sol']['SimpleNumber']['evm']['bytecode']['object']

SimpleNumber = w3.eth.contract(abi=abi, bytecode=bytecode)
transaction = SimpleNumber.constructor().build_transaction(
    {
    
    
        "gasPrice": w3.eth.gas_price,
        "chainId": 3,
        "from": "wallet 钱包地址 来自metamask账户",
        "nonce": w3.eth.get_block_transaction_count("wallet 钱包地址 来自metamask账户")

    }
)
metamask adds network eth test network
sign_transaction = w3.eth.account.sign_transaction(transaction, private_key='私钥')
print(sign_transaction)
transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
print(transaction_hash)
transaction_receipt2 = w3.eth.wait_for_transaction_receipt(transaction_hash)
print(transaction_receipt2)
Interacting with deployed smart contracts in python
contract_instance = w3.eth.contract(address=transaction_receipt2.contractAddress, abi=abi)

contract_instance.functions.getStoreNumber().call()
contract_instance.functions.updateStoreNumber(200).call()

update_number_transaction = contract_instance.functions.updateStoreNumber(200).call().build_transaction(
    {
    
    
        "gasPrice": w3.eth.gas_price,
        "chainId": 3,
        "from": "wallet 钱包地址 来自metamask账户",
        "nonce": w3.eth.get_block_transaction_count("wallet 钱包地址 来自metamask账户") + 1 # 每次加1 不能重复使用 nonce
    }
)

sign_transaction3 = w3.eth.account.sign_transaction(update_number_transaction, private_key='私钥')
print(sign_transaction3)
transaction_hash3 = w3.eth.send_raw_transaction(sign_transaction3.rawTransaction)
print(transaction_hash3)
transaction_receipt3 = w3.eth.wait_for_transaction_receipt(transaction_hash3)
print(transaction_receipt3)
# 此时进行变化
contract_instance.functions.getStoreNumber().call()

# print(latest_block)
# print(w3.is_connected())
# print(Web3)
# print(EthereumTesterProvider)

Guess you like

Origin blog.csdn.net/a6864657/article/details/131384279