Generating addresses
Overview
On Ethereum, there are two types of addresses: wallet addresses for holding assets and contract addresses. Wallet addresses are public accounts that can send and receive ETH tokens. Contract addresses refer to a smart contract that has been executed on Ethereum and the transactions associated with that contract.
Wallet and contract addresses can be queried using the EVM RPC canister.
Canister ETH addresses
To create an ETH address for your canister, first you will need to obtain an ECDSA public key associated to your canister. You can get this key by calling the ecdsa_public_key
API method of the management canister:
- Motoko
- Rust
// Declare "ic" to be the management canister, which is evoked by `actor("aaaaa-aa")`.:
let ic : IC = actor("aaaaa-aa");
public shared (msg) func public_key() : async { #Ok : { public_key: Blob }; #Err : Text } {
let caller = Principal.toBlob(msg.caller);
try {
// Make a call to the management canister to request an ECDSA public key:
let { public_key } = await ic.ecdsa_public_key({
canister_id = null;
derivation_path = [ caller ];
key_id = { curve = #secp256k1; name = "test_key_1" };
});
#Ok({ public_key })
} catch (err) {
#Err(Error.message(err))
}
};
#[update]
async fn public_key() -> Result<PublicKeyReply, String> {
let request = ECDSAPublicKey {
canister_id: None,
derivation_path: vec![],
key_id: EcdsaKeyIds::TestKeyLocalDevelopment.to_key_id(),
};
let (res,): (ECDSAPublicKeyReply,) =
ic_cdk::call(mgmt_canister_id(), "ecdsa_public_key", (request,))
.await
.map_err(|e| format!("ecdsa_public_key failed {}", e.1))?;
Ok(PublicKeyReply {
public_key_hex: hex::encode(&res.public_key),
})
}
Then, convert the public key bytes to an Ethereum address. An example in Rust can be found below:
/// Converts the public key bytes to an Ethereum address with a checksum.
fn pubkey_bytes_to_address(pubkey_bytes: &[u8]) -> String {
use k256::elliptic_curve::sec1::ToEncodedPoint;
let key =
PublicKey::from_sec1_bytes(pubkey_bytes).expect("failed to parse the public key as SEC1");
let point = key.to_encoded_point(false);
// we re-encode the key to the decompressed representation.
let point_bytes = point.as_bytes();
assert_eq!(point_bytes[0], 0x04);
let hash = keccak256(&point_bytes[1..]);
ethers_core::utils::to_checksum(&Address::from_slice(&hash[12..32]), None)
}
Next steps
You can use this address to make Ethereum transactions from your canister.