Skip to content

Commit af48efb

Browse files
dtbuchholzsanderpick
authored andcommitted
feat: convert 'f0...' strings to eth addrs (#55)
There are two places where an `f0...` string was included in the response data: - `getBlob()` and the `subscriber` field - `getAccount()` and the `to` field with a credit approval These are now converted to EVM addresses: - We call the FVM precompile to look up the delegated address for a given actor ID number - If a value is returned, we convert it to an EVM address - If a value is not returned, we assume it's a masked ID address and convert it accordingly For example, calling `getAccount()` used to return something like this: ``` (6, 499..., 504..., 0x000..., 7200, [("f0127", ...) ``` Now, it returns an address instead of `"f0127"`: ``` (6, 499..., 504..., 0x000..., 7200, [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, ...) ``` Also, the tests were failing, so the `foundry.toml` settings have been updated to fix this. The root cause was [here](https://github.com/hokunet/contracts/actions/runs/12779553700/job/35624355833#step:7:79) where `LibBlob` and `LibBucket` had a negative runtime margin value, causing the CI process to exit early. Lastly, this includes some linting fixes are included wrt tests.
1 parent 5a6f527 commit af48efb

File tree

10 files changed

+88
-40
lines changed

10 files changed

+88
-40
lines changed

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -385,13 +385,13 @@ We can get the credit account info for the address at `EVM_ADDRESS` (the variabl
385385
you could provide any account's EVM public key that exists in the subnet.
386386

387387
```sh
388-
cast abi-decode "getAccount(address)((uint64,uint256,uint256,address,uint64,(string,(uint256,uint256,uint64,uint256,uint256))[],uint64,uint256))" $(cast call --rpc-url $ETH_RPC_URL $CREDIT "getAccount(address)" $EVM_ADDRESS)
388+
cast abi-decode "getAccount(address)((uint64,uint256,uint256,address,uint64,(address,(uint256,uint256,uint64,uint256,uint256))[],uint64,uint256))" $(cast call --rpc-url $ETH_RPC_URL $CREDIT "getAccount(address)" $EVM_ADDRESS)
389389
```
390390

391391
This will return the following values:
392392

393393
```
394-
(6, 4999999999999999454276000000000000000000 [4.999e39], 504150000000000000000000 [5.041e23], 0x0000000000000000000000000000000000000000, 7200, [("f0127", (12345000000000000000000 [1.234e22], 987654321 [9.876e8], 11722 [1.172e4], 0, 0))], 86400 [8.64e4], 4999999984799342175554 [4.999e21])
394+
(6, 4999999999999999454276000000000000000000 [4.999e39], 504150000000000000000000 [5.041e23], 0x0000000000000000000000000000000000000000, 7200, [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, (12345000000000000000000 [1.234e22], 987654321 [9.876e8], 11722 [1.172e4], 0, 0))], 86400 [8.64e4], 4999999984799342175554 [4.999e21])
395395
```
396396

397397
Which maps to the `Account` struct:
@@ -414,7 +414,7 @@ approvals authorized. We can expand this to be interpreted as the following:
414414

415415
```solidity
416416
struct Approval {
417-
string to; // f0127
417+
string to; // 0x90F79bf6EB2c4f870365E785982E1f101E93b906
418418
CreditApproval approval; // See CreditApproval struct below
419419
}
420420
@@ -489,13 +489,13 @@ struct CreditApproval {
489489
Fetch the credit balance for the address at `EVM_ADDRESS`:
490490

491491
```sh
492-
cast abi-decode "getCreditBalance(address)((uint256,uint256,address,uint64,(string,(uint256,uint256,uint64,uint256,uint256))[],uint256))" $(cast call --rpc-url $ETH_RPC_URL $CREDIT "getCreditBalance(address)" $EVM_ADDRESS)
492+
cast abi-decode "getCreditBalance(address)((uint256,uint256,address,uint64,(address,(uint256,uint256,uint64,uint256,uint256))[],uint256))" $(cast call --rpc-url $ETH_RPC_URL $CREDIT "getCreditBalance(address)" $EVM_ADDRESS)
493493
```
494494

495495
This will return the following values:
496496

497497
```
498-
(5001999999999998208637000000000000000000 [5.001e39], 518400000000000000000000 [5.184e23], 0x0000000000000000000000000000000000000000, 6932, [("f0127", (0, 0, 0, 0, 0))], 1)
498+
(5001999999999998208637000000000000000000 [5.001e39], 518400000000000000000000 [5.184e23], 0x0000000000000000000000000000000000000000, 6932, [(0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65, (0, 0, 0, 0, 0))], 1)
499499
```
500500

501501
Which maps to the `Balance` struct:
@@ -510,7 +510,7 @@ struct Balance {
510510
}
511511
512512
struct Approval {
513-
string to; // f0127
513+
string to; // 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65
514514
CreditApproval approval; // See CreditApproval struct below
515515
}
516516
@@ -1046,13 +1046,13 @@ This will emit an `OverwriteBlob` event and overwrite the blob in the network.
10461046
##### Get a blob
10471047

10481048
```sh
1049-
cast abi-decode "getBlob(string)((uint64,string,(string,(string,(uint64,uint64,string,address,bool))[])[],uint8))" $(cast call --rpc-url $ETH_RPC_URL $BLOBS "getBlob(string)" "rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq")
1049+
cast abi-decode "getBlob(string)((uint64,string,(address,(string,(uint64,uint64,string,address,bool))[])[],uint8))" $(cast call --rpc-url $ETH_RPC_URL $BLOBS "getBlob(string)" "rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq")
10501050
```
10511051

10521052
This will return the following response:
10531053

10541054
```sh
1055-
(6, "utiakbxaag7udhsriu6dm64cgr7bk4zahiudaaiwuk6rfv43r3rq", [("f0124", [("foo", (4825, 91225 [9.122e4], "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq", 0x0000000000000000000000000000000000000000, false))])], 2)
1055+
(6, "utiakbxaag7udhsriu6dm64cgr7bk4zahiudaaiwuk6rfv43r3rq", [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, [("foo", (4825, 91225 [9.122e4], "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq", 0x0000000000000000000000000000000000000000, false))])], 2)
10561056
```
10571057

10581058
Which maps to the `Blob` struct:
@@ -1066,7 +1066,7 @@ struct Blob {
10661066
}
10671067
10681068
struct Subscriber {
1069-
string subscriber; // f0124
1069+
address subscriber; // 0x90F79bf6EB2c4f870365E785982E1f101E93b906
10701070
SubscriptionGroup[] subscriptionGroup; // See `SubscriptionGroup` struct below
10711071
}
10721072
@@ -1122,7 +1122,7 @@ cast abi-decode "getAddedBlobs(uint32)((string,(address,string,string)[])[])" $(
11221122
This returns the values of added blobs, up to the `size` passed as the parameter:
11231123

11241124
```sh
1125-
[("rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq", [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, "Default", "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq")])]
1125+
[("rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq", [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, "default", "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq")])]
11261126
```
11271127

11281128
Which maps to an array of the `BlobTuple` struct:
@@ -1135,7 +1135,7 @@ struct BlobTuple {
11351135
11361136
struct BlobSourceInfo {
11371137
address subscriber; // 0x90F79bf6EB2c4f870365E785982E1f101E93b906
1138-
string subscriptionId; // "Default"
1138+
string subscriptionId; // "default"
11391139
string source; // "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq"
11401140
}
11411141
```
@@ -1149,7 +1149,7 @@ cast abi-decode "getPendingBlobs(uint32)((string,(address,string,string)[])[])"
11491149
This returns the values of pending blobs, up to the `size` passed as the parameter:
11501150

11511151
```sh
1152-
[("rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq", [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, "Default", "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq")])]
1152+
[("rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq", [(0x90F79bf6EB2c4f870365E785982E1f101E93b906, "default", "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq")])]
11531153
```
11541154

11551155
Which maps to an array of the `BlobTuple` struct:
@@ -1162,7 +1162,7 @@ struct BlobTuple {
11621162
11631163
struct BlobSourceInfo {
11641164
address subscriber; // 0x90F79bf6EB2c4f870365E785982E1f101E93b906
1165-
string subscriptionId; // "Default"
1165+
string subscriptionId; // "default"
11661166
string source; // "cydkrslhbj4soqppzc66u6lzwxgjwgbhdlxmyeahytzqrh65qtjq"
11671167
}
11681168
```

foundry.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ test = "test"
66
build_info = true
77
extra_output = ["storageLayout"]
88
gas_reports = ["Hoku"]
9+
solc_version = "0.8.26"
10+
optimizer = true
11+
optimizer_runs = 200
912
ffi = true
1013
ast = true
1114
via_ir = true

src/types/BlobTypes.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ struct Account {
2828
}
2929

3030
/// @dev Credit approval from one account to another.
31-
/// @param to (string): Optional restriction on caller address, e.g., an object store. Use zero address if
31+
/// @param to (address): Optional restriction on caller address, e.g., an object store. Use zero address if
3232
/// unused, indicating a null value.
3333
/// @param approval (CreditApproval): The credit approval. See {CreditApproval} for more details.
3434
struct Approval {
35-
string to;
35+
address to;
3636
CreditApproval approval;
3737
}
3838

@@ -185,10 +185,10 @@ enum BlobStatus {
185185
}
186186

187187
/// @dev A subscriber and their subscription groups.
188-
/// @param subscriber (string): The subscriber address as a string value (e.g., "f0124").
188+
/// @param subscriber (address): The subscriber address.
189189
/// @param subscriptionGroup (SubscriptionGroup[]): The subscription groups. See {SubscriptionGroup} for more details.
190190
struct Subscriber {
191-
string subscriber;
191+
address subscriber;
192192
SubscriptionGroup[] subscriptionGroup;
193193
}
194194

src/wrappers/LibBlob.sol

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ library LibBlob {
111111
bytes[2][] memory decoded = data.decodeCborMappingToBytes();
112112
approvals = new Approval[](decoded.length);
113113
for (uint256 i = 0; i < decoded.length; i++) {
114-
// TODO: this address (string) value is mainnet prefixed with `f` instead of `t`
115-
approvals[i].to = string(decoded[i][0]);
114+
// The `to` value is an ID address string, like `f0123`, so we convert it to an address
115+
approvals[i].to = decoded[i][0].decodeCborActorIdStringToAddress();
116116
approvals[i].approval = decodeCreditApproval(decoded[i][1]);
117117
}
118118
}
@@ -175,7 +175,8 @@ library LibBlob {
175175
bytes[2][] memory decoded = data.decodeCborMappingToBytes();
176176
subscribers = new Subscriber[](decoded.length);
177177
for (uint256 i = 0; i < decoded.length; i++) {
178-
subscribers[i].subscriber = string(decoded[i][0]);
178+
// The `subscriber` value is an ID address string, like `f0123`, so we convert it to an address
179+
subscribers[i].subscriber = decoded[i][0].decodeCborActorIdStringToAddress();
179180
subscribers[i].subscriptionGroup = decodeSubscriptionGroup(decoded[i][1]);
180181
}
181182
}

src/wrappers/LibWasm.sol

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
pragma solidity ^0.8.26;
33

4+
import {PrecompilesAPI} from "@filecoin-solidity/v0.8/PrecompilesAPI.sol";
45
import {BytesCBOR} from "@filecoin-solidity/v0.8/cbor/BytesCbor.sol";
56
import {FilecoinCBOR, Misc} from "@filecoin-solidity/v0.8/cbor/FilecoinCbor.sol";
67
import {CommonTypes} from "@filecoin-solidity/v0.8/types/CommonTypes.sol";
@@ -155,8 +156,9 @@ library LibWasm {
155156
result := shr(96, mload(add(addr, 34)))
156157
}
157158
} else if (addr[0] == 0x00) {
158-
// Otherwise, it should be an ID address
159-
result = decodeCborActorIdBytesToAddress(addr);
159+
// Otherwise, it should be an ID address—and we need to check if it's delegated or masked
160+
uint64 actorId = decodeCborActorIdBytesToUint64(addr);
161+
result = getDelegatedOrMaskedAddressFromActorId(actorId);
160162
} else {
161163
revert InvalidValue("Invalid address length or protocol");
162164
}
@@ -166,7 +168,24 @@ library LibWasm {
166168
/// @dev Decode CBOR encoded actor ID bytes to an Ethereum address.
167169
/// @param data The encoded CBOR actor ID bytes.
168170
/// @return result The decoded Ethereum address.
169-
function decodeCborActorIdBytesToAddress(bytes memory data) internal view returns (address) {
171+
function decodeCborActorIdStringToAddress(bytes memory data) internal view returns (address) {
172+
bytes memory actorIdStr = new bytes(data.length - 1);
173+
// Remove the first byte, which is the network prefix (`f`, which is 0x66)
174+
assembly {
175+
// Copy to the new array, starting from the second byte
176+
let srcPtr := add(add(data, 32), 1) // +32 for length prefix, +1 to skip first byte
177+
let destPtr := add(actorIdStr, 32) // +32 for length prefix
178+
mstore(destPtr, mload(srcPtr))
179+
}
180+
uint64 actorId = 0;
181+
for (uint64 i = 0; i < actorIdStr.length; i++) {
182+
// Just subtract 0x30 to convert from ASCII to number
183+
actorId = actorId * 10 + (uint8(actorIdStr[i]) - 0x30);
184+
}
185+
return getDelegatedOrMaskedAddressFromActorId(actorId);
186+
}
187+
188+
function decodeCborActorIdBytesToUint64(bytes memory data) internal pure returns (uint64) {
170189
// First, decode the LEB128 bytes value to the actor ID
171190
uint64 result = 0;
172191
uint64 shift = 0;
@@ -180,7 +199,16 @@ library LibWasm {
180199
}
181200
shift += 7;
182201
}
183-
return FilAddressIdConverter.toAddress(result);
202+
return result;
203+
}
204+
205+
/// @dev Get the delegated address for an actor ID, or the masked address if the actor ID is not delegated.
206+
/// @param actorId The actor ID.
207+
/// @return delegatedAddress The delegated address as bytes. Will be zero value if the actor ID is not delegated.
208+
function getDelegatedOrMaskedAddressFromActorId(uint64 actorId) internal view returns (address) {
209+
bytes memory data = PrecompilesAPI.lookupDelegatedAddress(actorId);
210+
if (data.length == 0) return FilAddressIdConverter.toAddress(actorId);
211+
return FilAddresses.toEthAddress(CommonTypes.FilAddress(data));
184212
}
185213

186214
/// @dev Decode a CBOR encoded Wasm actor address to a string.

test/LibBlob.t.sol

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,16 @@ contract LibBlobTest is Test {
6666
assertEq(account.creditSponsor, address(0));
6767
assertEq(account.lastDebitEpoch, 6196);
6868
assertEq(account.approvals.length, 2);
69-
assertEq(account.approvals[0].to, "f0125");
69+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
70+
// This is because tests don't access the delegated address lookup precompile via the FVM runtime
71+
assertEq(account.approvals[0].to, 0xFF0000000000000000000000000000000000007D);
7072
assertEq(account.approvals[0].approval.creditLimit, 0);
7173
assertEq(account.approvals[0].approval.gasFeeLimit, 0);
7274
assertEq(account.approvals[0].approval.expiry, 0);
7375
assertEq(account.approvals[0].approval.creditUsed, 0);
7476
assertEq(account.approvals[0].approval.gasFeeUsed, 0);
75-
assertEq(account.approvals[1].to, "f0127");
77+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
78+
assertEq(account.approvals[1].to, 0xff0000000000000000000000000000000000007f);
7679
assertEq(account.approvals[1].approval.creditLimit, 0);
7780
assertEq(account.approvals[1].approval.gasFeeLimit, 0);
7881
assertEq(account.approvals[1].approval.expiry, 0);
@@ -85,7 +88,8 @@ contract LibBlobTest is Test {
8588
data =
8689
hex"880652000eb194f8e1ae50e56f45b07f78a0d400004b006dc68533171604000000f6191834a1656630313235854c0052b7d2dcc80cd2e4000000430003e81927844b0005650e85ae5dfb900000401a000151804b00010f0cf0647f152e0ab9";
8790
account = data.decodeAccount();
88-
assertEq(account.approvals[0].to, "f0125");
91+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
92+
assertEq(account.approvals[0].to, 0xFF0000000000000000000000000000000000007D);
8993
assertEq(account.approvals[0].approval.creditLimit, 100000000000000000000000000);
9094
assertEq(account.approvals[0].approval.gasFeeLimit, 1000);
9195
assertEq(account.approvals[0].approval.expiry, 10116);
@@ -174,7 +178,8 @@ contract LibBlobTest is Test {
174178
bytes memory data =
175179
hex"a1656630313234a17840343564616464393261326161373431383133323638376639613432313439306261643963356161643332656134653937623135356533373739316630306435308519021f1a0001539f982018fd18bb1871185b08188508184418a1183e182b18b518980118cb18a60218bf183b18351118720418fb18f41836188101181b18ef18941839f6f4";
176180
Subscriber[] memory subscribers = LibBlob.decodeSubscribers(data);
177-
assertEq(subscribers[0].subscriber, "f0124");
181+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
182+
assertEq(subscribers[0].subscriber, 0xFf0000000000000000000000000000000000007C);
178183
assertEq(
179184
subscribers[0].subscriptionGroup[0].subscriptionId,
180185
"45dadd92a2aa7418132687f9a421490bad9c5aad32ea4e97b155e37791f00d50"
@@ -192,7 +197,8 @@ contract LibBlobTest is Test {
192197
data =
193198
hex"a1656630313234a2784034316664336363386562303731323262663165356564383562306236393237666236663162336630643662313331326261366531396437646536386366373833851908eb1a00015a6b982018fd18bb1871185b08188508184418a1183e182b18b518980118cb18a60218bf183b18351118720418fb18f41836188101181b18ef18941839f6f47840343564616464393261326161373431383133323638376639613432313439306261643963356161643332656134653937623135356533373739316630306435308519021f1a0001539f982018fd18bb1871185b08188508184418a1183e182b18b518980118cb18a60218bf183b18351118720418fb18f41836188101181b18ef18941839f6f4";
194199
subscribers = LibBlob.decodeSubscribers(data);
195-
assertEq(subscribers[0].subscriber, "f0124");
200+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
201+
assertEq(subscribers[0].subscriber, 0xFf0000000000000000000000000000000000007C);
196202
assertEq(
197203
subscribers[0].subscriptionGroup[0].subscriptionId,
198204
"41fd3cc8eb07122bf1e5ed85b0b6927fb6f1b3f0d6b1312ba6e19d7de68cf783"
@@ -202,12 +208,13 @@ contract LibBlobTest is Test {
202208
"45dadd92a2aa7418132687f9a421490bad9c5aad32ea4e97b155e37791f00d50"
203209
);
204210

205-
// Two different subscribers
211+
// // Two different subscribers
206212
data =
207213
hex"a2656630313234a2784034316664336363386562303731323262663165356564383562306236393237666236663162336630643662313331326261366531396437646536386366373833851908eb1a00015a6b982018fd18bb1871185b08188508184418a1183e182b18b518980118cb18a60218bf183b18351118720418fb18f41836188101181b18ef18941839f6f47840343564616464393261326161373431383133323638376639613432313439306261643963356161643332656134653937623135356533373739316630306435308519021f1a0001539f982018fd18bb1871185b08188508184418a1183e182b18b518980118cb18a60218bf183b18351118720418fb18f41836188101181b18ef18941839f6f4656630313236a1784033333566316434636333343130646136363562613133336136616339653666656536626334653861356435643239336262333966396465313266303530326438851909881a00015b08982018fd18bb1871185b08188508184418a1183e182b18b518980118cb18a60218bf183b18351118720418fb18f41836188101181b18ef18941839f6f4";
208214
subscribers = LibBlob.decodeSubscribers(data);
209-
assertEq(subscribers[0].subscriber, "f0124");
210-
assertEq(subscribers[1].subscriber, "f0126");
215+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
216+
assertEq(subscribers[0].subscriber, 0xFf0000000000000000000000000000000000007C);
217+
assertEq(subscribers[1].subscriber, 0xFF0000000000000000000000000000000000007e);
211218
}
212219

213220
function testDecodeSubscriptionGroup() public view {
@@ -242,6 +249,7 @@ contract LibBlobTest is Test {
242249
assertEq(subscription.added, 2961);
243250
assertEq(subscription.expiry, 89361);
244251
assertEq(subscription.source, "7w5xcwyiqueejij6fo2zqaoluybl6ozvcfzaj67ug2aqcg7psq4q");
252+
// Note: tests will always show this as a masked ID address, but in reality, it's a looked up delegated address
245253
assertEq(subscription.delegate, 0xFF0000000000000000000000000000000000007D);
246254
assertEq(subscription.failed, false);
247255
}

test/LibWasm.t.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ contract LibWasmTest is Test {
215215
assertEq(result, "t2o4gsdesxam4qui3pnd4e54ouglffoqwecfnrdzq");
216216
}
217217

218+
function testDecodeCborActorIdStringToAddress() public view {
219+
bytes memory data = bytes("f0152");
220+
address result = LibWasm.decodeCborActorIdStringToAddress(data);
221+
assertEq(result, 0xff00000000000000000000000000000000000098);
222+
}
223+
218224
function testDecodeCborString() public view {
219225
bytes memory data = hex"65696e6e6572";
220226
bytes memory result = LibWasm.decodeCborStringToBytes(data);

0 commit comments

Comments
 (0)