Account, Address and Wallet

Run this example online by clicking 🚀 -> Binder on the top bar!

Preparation

This part prepares the web3 instance and key private_key to use in the following part of this documentation.

# 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()

Account and Address

In web3, typically, owning an account means knowing a secret, or private key. Although private key should be kept secret, address can be revealed. Address is a string one-way derived from the private key, and identifies an account. Different blockchain might adopt different approach to derive an address, e.g. bitcoin and ethereum use different methods to generate account addresses .In Conflux, addresses are encoded in base32 format defined by CIP-37.

LocalAccount Object

w3.account is a factory which is used to produce LocalAccount objects (e.g. random_account). which can be used to sign transactions.

NOTE: it is quite tedious to manually sign a transaction. Refer to wallet part to see how to sign and send a transaction by using wallet. Or you can refer to construct_transaction_from_scratch to see how to manually sign transactions correctly.

More documents: w3.account is a cfx_account.Account object inherited from eth_account.Account, and most of its apis are consistent with 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).rawTransaction.hex()}")
account address: cfxtest:aak610kbgm17u1knr11xjs4b9cgehft3uy07v7w3dy
account private key: 0xdd8fd18afdcebd8801f9c6a04812ac6e8f5ab31d9c50de51eda0581291b61d61
signed raw tx: 0xf867e301843b9aca00825208940000000000000000000000000000000000000000018064018001a0541786f46b98237f85b5d26bb0844fc6a5596c5856c217f257abbcf2a156f9f1a02f07e5ca33b4b0d235b39b8e116f792a76e1121a74e15b4cb5f8e4f6f1bc52ce

How to Create a LocalAccount object

There are several ways to create a LocalAccount object.

  • from random by using w3.account.create

  • from existed key by using w3.account.from_key

  • from mnemonic by using w3.account.from_mnemonic

  • from keystore by using w3.account.decrypt

Here are the examples of how to use the above APIs:

# 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 Addresses

In Conflux, addresses are encoded in base32 format following CIP-37. You can infer which network the address belongs to simply from the address literal.

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

The addresses returned by SDK are all wrapped by class Base32Address. The class provides convenient methods to operate base32 addresses, but you can also use Base32Address object as trivial python str object. Refer to Base32Address documentation for more information.

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

We use a wallet middleware to help us sign and send transactions. w3.wallet will sign the unsigned transaction sent via w3.cfx.send_transaction if the transaction is from an account in w3.wallet.

wallet middleware follows the behaviour of web3.py’s construct_sign_and_send_raw_middleware, but provides more feature. For example, we can use w3.wallet.add_account, w3.wallet.add_accounts, w3.wallet.pop to add or remove accounts dynamically.

# 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': []})

use w3.cfx.default_account to add account to wallet

If w3.cfx.default_account is set, from field of the sent transaction will be set to w3.cfx.default_account if empty.

w3.cfx.default_account is a Base32Address, but can be set using a LocalAccount object. In this case, the provided LocalAccount object will be added to the wallet at the same time.

w3.cfx.default_account = random_account

is equivalent to

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