使用web3.py发送ETH和ERC20

Posted sanqima

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用web3.py发送ETH和ERC20相关的知识,希望对你有一定的参考价值。

    2021年,web3.py的版本更新到了v5.4,其库函数的名称改了很多,库函数名称由之前的驼峰命名法: xxxYYYzzz (错落有致,用大小写区别不同的名称),改成 蛇形命名法: xxx_yyy_zzz (名称全部小写,名字之间用_下划线连接)。
    使用web3.eth.send_transaction()来发送ETH, 使用web3.eth.wait_for_transaction_receipt()来发送ERC20。
    下面介绍,ETH和ERC20的发送语法:
    a)发送ETH

from web3 import Web3, HTTPProvider
w3 = Web3(HTTPProvider("http://localhost:8545"))

w3.eth.send_transaction({
  'to': '0x6927c1c19da90EabC30D5fde4D27D98Dfe2D9866',
  'from': '0xc88D334e8045aE5791835CC6471605C7d36CEfFc',
  'value': 12345,
  'gas': 21000,
  'gasPrice': web3.toWei(50, 'gwei'),
})

    b) 发送ERC20

from web3 import Web3, HTTPProvider
w3 = Web3(HTTPProvider("http://localhost:8545"))
contract = w3.eth.contract(contract_address, abi=ABI)

alice = '0xc88D334e8045aE5791835CC6471605C7d36CEfFc'
bob = '0x6927c1c19da90EabC30D5fde4D27D98Dfe2D9866'

tx_hash = contract.functions.transfer(bob, 100).transact({'from': alice})
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

1、版本更替

1.1 获取当前高度

原版:web3.eth.blockNumber()
新版:web3.eth.block_number()

1.2 获取链ID

原版:web3.eth.chainId
新版:web3.eth.chain_id

1.3 获取余额

原版:web3.eth.getBalance(account)
新版:web3.eth.get_balance(account)

1.4 对交易进行签名

原版:web3.eth.signTransaction
新版:web3.eth.sign_transaction

1.5 对交易进行广播

原版:web3.eth.sendRawTransaction
新版:web3.eth.send_raw_transaction

2、创建onepython工程

2.1 创建文件夹

mkdir onepython
cd onepython
npm init -y
truffle init

## 新建文件夹onepy
mkdir -p test\\onepy

2.2 新建DPCToken智能合约

    在onepython/contracts目录下,新建DPCToken.sol合约
    //DPCToken.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;
        return c;
    }
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
    }
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract DPCToken is IERC20 {
    using SafeMath for uint256;

    string private _name = "Delta Park Chain";
    string private _symbol = "DPC";
    uint8 private _decimals = 18;
    uint256 private _totalSupply = 50000000 * (10 ** uint256(_decimals));

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;

    constructor () public {
        _balances[msg.sender] = _totalSupply;
    }

    function name() public view returns (string memory) {
        return _name;
    }

    function symbol() public view returns (string memory) {
        return _symbol;
    }

    function decimals() public view returns (uint8) {
        return _decimals;
    }

    function totalSupply() public override view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public override view returns (uint256) {
        return _balances[account];
    }

    function allowance(address owner, address spender) public override view returns (uint256) {
        return _allowances[owner][spender];
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

2.3 编写测试脚本

    a) 在onepython/test/onepy目录,新建1.sendETH.py,用于发送ETH。
    // 1.sendETH.py

from web3 import Web3, HTTPProvider
# from web3.contract import ConciseContract

# web3.py instance
w3 = Web3(HTTPProvider("http://localhost:8545"))
print(w3.isConnected())

fromAddr = w3.eth.accounts[0]
toAddr   = w3.eth.accounts[1]
print('fromAddr=',fromAddr)
print('toAddr  =',toAddr)


def getEthBalance(accountAddr):
    balance = w3.eth.get_balance(accountAddr)
    return w3.toWei(balance,'ether')

def sendEth(web3obj,fromAddr,toAddr,value):
    web3obj.eth.sendTransaction({'to':toAddr,'from':fromAddr,'value':value})


def printEthBalance(web3obj,accountAddr,mark):
    balance = web3obj.eth.getBalance(accountAddr)
    markStr = mark +" balance="
    print(mark,web3obj.fromWei(balance,'ether'))


# balance2 = w3.eth.getBalance(fromAddr)
# print('before balance= ',w3.fromWei(balance2,'ether'))

# w3.eth.sendTransaction({'to':toAddr,'from':fromAddr,'value':w3.toWei(0.1,'ether')})

# balance2 = w3.eth.getBalance(fromAddr)
# print('after  balance= ',w3.fromWei(balance2,'ether'))


printEthBalance(w3,fromAddr,"#1")
sendEth(w3,fromAddr,toAddr,w3.toWei(0.1,'ether'))
printEthBalance(w3,fromAddr,"#2")

    b) 在onepython/test/onepy目录,新建2.sendErc20.py,用于发送Erc20,此处是DPC通证。
    // 2.sendErc20.py

import json
from web3 import Web3, HTTPProvider
#from web3.contract import ConciseContract

## 获取合约的abi
def getAbi(filePath):
    with open(filePath,'r') as abi_file:
        mpc_abi = json.load(abi_file)
    return mpc_abi

## 获取余额
def getBalance(contractObj,accountAddr):
    return contractObj.functions.balanceOf(accountAddr).call()

## 获取合约对象
def getContractObj(web3Obj,contractAddr,abiPath):
    con_abi = getAbi(abiPath)
    return web3Obj.eth.contract(address=contractAddr,abi=con_abi)

## 发送ERC20
def sendErc20(web3obj,fromAddr,toAddr,value,contractAddr,abiPath):
    contractAbi = getAbi(abiPath)
    contractObj = web3obj.eth.contract(address=contractAddr,abi=contractAbi)
    tx_hash = contractObj.functions.transfer(toAddr,value).transact({'from':fromAddr})
    tx_receipt = web3obj.eth.wait_for_transaction_receipt(tx_hash)
    if tx_receipt['status'] == 1:
        return 'send Success'
    else:
        return 'send Failed'

def printBalance(web3obj,contractObj,fromAddr,toAddr,markIndex):
    balanceA = getBalance(contractObj,fromAddr)
    balanceB = getBalance(contractObj,toAddr)
    fromMark = markIndex+" balanceA="
    toMark   = markIndex+" balanceB="
    print(fromMark,web3obj.fromWei(balanceA,'ether'))
    print(toMark,  web3obj.fromWei(balanceB,'ether'))

#####   发送ERC20 ######
# web3.py instance
w3 = Web3(HTTPProvider("http://localhost:8545"))
print('web3 connect:',w3.isConnected())

fromAddr = w3.eth.accounts[0]
toAddr   = w3.eth.accounts[1]
print('fromAddr=',fromAddr)
print('toAddr  =',toAddr)

value = w3.toWei(0.1,'ether')
abiPath = './myabi/DPC_abi.json'
contractAddr = '0xE250d901baeCb66F85D184D8aE9dA2bD4e705854' ##DPC合约地址
contractObj = getContractObj(w3,contractAddr,abiPath)

## 发送前
printBalance(w3,contractObj,fromAddr,toAddr,"#1")

bRet = sendErc20(w3,fromAddr,toAddr,value,contractAddr,abiPath)
print('result= ',bRet)

## 发送后
printBalance(w3,contractObj,fromAddr,toAddr,"#2")

2.4 编写合约的部署脚本

    在onepython/migrations目录下,新建2_deploy_DPC.js文件,内容如下:
    // 2_deploy_DPC.js

const DPCToken = artifacts.require("DPCToken");

module.exports = function (deployer) {
  deployer.deploy(DPCToken);
};

2.5 编译和部署合约

    打开一个黑框框终端,进入onepython目录,依次输入如下命令:

truffle console
compile
migrate

    得到DPC合约地址:0xE250d901baeCb66F85D184D8aE9dA2bD4e705854

2.6 拷贝abi

    DPCToken合约在migrate之后,会在本地的build/contracts/DPCToken.json文件里生成 abi:[]字段 ,将abi:[]里的内容拷贝到onepython/myabi/DPC_abi.json里即可,具体请看这篇文章: [将abi压缩为一行]

2.7 工程目录结构

    onepython的工程目录,如图(1)所示:

图(1) onepython的工程目录结构

3、发送ETH或ERC20

3.1 发送ETH

cd onepython
python3 test/onepy/1.sendETH.py 

3.2 发送ERC20

cd onepython
python3 test/onepy/2.sendErc20.py 

效果如图(2)所示:

图(2) 用web3.py发送ETH或ERC20

如图(2)所示,fromAddr 发了0.1 ETH给 toAddr,它自己还剩998.22 ETH
         fromAddr 发了0.1 DPC给 toAddr,它自己还剩49999999.9 DPC。

参考文献

web3.py -v5.4 - [sending-token] 手册

以上是关于使用web3.py发送ETH和ERC20的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 web3.py 在钱包之间转移 ERC20 代币

我可以使用 PHP 从 ERC20 合约中转移代币吗?

eth和erc20代码接入

eth和erc20代码接入

eth和erc20代码接入

eth和erc20代码接入