账户、地址和钱包#

点击顶部栏的 🚀 -> Binder 在线运行此示例!

准备工作#

本部分准备了在后续文档中使用的 web3 实例和密钥 private_key

# claim 1000 CFX
from pprint import pprint
from conflux_web3 import Web3

w3_ = Web3(Web3.HTTPProvider("https://test.confluxrpc.com"))

acct = w3_.account.create()

w3_.cfx.default_account = acct

faucet = w3_.cfx.contract(name="Faucet")
tx_receipt = faucet.functions.claimCfx().transact().executed()

# we use a new w3 object for the following part
w3 = Web3(Web3.HTTPProvider("https://test.confluxrpc.com"))
private_key: str = acct.key.hex()

账户和地址#

在web3中,拥有一个账户通常意味着知道一个密钥,或者说私钥。虽然私钥应该保密,但是地址可以公开。地址是从私钥单向派生出的字符串,用于标识一个账户。不同的区块链可能采用不同的方法来派生地址,例如比特币和以太坊使用不同的方法来生成账户地址。在Conflux中,地址使用CIP-37定义的base32格式进行编码。

LocalAccount 对象#

w3.account 是一个工厂,用于生成 LocalAccount 对象(例如 random_account),这些对象可以用来签名交易。

注意:手动签名交易是一个繁琐的过程。请参考 wallet 部分了解如何使用钱包来签名和发送交易。或者您可以参考 construct_transaction_from_scratch 来了解如何正确地手动签名交易。

更多文档:w3.account 是一个继承自 eth_account.Accountcfx_account.Account 对象,其大部分API与 eth_account 保持一致。

random_account = w3.account.from_key(private_key)
print(f"account address: {random_account.address}")
print(f"account private key: {private_key}")

transaction = {
    'to': w3.address.zero_address(),
    'nonce': 1,
    'value': 1,
    'gas': 21000,
    'gasPrice': 10**9,
    'storageLimit': 0,
    'epochHeight': 100,
    'chainId': 1
}
print(f"signed raw tx: {random_account.sign_transaction(transaction).raw_transaction.hex()}")
account address: cfxtest:aak610kbgm17u1knr11xjs4b9cgehft3uy07v7w3dy
account private key: 0xdd8fd18afdcebd8801f9c6a04812ac6e8f5ab31d9c50de51eda0581291b61d61
signed raw tx: 0xf867e301843b9aca00825208940000000000000000000000000000000000000000018064018001a0541786f46b98237f85b5d26bb0844fc6a5596c5856c217f257abbcf2a156f9f1a02f07e5ca33b4b0d235b39b8e116f792a76e1121a74e15b4cb5f8e4f6f1bc52ce

如何创建 LocalAccount 对象#

创建 LocalAccount 对象有以下几种方式:

  • 使用 w3.account.create 随机创建

  • 使用 w3.account.from_key 从已有私钥创建

  • 使用 w3.account.from_mnemonic 从助记词创建

  • 使用 w3.account.decrypt 从密钥库文件创建

以下是这些API的使用示例:

# from random
# this api will collect randomness from operating system to generate the account,
# the parameter could provide extra entropy
generated_account = w3.account.create("extra_entropy")

# from existed key
generated_account = w3.account.from_key("0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364")

# from mnemonic
# "m/44'/503'/0'/0/0" is the default Conflux derive path
generated_account = w3.account.from_mnemonic(
    "health embark april buyer eternal leopard want before nominee head thing tackle",
    passphrase="",
    account_path="m/44'/503'/0'/0/0"
)

# from keystore
# and of course, you can use `w3.account.encrypt` to get a keystore file json
keystore = {
    "version": 3,
    "id": "db029583-f1bd-41cc-aeb5-b2ed5b33227b",
    "address": "1cad0b19bb29d4674531d6f115237e16afce377c",
    "crypto": {
        "ciphertext": "3198706577b0880234ecbb5233012a8ca0495bf2cfa2e45121b4f09434187aba",
        "cipherparams": {"iv": "a9a1f9565fd9831e669e8a9a0ec68818"},
        "cipher": "aes-128-ctr",
        "kdf": "scrypt",
        "kdfparams": {
            "dklen": 32,
            "salt": "3ce2d51bed702f2f31545be66fa73d1467d24686059776430df9508407b74231",
            "n": 8192,
            "r": 8,
            "p": 1,
        },
        "mac": "cf73832f328f3d5d1e0ec7b0f9c220facf951e8bba86c9f26e706d2df1e34890",
    }
}
generated_account = w3.account.decrypt(keystore, password="password")

Conflux 地址#

在 Conflux 中,地址按照 CIP-37 规范使用 base32 格式进行编码。通过地址字面值,你可以直接判断该地址属于哪个网络。

# "cfxtest" marks an address in testnet
assert random_account.address.startswith("cfxtest:")

SDK 返回的地址都被封装为 Base32Address 类。该类提供了便捷的方法来操作 base32 地址,同时 Base32Address 对象也可以像普通的 Python str 对象一样使用。 更多信息请参考 Base32Address 文档

addr = random_account.address
print(f"the type of addr: {type(addr)}")
# a Base32Address object is also a `str`
assert isinstance(addr, str)

# encode a base32 address from hex address and network_id
# it is also supported to use `w3.address("cfxtest:aatp533cg7d0agbd87kz48nj1mpnkca8be1rz695j4")`
address = w3.address("0x1ecde7223747601823f7535d7968ba98b4881e09", network_id=1)
print(address)
pprint([
    address.address_type,
    address.network_id,
    address.hex_address,
    address.verbose_address,
    address.abbr,
    address.mapped_evm_space_address,
])
the type of addr: <class 'cfx_address.address.Base32Address'>
cfxtest:aatp533cg7d0agbd87kz48nj1mpnkca8be1rz695j4
['user',
 1,
 '0x1ECdE7223747601823f7535d7968Ba98b4881E09',
 'CFXTEST:TYPE.USER:AATP533CG7D0AGBD87KZ48NJ1MPNKCA8BE1RZ695J4',
 'cfxtest:aat...95j4',
 '0x349f086998cF4a0C5a00b853a0E93239D81A97f6']

钱包#

我们使用 wallet 中间件来帮助签名和发送交易。当通过 w3.cfx.send_transaction 发送未签名交易时,如果交易的 from 地址是 w3.wallet 中的账户,w3.wallet 将会为该交易进行签名。

wallet 中间件的行为与 web3.pySignAndSendRawMiddleware 类似,但提供了更多功能。例如,我们可以使用 w3.wallet.add_accountw3.wallet.add_accountsw3.wallet.pop 来动态添加或移除账户。

# wallet serves as a middleware for conflux_web3 and contains a collection of LocalAccount
assert w3.wallet is w3.middleware_onion["wallet"]

w3.wallet.add_account(random_account)

assert random_account.address in w3.wallet
w3.cfx.send_transaction({
    "from": random_account.address,
    "to": random_account.address,
    "value": 10**18
}).executed()
AttributeDict({'transactionHash': HexBytes('0x563c8e1d944d4e688b619808ddd9e92178881ebfe64f3da7c027922e54ef3d55'),
 'index': 0,
 'blockHash': HexBytes('0x4e46fe16028137953f5eca7f34ef63e18f323809eadab3137c497764d7cf39de'),
 'epochNumber': 109112027,
 'from': 'cfxtest:aak610kbgm17u1knr11xjs4b9cgehft3uy07v7w3dy',
 'to': 'cfxtest:aak610kbgm17u1knr11xjs4b9cgehft3uy07v7w3dy',
 'gasUsed': 21000,
 'gasFee': 21000000000000 Drip,
 'contractCreated': None,
 'logs': [],
 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'),
 'stateRoot': HexBytes('0x50b523a7e94e9cc3a699c8d2db5402d5f146cddb8e552f6fe8ce697ee26a9bd7'),
 'outcomeStatus': 0,
 'txExecErrorMsg': None,
 'gasCoveredBySponsor': False,
 'storageCoveredBySponsor': False,
 'storageCollateralized': 0,
 'storageReleased': []})

使用 w3.cfx.default_account 向钱包添加账户#

如果设置了 w3.cfx.default_account,当发送交易时如果没有指定 from 字段,该字段将被设置为 w3.cfx.default_account

w3.cfx.default_account 是一个 Base32Address 类型,但可以使用 LocalAccount 对象来设置。在这种情况下,提供的 LocalAccount 对象会同时被添加到钱包中。

w3.cfx.default_account = random_account

等价于

w3.cfx.default_account = random_account.address
w3.wallet.add_account(random_account)