Skip to content

Commit 3e89921

Browse files
committed
feat(actor): return delegated EVM address by default and preserve bytes lookup
1 parent 93d64a6 commit 3e89921

3 files changed

Lines changed: 143 additions & 40 deletions

File tree

src/Demo.sol

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,28 @@ contract Demo {
8989
// delegated address lookup
9090
using FVMActor for uint64;
9191

92-
/// @notice Try to look up the delegated address of an actor by ID
93-
function tryLookupDelegatedAddress(uint64 actorId)
92+
/// @notice Try to look up the delegated EVM address of an actor by ID
93+
function tryLookupDelegatedAddress(uint64 actorId) external view returns (bool exists, address ethAddress) {
94+
return actorId.tryLookupDelegatedAddress();
95+
}
96+
97+
/// @notice Look up the delegated EVM address of an actor, requiring it exists
98+
function lookupDelegatedAddress(uint64 actorId) external view returns (address ethAddress) {
99+
return actorId.lookupDelegatedAddress();
100+
}
101+
102+
/// @notice Try to look up the delegated address of an actor by ID as raw bytes
103+
function tryLookupDelegatedAddressBytes(uint64 actorId)
94104
external
95105
view
96106
returns (bool exists, bytes memory delegatedAddress)
97107
{
98-
return actorId.tryLookupDelegatedAddress();
108+
return actorId.tryLookupDelegatedAddressBytes();
99109
}
100110

101-
/// @notice Look up the delegated address of an actor, requiring it exists
102-
function lookupDelegatedAddress(uint64 actorId) external view returns (bytes memory delegatedAddress) {
103-
return actorId.lookupDelegatedAddress();
111+
/// @notice Look up the delegated address of an actor as raw bytes, requiring it exists
112+
function lookupDelegatedAddressBytes(uint64 actorId) external view returns (bytes memory delegatedAddress) {
113+
return actorId.lookupDelegatedAddressBytes();
104114
}
105115

106116
// address encoding

src/FVMActor.sol

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,36 @@ library FVMActor {
105105
// DELEGATED ADDRESS LOOKUP IMPLEMENTATION
106106
// =============================================================
107107

108-
/// @notice Tries to look up the delegated address of an actor by ID
108+
/// @notice Tries to look up the delegated address of an actor by ID as an EVM address
109+
/// @dev Returns exists=false if actor doesn't exist or has no delegated address.
110+
/// Reverts with InvalidDelegatedAddress if the actor's delegated address is not f410.
111+
/// @param actorId The actor ID (uint64)
112+
/// @return exists Whether the delegated address exists
113+
/// @return ethAddress The EVM address, valid only if exists is true
114+
function tryLookupDelegatedAddress(uint64 actorId) internal view returns (bool exists, address ethAddress) {
115+
bytes memory delegatedAddress;
116+
(exists, delegatedAddress) = tryLookupDelegatedAddressBytes(actorId);
117+
if (exists) {
118+
ethAddress = FVMAddress.toEthAddress(delegatedAddress);
119+
}
120+
}
121+
122+
/// @notice Looks up the delegated address as an EVM address, requiring it exists
123+
/// @dev Reverts if the actor has no delegated address or if it is not an f410 address.
124+
/// @param actorId The actor ID (uint64)
125+
/// @return ethAddress The EVM address
126+
function lookupDelegatedAddress(uint64 actorId) internal view returns (address ethAddress) {
127+
bool exists;
128+
(exists, ethAddress) = tryLookupDelegatedAddress(actorId);
129+
if (!exists) revert DelegatedAddressNotFound(actorId);
130+
}
131+
132+
/// @notice Tries to look up the delegated address of an actor by ID as raw bytes
109133
/// @dev Returns empty bytes and exists=false if actor doesn't exist or has no delegated address
110134
/// @param actorId The actor ID (uint64)
111135
/// @return exists Whether the delegated address exists
112136
/// @return delegatedAddress The delegated address as raw bytes
113-
function tryLookupDelegatedAddress(uint64 actorId)
137+
function tryLookupDelegatedAddressBytes(uint64 actorId)
114138
internal
115139
view
116140
returns (bool exists, bytes memory delegatedAddress)
@@ -146,13 +170,13 @@ library FVMActor {
146170
}
147171
}
148172

149-
/// @notice Looks up the delegated address, requiring it exists
173+
/// @notice Looks up the delegated address as raw bytes, requiring it exists
150174
/// @dev Reverts if the actor has no delegated address
151175
/// @param actorId The actor ID (uint64)
152176
/// @return delegatedAddress The delegated address as raw bytes
153-
function lookupDelegatedAddress(uint64 actorId) internal view returns (bytes memory delegatedAddress) {
177+
function lookupDelegatedAddressBytes(uint64 actorId) internal view returns (bytes memory delegatedAddress) {
154178
bool exists;
155-
(exists, delegatedAddress) = tryLookupDelegatedAddress(actorId);
179+
(exists, delegatedAddress) = tryLookupDelegatedAddressBytes(actorId);
156180
if (!exists) revert DelegatedAddressNotFound(actorId);
157181
}
158182
}

test/Actor.t.sol

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@ contract ResolveAddressTest is MockFVMTest {
2222
return addr.getActorId();
2323
}
2424

25-
function _lookupDelegatedAddressStrict(uint64 actorId) public view returns (bytes memory) {
25+
function _lookupDelegatedAddressStrict(uint64 actorId) public view returns (address) {
2626
return actorId.lookupDelegatedAddress();
2727
}
2828

29+
function _lookupDelegatedAddressBytesStrict(uint64 actorId) public view returns (bytes memory) {
30+
return actorId.lookupDelegatedAddressBytes();
31+
}
32+
33+
function _tryLookupDelegatedAddress(uint64 actorId) public view returns (bool, address) {
34+
return actorId.tryLookupDelegatedAddress();
35+
}
36+
2937
// =============================================================
3038
// CONSTRUCTOR MOCK TESTS
3139
// =============================================================
@@ -249,39 +257,50 @@ contract ResolveAddressTest is MockFVMTest {
249257
}
250258

251259
// =============================================================
252-
// DELEGATED ADDRESS TESTS
260+
// DELEGATED ADDRESS TESTS (address)
253261
// =============================================================
254262

255263
function testTryLookupDelegatedAddressExists() public {
256264
uint64 actorId = 1234;
257-
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), address(1));
265+
address ethAddr = address(1);
258266

259-
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
267+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
260268

261-
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddress();
269+
(bool exists, address result) = actorId.tryLookupDelegatedAddress();
262270

263271
assertTrue(exists, "Actor should have a delegated address");
264-
assertEq(result, expected, "Delegated address should match");
272+
assertEq(result, ethAddr, "EVM address should match");
265273
}
266274

267275
function testTryLookupDelegatedAddressDoesNotExist() public view {
268276
uint64 actorId = 9999;
269277
// No mock set — precompile returns empty
270278

271-
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddress();
279+
(bool exists, address result) = actorId.tryLookupDelegatedAddress();
272280

273281
assertFalse(exists, "Actor should have no delegated address");
274-
assertEq(result.length, 0, "Should return empty bytes");
282+
assertEq(result, address(0), "Should return zero address");
283+
}
284+
285+
function testTryLookupDelegatedAddressRevertsOnNonF410Bytes() public {
286+
uint64 actorId = 1234;
287+
// f1 secp256k1 address: protocol 0x01 + 20-byte payload (not f410)
288+
bytes memory nonF410 = abi.encodePacked(uint8(0x01), bytes20(address(1)));
289+
290+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, nonF410);
291+
292+
vm.expectRevert(FVMAddress.InvalidDelegatedAddress.selector);
293+
this._tryLookupDelegatedAddress(actorId);
275294
}
276295

277296
function testLookupDelegatedAddressExists() public {
278297
uint64 actorId = 42;
279-
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), address(1));
298+
address ethAddr = address(1);
280299

281-
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
300+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
282301

283-
bytes memory result = actorId.lookupDelegatedAddress();
284-
assertEq(result, expected, "Strict lookup should return delegated address");
302+
address result = actorId.lookupDelegatedAddress();
303+
assertEq(result, ethAddr, "Strict lookup should return EVM address");
285304
}
286305

287306
function testLookupDelegatedAddressReverts() public {
@@ -293,24 +312,24 @@ contract ResolveAddressTest is MockFVMTest {
293312

294313
function testLookupDelegatedAddressActorIdZero() public {
295314
uint64 actorId = 0;
296-
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), address(1));
315+
address ethAddr = address(1);
297316

298-
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
317+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
299318

300-
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddress();
319+
(bool exists, address result) = actorId.tryLookupDelegatedAddress();
301320
assertTrue(exists, "Actor ID 0 should work");
302-
assertEq(result, expected, "Delegated address should match for actor ID 0");
321+
assertEq(result, ethAddr, "EVM address should match for actor ID 0");
303322
}
304323

305324
function testLookupDelegatedAddressActorIdMaxUint64() public {
306325
uint64 actorId = type(uint64).max;
307-
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), address(2));
326+
address ethAddr = address(2);
308327

309-
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
328+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
310329

311-
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddress();
330+
(bool exists, address result) = actorId.tryLookupDelegatedAddress();
312331
assertTrue(exists, "Max uint64 actor ID should work");
313-
assertEq(result, expected, "Delegated address should match for max uint64 actor ID");
332+
assertEq(result, ethAddr, "EVM address should match for max uint64 actor ID");
314333
}
315334

316335
function testPrecompileRevertOnLargeId() public {
@@ -321,13 +340,63 @@ contract ResolveAddressTest is MockFVMTest {
321340
assertFalse(success, "Precompile should revert on ID > u64");
322341
}
323342

343+
function testFuzzTryLookupDelegatedAddress(uint64 actorId, address ethAddr) public {
344+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
345+
346+
(bool exists, address result) = actorId.tryLookupDelegatedAddress();
347+
assertTrue(exists, "Actor should exist");
348+
assertEq(result, ethAddr, "Fuzzed EVM address should match");
349+
}
350+
351+
// =============================================================
352+
// DELEGATED ADDRESS TESTS (bytes)
353+
// =============================================================
354+
355+
function testTryLookupDelegatedAddressBytesExists() public {
356+
uint64 actorId = 1234;
357+
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), address(1));
358+
359+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
360+
361+
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddressBytes();
362+
363+
assertTrue(exists, "Actor should have a delegated address");
364+
assertEq(result, expected, "Raw bytes should match");
365+
}
366+
367+
function testTryLookupDelegatedAddressBytesDoesNotExist() public view {
368+
uint64 actorId = 9999;
369+
370+
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddressBytes();
371+
372+
assertFalse(exists, "Actor should have no delegated address");
373+
assertEq(result.length, 0, "Should return empty bytes");
374+
}
375+
376+
function testLookupDelegatedAddressBytesExists() public {
377+
uint64 actorId = 42;
378+
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), address(1));
379+
380+
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
381+
382+
bytes memory result = actorId.lookupDelegatedAddressBytes();
383+
assertEq(result, expected, "Bytes lookup should return raw delegated address");
384+
}
385+
386+
function testLookupDelegatedAddressBytesReverts() public {
387+
uint64 actorId = 9999;
388+
389+
vm.expectRevert(abi.encodeWithSelector(FVMActor.DelegatedAddressNotFound.selector, actorId));
390+
this._lookupDelegatedAddressBytesStrict(actorId);
391+
}
392+
324393
function testMockLookupDelegatedAddressUsingAddress() public {
325394
uint64 actorId = 100;
326395
address ethAddr = address(1);
327396

328397
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
329398

330-
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddress();
399+
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddressBytes();
331400
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), ethAddr);
332401

333402
assertTrue(exists, "Actor should have a delegated address");
@@ -340,20 +409,20 @@ contract ResolveAddressTest is MockFVMTest {
340409

341410
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
342411

343-
bytes memory result = actorId.lookupDelegatedAddress();
412+
bytes memory result = actorId.lookupDelegatedAddressBytes();
344413
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), ethAddr);
345414

346-
assertEq(result, expected, "Strict lookup via address mock should match f410 encoding");
415+
assertEq(result, expected, "Bytes lookup via address mock should match f410 encoding");
347416
}
348417

349-
function testFuzzTryLookupDelegatedAddress(uint64 actorId, address ethAddr) public {
418+
function testFuzzTryLookupDelegatedAddressBytes(uint64 actorId, address ethAddr) public {
350419
bytes memory expected = abi.encodePacked(uint8(0x04), uint8(0x0a), ethAddr);
351420

352421
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, expected);
353422

354-
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddress();
423+
(bool exists, bytes memory result) = actorId.tryLookupDelegatedAddressBytes();
355424
assertTrue(exists, "Actor should exist");
356-
assertEq(result, expected, "Fuzzed delegated address should match");
425+
assertEq(result, expected, "Fuzzed delegated address bytes should match");
357426
}
358427

359428
// =============================================================
@@ -402,7 +471,7 @@ contract ResolveAddressTest is MockFVMTest {
402471
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
403472

404473
uint64 resolvedId = ethAddr.getActorId();
405-
address recovered = resolvedId.lookupDelegatedAddress().toEthAddress();
474+
address recovered = resolvedId.lookupDelegatedAddress();
406475

407476
assertEq(resolvedId, actorId, "Actor ID should match");
408477
assertEq(recovered, ethAddr, "Recovered ETH address should match original");
@@ -418,7 +487,7 @@ contract ResolveAddressTest is MockFVMTest {
418487
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
419488

420489
uint64 resolvedId = f410Addr.getActorId();
421-
address recovered = resolvedId.lookupDelegatedAddress().toEthAddress();
490+
address recovered = resolvedId.lookupDelegatedAddress();
422491

423492
assertEq(resolvedId, actorId, "Actor ID should match");
424493
assertEq(recovered, ethAddr, "Recovered ETH address should match original");
@@ -436,7 +505,7 @@ contract ResolveAddressTest is MockFVMTest {
436505
LOOKUP_DELEGATED_ADDRESS_PRECOMPILE.mockLookupDelegatedAddress(actorId, ethAddr);
437506

438507
uint64 resolvedId = ethAddr.getActorId();
439-
address recovered = resolvedId.lookupDelegatedAddress().toEthAddress();
508+
address recovered = resolvedId.lookupDelegatedAddress();
440509

441510
assertEq(resolvedId, actorId);
442511
assertEq(recovered, ethAddr);

0 commit comments

Comments
 (0)