# Deriving the Eth Address from Caller indetnficiation \`shared({caller})\`

**URL:** <https://forum.dfinity.org/t/deriving-the-eth-address-from-caller-indetnficiation-shared-caller/37313>\
**Category:** Motoko\
**Created:** [November 14, 2024, 12:55pm UTC](https://forum.dfinity.org/t/deriving-the-eth-address-from-caller-indetnficiation-shared-caller/37313 "2024-11-14T12:55:22Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![dominicletz](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/dominicletz/32/17942_2.png) [@dominicletz](https://forum.dfinity.org/u/dominicletz)\
**Post date:** [November 14, 2024, 12:55pm UTC](https://forum.dfinity.org/t/deriving-the-eth-address-from-caller-indetnficiation-shared-caller/37313/1 "2024-11-14T12:55:22Z")

</div>

Hi there,

I’m implementing in Motoko some cross chain code. It’s supposed to lookup certain information about the caller on remote evm chain. I read the docs from [https://internetcomputer.org/docs/current/motoko/main/writing-motoko/caller-id](https://internetcomputer.org/docs/current/motoko/main/writing-motoko/caller-id) and have tried the `ecdsa_public_key` method to retrieve the public key to do this:

`Principal -> PublicKey -> (hashing) -> EthAddress`

Unfortunately it looks like I can only fetch the PublicKey from existing canisters but not from the caller identification. Am I correct that there is no way to derive the EthAddress from the caller identification alone at this time? What have others done to alleviate this issue for chain fusion solutions?

---

<div class="post-metadata">

**Author:** ![Vivienne](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/vivienne/32/29170_2.png) [@Vivienne](https://forum.dfinity.org/u/Vivienne)\
**Post date:** [November 14, 2024, 4:19pm UTC](https://forum.dfinity.org/t/deriving-the-eth-address-from-caller-indetnficiation-shared-caller/37313/2 "2024-11-14T16:19:06Z")

</div>

Users/principals do not have ETH addresses by default. Only canisters can hold funds. But, canisters can have multiple addresses. If you have a look at [this Rust example](https://github.com/bjoernek/multi_chain_voting/blob/main/src/backend/src/eth_rpc.rs#L455-L461), you will see that the public key (from which the ETH address is derived) takes a derivation path. Often, projects use the caller principal as a derivation path and then derive an address from that public key.

Side note: The user will then not hold ETH directly. The ETH will be held by the canister on behalf of the user

---

<div class="post-metadata">

**Author:** ![dominicletz](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/dominicletz/32/17942_2.png) [@dominicletz](https://forum.dfinity.org/u/dominicletz)\
**Post date:** [November 17, 2024, 4:18am UTC](https://forum.dfinity.org/t/deriving-the-eth-address-from-caller-indetnficiation-shared-caller/37313/3 "2024-11-17T04:18:03Z")

</div>

Thanks for the answer. I think it’s somewhat a misunderstanding. My question was if it’s still possible to access the public key from which the caller id is stemming - since in the caller canister there is a signature and thus in case of secp256k1 callees also a recoverable public key. It seems though that from Motoko only the already hash derived ICP id “Principal” is available from which the Ethereum Address is not recoverable.

I’ve now built a work-around in which we require callees to first register themselves with the Canister using their secp256k1 public key. Using the sha2 and sha3 mops libraries [Mops • Motoko Package Manager](https://mops.one/search/sha3) we then calculate from the long-form of the secp256k1 pubkey the correct ethereum address and icp principal to create a map we can use later to make the caller lookup.

Flattened pseudo-code to see the critical parts:

```motoko
  public func register(public_key : Blob) : async Nat {
    if (Array.size(pubkey) != 65) {
      Debug.trap("Invalid public key size: " # debug_show(Array.size(pubkey)));
    };
    let eth_address = address_from_pubkey(public_key)
    let icp_principal = principal_from_pubkey(public_key)

    ... make cross-chain call to know if permissed and store in cache ...
  }

  public func address_from_pubkey(public_key : Blob) : Blob {
    var pubkey = Blob.toArray(public_key);

    let subject = Array.subArray(pubkey, 1, 64);
    var sha = SHA3.Keccak(256);
    sha.update(subject);
    let result = sha.finalize();
    Blob.fromArray(Array.subArray(result, 12, 20))
  };

  public func principal_from_pubkey(public_key : Blob) : Principal {
    var pubkey = Blob.toArray(public_key);
    let hash = Blob.toArray(Sha256.fromBlob(#sha224, derFromPublicKey(public_key)));
    let id = Blob.fromArray(Array.append<Nat8>(hash, [2]));
    Principal.fromBlob(id)
  };

```

And then at runtime we check access using the caller identification:

```motoko
  public shared(msg) func add_message(key_id : Blob, ciphertext : Blob) : async Result.Result<(), Text> {
    assert_membership(msg.caller);

    ... logic ...
  };

  func assert_membership(member : Principal) {
    if <not principal is stored in cache as allowed> {
      Debug.print("Not allowed");
      assert false;
    };
  };

```
