Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions crates/fe/tests/fixtures/fe_test_runner/assert_msg.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::evm::{Address, Call, Create, Evm, RawMem, last_returndata}
use std::evm::mem

#[test(should_revert, selector = 0x08c379a0)]
fn test_assert_msg_reverts() uses (evm: mut Evm) {
assert_msg(false, "boom")
}

#[test]
fn test_assert_msg_passes() uses (evm: mut Evm) {
assert_msg(true, "should not revert")
}

msg ReverterMsg {
#[selector = 0xaabbccdd]
Trigger,
}

pub contract Reverter {
recv ReverterMsg {
Trigger {} {
assert_msg(false, "boom")
}
}
}

#[test]
fn test_assert_msg_payload_layout() uses (evm: mut Evm) {
let addr = evm.create2<Reverter>(value: 0, args: (), salt: 0)

// Build calldata containing only the ReverterMsg::Trigger selector.
let selector_word = (0xaabbccdd as u256) << 224
let ptr = mem::alloc(4)
evm.mstore(addr: ptr, value: selector_word)

let ok = evm.raw_call(addr: addr, gas: 100000, value: 0, args_offset: ptr, args_len: 4)
assert(!ok)

// Solidity `Error(string)` for "boom":
// selector(4) | offset = 0x20 (32) | len = 4 (32) | "boom" right-padded (32) = 100
// Catches the bug where assert_msg encodes the bare `Text` payload
// (`len || bytes`) instead of the single-arg tuple `(message,)`
// (`offset || len || bytes`).
let ret = last_returndata()
assert(ret.len == 100)
assert(evm.mload(ret.base) >> 224 == 0x08c379a0)
assert(evm.mload(ret.base + 4) == 0x20)
assert(evm.mload(ret.base + 36) == 4)
assert(
evm.mload(ret.base + 68)
== 0x626f6f6d00000000000000000000000000000000000000000000000000000000,
)
}
13 changes: 13 additions & 0 deletions crates/fe/tests/fixtures/fe_test_runner/assert_msg.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/fe/tests/cli_output.rs
expression: output
input_file: tests/fixtures/fe_test_runner/assert_msg.fe
---
=== STDOUT ===
PASS [<time>] test_assert_msg_reverts
PASS [<time>] test_assert_msg_passes
PASS [<time>] test_assert_msg_payload_layout

test result: ok. 3 passed; 0 failed

=== EXIT CODE: 0 ===
3 changes: 3 additions & 0 deletions crates/language-server/test_files/completion.snap
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ completions:
- Vec (Class)
- another_func_with_args (Function) -> "another_func_with_args(${1:x}, ${2:y})$0" [fn another_func_with_args(x: i32, y: i32) -> i32]
- assert (Function) -> "assert(${1:b})$0" [fn assert(b: bool)]
- assert_msg (Function) -> "assert_msg(${1:b}, ${2:message})$0" [fn assert_msg(b: bool, message: DynString)]
- func_with_args (Function) -> "func_with_args(${1:x}, ${2:y})$0" [use utils::func_with_args [fn func_with_args(x: i32, y: i32) -> i32]] {+0:use utils::func_with_args}
- help (Function) -> "help()$0" [use utils::help [fn help() -> i32]] {+0:use utils::help}
- helper_func (Function) -> "helper_func()$0" [use utils::helper_func [fn helper_func() -> i32]] {+0:use utils::helper_func}
Expand Down Expand Up @@ -114,6 +115,7 @@ completions:
- Some (EnumMember)
- another_func_with_args (Function) -> "another_func_with_args(${1:x}, ${2:y})$0" [fn another_func_with_args(x: i32, y: i32) -> i32]
- assert (Function) -> "assert(${1:b})$0" [fn assert(b: bool)]
- assert_msg (Function) -> "assert_msg(${1:b}, ${2:message})$0" [fn assert_msg(b: bool, message: DynString)]
- func_with_args (Function) -> "func_with_args(${1:x}, ${2:y})$0" [use utils::func_with_args [fn func_with_args(x: i32, y: i32) -> i32]] {+0:use utils::func_with_args}
- help (Function) -> "help()$0" [use utils::help [fn help() -> i32]] {+0:use utils::help}
- helper_func (Function) -> "helper_func()$0" [use utils::helper_func [fn helper_func() -> i32]] {+0:use utils::helper_func}
Expand Down Expand Up @@ -178,6 +180,7 @@ completions:
- Some (EnumMember)
- another_func_with_args (Function) -> "another_func_with_args(${1:x}, ${2:y})$0" [fn another_func_with_args(x: i32, y: i32) -> i32]
- assert (Function) -> "assert(${1:b})$0" [fn assert(b: bool)]
- assert_msg (Function) -> "assert_msg(${1:b}, ${2:message})$0" [fn assert_msg(b: bool, message: DynString)]
- func_with_args (Function) -> "func_with_args(${1:x}, ${2:y})$0" [use utils::func_with_args [fn func_with_args(x: i32, y: i32) -> i32]] {+0:use utils::func_with_args}
- help (Function) -> "help()$0" [use utils::help [fn help() -> i32]] {+0:use utils::help}
- helper_func (Function) -> "helper_func()$0" [use utils::helper_func [fn helper_func() -> i32]] {+0:use utils::helper_func}
Expand Down
9 changes: 4 additions & 5 deletions crates/language-server/test_files/goto_contract.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
source: crates/language-server/src/functionality/goto.rs
assertion_line: 456
expression: snapshot
input_file: test_files/goto_contract.fe
---
Expand Down Expand Up @@ -104,25 +103,25 @@ note:
┌─ goto_contract.fe:70:5
70 │ Mint { to: u256, amount: u256 } -> bool,
│ ^^^^ -> goto_contract::TokenMsg::Mint
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> goto_contract::TokenMsg::Mint

note:
┌─ goto_contract.fe:71:5
71 │ GetSupply -> u256,
│ ^^^^^^^^^ -> goto_contract::TokenMsg::GetSupply
│ ^^^^^^^^^^^^^^^^^ -> goto_contract::TokenMsg::GetSupply

note:
┌─ goto_contract.fe:75:5
75 │ SetOwner { new_owner: u256 } -> bool,
│ ^^^^^^^^ -> goto_contract::AdminMsg::SetOwner
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> goto_contract::AdminMsg::SetOwner

note:
┌─ goto_contract.fe:76:5
76 │ GetOwner -> u256,
│ ^^^^^^^^ -> goto_contract::AdminMsg::GetOwner
│ ^^^^^^^^^^^^^^^^ -> goto_contract::AdminMsg::GetOwner

note:
┌─ goto_contract.fe:81:31
Expand Down
4 changes: 2 additions & 2 deletions crates/language-server/test_files/goto_effects.snap
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,13 @@ note:
┌─ goto_effects.fe:71:5
71 │ Add { x: u256 } -> u256,
│ ^^^ -> goto_effects::CalcMsg::Add
│ ^^^^^^^^^^^^^^^^^^^^^^^ -> goto_effects::CalcMsg::Add

note:
┌─ goto_effects.fe:72:5
72 │ GetState -> u256,
│ ^^^^^^^^ -> goto_effects::CalcMsg::GetState
│ ^^^^^^^^^^^^^^^^ -> goto_effects::CalcMsg::GetState

note:
┌─ goto_effects.fe:75:40
Expand Down
18 changes: 16 additions & 2 deletions crates/language-server/test_files/hoverable/src/lib.snap
Original file line number Diff line number Diff line change
Expand Up @@ -669,17 +669,31 @@ note:
97 │ │ }
│ ╰─^ def: defined here @ 92:0 (11 refs)

note:
┌─ lib.fe:93:3
93 │ ╭ #[selector = 0x01]
94 │ │ Mint { to: i32, amount: i32 } -> bool,
│ ╰───────────────────────────────────────^ ref: 93:2

note:
┌─ lib.fe:94:3
94 │ Mint { to: i32, amount: i32 } -> bool,
│ ^^^^ ref: 94:2
│ ^^^^ def: defined here @ 94:2 (13 refs)

note:
┌─ lib.fe:95:3
95 │ ╭ #[selector = 0x02]
96 │ │ Burn { amount: i32 } -> bool,
│ ╰──────────────────────────────^ ref: 95:2

note:
┌─ lib.fe:96:3
96 │ Burn { amount: i32 } -> bool,
│ ^^^^ ref: 96:2
│ ^^^^ def: defined here @ 96:2 (13 refs)

note:
┌─ lib.fe:99:14
Expand Down
18 changes: 16 additions & 2 deletions crates/language-server/test_files/hoverable/src/stuff.snap
Original file line number Diff line number Diff line change
Expand Up @@ -669,17 +669,31 @@ note:
97 │ │ }
│ ╰─^ def: defined here @ 92:0 (11 refs)

note:
┌─ lib.fe:93:3
93 │ ╭ #[selector = 0x01]
94 │ │ Mint { to: i32, amount: i32 } -> bool,
│ ╰───────────────────────────────────────^ ref: 93:2

note:
┌─ lib.fe:94:3
94 │ Mint { to: i32, amount: i32 } -> bool,
│ ^^^^ ref: 94:2
│ ^^^^ def: defined here @ 94:2 (13 refs)

note:
┌─ lib.fe:95:3
95 │ ╭ #[selector = 0x02]
96 │ │ Burn { amount: i32 } -> bool,
│ ╰──────────────────────────────^ ref: 95:2

note:
┌─ lib.fe:96:3
96 │ Burn { amount: i32 } -> bool,
│ ^^^^ ref: 96:2
│ ^^^^ def: defined here @ 96:2 (13 refs)

note:
┌─ lib.fe:99:14
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ error[6-0003]: trait bound is not satisfied
6 │ │ }
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `Encode<Sol>`
┌─ src/evm/effects.fe:343:14
┌─ src/evm/effects.fe:344:14
343 │ where T: Encode<Sol> + AbiSize
344 │ where T: Encode<Sol> + AbiSize
│ ----------- required by this bound on `encode_abi_payload`

error[6-0003]: trait bound is not satisfied
Expand All @@ -26,7 +26,7 @@ error[6-0003]: trait bound is not satisfied
6 │ │ }
│ ╰─^ `StorageMap<Address, u256, 0>` doesn't implement `AbiSize`
┌─ src/evm/effects.fe:343:28
┌─ src/evm/effects.fe:344:28
343 │ where T: Encode<Sol> + AbiSize
344 │ where T: Encode<Sol> + AbiSize
│ ------- required by this bound on `encode_abi_payload`
14 changes: 14 additions & 0 deletions ingots/std/src/evm/effects.fe
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use ingot::evm::intrinsic::{
code_region_offset as intrinsic_code_region_offset,
}
use ingot::abi::Sol
use ingot::abi::Text
use ingot::abi::sol::{decode_input_at, decode_output}
use super::calldata::CallData
use super::contract_field_slot
Expand Down Expand Up @@ -683,6 +684,19 @@ pub fn assert(_ b: own bool) {
if !b { revert_error(Panic { code: 0x01 }) }
}

/// Assert with a Solidity-compatible `Error(string)` revert message.
///
/// When `b` is false, reverts with the 4-byte selector `0x08c379a0`
/// (keccak256("Error(string)")[..4]) followed by the ABI-encoded
/// message, matching Solidity's `require(cond, "message")` / `revert("message")`.
pub fn assert_msg(_ b: own bool, _ message: own Text) {
if !b {
// Error(string) has one ABI argument, so encode the full argument list.
let (ptr, len) = encode_calldata(0x08c379a0, (message,))
ops::revert(offset: ptr, len: len)
}
}

/// Revert the current transaction with ABI-encoded data.
///
/// Encodes the given value using Solidity ABI encoding and reverts
Expand Down
2 changes: 1 addition & 1 deletion ingots/std/src/prelude.fe
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ pub use core::abi::{Abi, AbiEncoder, AbiSize, Encode}
pub use super::abi::{Sol, Text, Vec}

// Common EVM types.
pub use super::evm::{Evm, Address, Call, Ctx, StorageMap, Create, Log, Panic, assert, revert, revert_error}
pub use super::evm::{Evm, Address, Call, Ctx, StorageMap, Create, Log, Panic, assert, assert_msg, revert, revert_error}
1 change: 1 addition & 0 deletions newsfragments/1348.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `assert_msg(cond, message)`, which reverts with a Solidity-compatible `Error(string)` payload (selector `0x08c379a0`) when `cond` is false.
Loading