Skip to content

Commit 6ebc98a

Browse files
add felt comparison/l1 to l2 conversion
1 parent 12a414d commit 6ebc98a

File tree

2 files changed

+70
-0
lines changed
  • not-so-smart-contracts/cairo

2 files changed

+70
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# L1 to L2 Address Conversion
2+
3+
In Starknet, addresses are of type `felt` while on L1 addresses are of type `uint160`. Thus, in order to pass around address types during cross layer messaging, the address variable is typically given as a `uint256`. However, this can create an issue where an address on L1 can map to the zero address (or an unexpected address) on L2. This is because the primitive type in Cairo is the `felt`, which is in between the range ` 0 < x < P`, where P is the prime order of the curve. Typically we have `P = 2^251 + 17 * 2^192 + 1`.
4+
5+
# Example
6+
7+
Suppose that the following code to initiate L2 deposits from L1. The first example has no checks on the `to` parameter and thus depending on the users' address, it is possible to transfer tokens to an unexpected address on L2. The second example, however, verifies to make sure this check cannot happen.
8+
9+
```solidity
10+
uint256 public constant STARKNET_FIELD_PRIME; // the prime order P of the elliptic curve used
11+
IERC20 public constant token; //some token to deposit on L2
12+
event Deposited(uint256 sender, uint256 amount);
13+
14+
function badDepositToL2(uint256 to, uint256 amount) public returns (bool) {
15+
token.transferFrom(to, address(this),amount);
16+
emit Deposited(to,amount); // this message gets processed on L2
17+
return true;
18+
}
19+
20+
function goodDepositToL2(uint256 to, uint256 amount) public returns (bool) {
21+
require(to !=0 && to < STARKNET_FIELD_PRIME, "invalid address"); //verifies 0 < to < P
22+
token.transferFrom(to, address(this),amount);
23+
emit Deposited(to,amount); // this message gets processed on L2
24+
return true;
25+
}
26+
```
27+
28+
# Mitigations
29+
30+
When sending a message from L1 to L2, remember to verify parameters, especially user supplied params. Remember that the type and range of Cairo's default `felt` type is less than the `uint256` type used by Solidity.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Incorrect Felt Comparison
2+
3+
In cairo, there are two methods for comparison, in particular for the less than or equal to operator we have the methods `assert_le` and `assert_nn_le`. `assert_le` asserts that a number a is less than or equal to b, regardless of the size of a, while `assert_nn_le` will also assert that a is non-negative, ie not greater than the `RANGE_CHECK_BOUND` value of `2^128`: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/cairo/common/math.cairo#L66-L67
4+
5+
# Example
6+
7+
Suppose that a codebase uses the following checks regarding a hypothetical ERC20 token. In the first function, it may be possible that `value` is in fact greater than `max_supply`, yet because the function does not verify `value <0` the assertion will incorrectly pass. The second function, however, asserts that `0 < value < max_supply`, which will correctly not let an incorrect `value` go through the assertion.
8+
9+
```cairo
10+
@storage_var
11+
func max_supply() -> (res: felt):
12+
end
13+
14+
@external
15+
func bad_comparison{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}():
16+
let (value: felt) = ERC20.total_supply()
17+
assert_le{range_check_ptr=range_check_ptr}(value, max_supply.read())
18+
19+
# do something...
20+
21+
return ()
22+
end
23+
24+
@external
25+
func better_comparison{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}():
26+
let (value: felt) = ERC20.total_supply()
27+
assert_nn_le{range_check_ptr=range_check_ptr}(value, max_supply.read())
28+
29+
# do something...
30+
31+
return ()
32+
33+
34+
end
35+
```
36+
37+
38+
39+
# Mitigations
40+
Review all felt comparisons closely. Determine what sort of behavior the comparison should have, and if `assert_nn_le` is more appropriate.

0 commit comments

Comments
 (0)