Skip to content

feat: add LOOKUP_DELEGATED_ADDRESS precompile wrapper#9

Open
pali101 wants to merge 14 commits intofilecoin-project:mainfrom
pali101:feat/lookup-delegated-address-v2
Open

feat: add LOOKUP_DELEGATED_ADDRESS precompile wrapper#9
pali101 wants to merge 14 commits intofilecoin-project:mainfrom
pali101:feat/lookup-delegated-address-v2

Conversation

@pali101
Copy link
Copy Markdown
Contributor

@pali101 pali101 commented Mar 1, 2026

Summary

Implements the LOOKUP_DELEGATED_ADDRESS precompile wrapper (0xfe...02) to retrieve an actor's delegated address (f4) and extract the Ethereum-style address using optimized inline assembly.

The primary API returns address for ergonomic use in Solidity while preserving raw bytes helpers for applications that require the f4 encoding.

Addresses all reviewer feedback from the initial submission (#6).

Key Changes

  • src/FVMActor.sol

    • Added tryLookupDelegatedAddress(uint64) → (bool, address): returns empty and exists=false if actor has no delegated address
    • Added lookupDelegatedAddress(uint64) → address: strict variant, reverts with DelegatedAddressNotFound(actorId) if no delegated address exists
    • Added tryLookupDelegatedAddressBytes(uint64) → (bool, bytes): returns empty and exists=false if actor has no delegated address; defaults to zero slot (0x60) to safely represent empty bytes without allocation
    • Added lookupDelegatedAddressBytes(uint64) → bytes: strict raw-bytes variant
  • src/FVMAddress.sol

    • Added toEthAddress(bytes) → address: validates f410 format (length 22, prefix 0x040a) and extracts the last 20 bytes as an Ethereum address
    • Added InvalidDelegatedAddress error
  • src/mocks/FVMActor.sol

    • Added delegatedAddressMocks mapping to support LOOKUP_DELEGATED_ADDRESS in the unified fallback
    • Added mockLookupDelegatedAddress(uint64, bytes) and mockLookupDelegatedAddress(uint64, address) overloads
    • Fallback routes on msg.data.length == 32 to distinguish lookup from resolve calls
  • src/mocks/MockFVMTest.sol

    • Etches and copies storage for LOOKUP_DELEGATED_ADDRESS alongside RESOLVE_ADDRESS
  • src/Demo.sol

Reviewer Notes

  • FVMLookupDelegatedAddress merged into FVMActor per feedback
  • toEthAddress moved to FVMAddress per feedback
  • Precompile revert data propagated directly via returndatacopy + revert(0, returnSize), consistent with feat: add RESOLVE_ADDRESS precompile wrapper #5

Update (after review feedback)

Based on reviewer feedback, the delegated address API has been updated to return address by default since delegated addresses are native to the FEVM.

The raw bytes helpers are still available for callers that need the f4/f410 encoding.

Updated API:

  • tryLookupDelegatedAddress(uint64)(bool, address)
  • lookupDelegatedAddress(uint64)address
  • tryLookupDelegatedAddressBytes(uint64)(bool, bytes)
  • lookupDelegatedAddressBytes(uint64)bytes

Closes #2

@wjmelements
Copy link
Copy Markdown
Collaborator

I think it could be more natural to return address for delegated address since a delegated address is native to the FEVM. However, we should keep the bytes method in case somebody needs to format it that way, so they aren't forced to decode and re-encode it.

@wjmelements
Copy link
Copy Markdown
Collaborator

This is very close to merging. Can you link a deployed Demo contract with your next push?

pali101 added 2 commits March 20, 2026 13:01
- Combined BigBrain code blocks in README.md for clarity.
- Moved _toEthAddress under the correct test banner in Address.t.sol.
@pali101
Copy link
Copy Markdown
Contributor Author

pali101 commented Mar 20, 2026

Here is the deployed Demo contract on the Calibration testnet: 0x09f149b6AeCFCeF271fA2E84b01205373c84c16D

I also ran a quick profiling trick: by using cast send instead of cast call on the view function, I forced a transaction to get the exact WASM gas trace on the block explorer. This comparison is before and after the returndatasize() and input overwrite optimizations.

Before: 3205778 (txn hash: 0x13de8f573707c7845b7764c72a3f816a36781e47a5323beef967a4bb811b3c6a)
After: 2702216 (txn hash: 0x3fe4999b1037c08b3801f1ade2c705a941a3e80da2b3bc3dbb7d9cf494ad9e9b)

Saving: 503562 gas

assertEq(result, ethAddr, "EVM address should match for max uint64 actor ID");
}

function testPrecompileRevertOnLargeId() public {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning (2018): Function state mutability can be restricted to view
   --> test/Actor.t.sol:335:5:
    |
335 |     function testPrecompileRevertOnLargeId() public {
    |     ^ (Relevant source part starts here and spans across multiple lines).

Copy link
Copy Markdown
Collaborator

@wjmelements wjmelements Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function testPrecompileRevertOnLargeId() public {
function testPrecompileRevertOnLargeId() public view {

Comment on lines +29 to +30
// Validate that actor ID fits in u64 (max u64 = 2^64 - 1)
require(actorIdFull <= type(uint64).max, "Invalid actor ID: exceeds max u64");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should revert with empty data

Suggested change
// Validate that actor ID fits in u64 (max u64 = 2^64 - 1)
require(actorIdFull <= type(uint64).max, "Invalid actor ID: exceeds max u64");
// Validate that actor ID fits in u64 (max u64 = 2^64 - 1)
if (actorIdFull > type(uint64).max) {
assembly ("memory-safe") {
revert(0, 0)
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, add test coverage for this case

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude explains:

  For lookup_delegated_address (precompile 0xfe...02), read_value::<u64>() reads a 256-bit word from the ABI-encoded input and calls drop_zeros::<24>() on the upper 24
  bytes. If any of those bytes are non-zero (i.e., the value exceeds u64::MAX), drop_zeros returns OverflowError, which converts to PrecompileError::InvalidInput, which
  causes call_precompile to return Err.

  That Err path in call.rs:189-193 maps to: success = 0 (revert), empty return data.

@wjmelements wjmelements added the enhancement New feature or request label Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support precompile: LookupDelegatedAddress

2 participants