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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ This is a development pre-release.

Supported `polkadot-sdk` rev: `2509.0.0`

### Added
- Support for `selfdestruct`.

### Changed
- Emulated EVM heap memory accesses of zero length are never out of bounds.

Expand Down
66 changes: 66 additions & 0 deletions crates/integration/contracts/Selfdestruct.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8;

// TODO: This currently fails the differential test.
// The pallet doesn't send the correct balance back.

/* runner.json
{
"differential": false,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "SelfdestructTester"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Selfdestruct"
}
},
"value": 123456789
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/

contract Selfdestruct {
address tester;
uint value;

constructor() payable {
require(msg.value > 0, "the test should have value");
value = msg.value;

SelfdestructTester s = new SelfdestructTester{value: msg.value}();
tester = address(s);
}

fallback() external {
(bool success, ) = tester.call(hex"");
require(success, "the call to the self destructing contract should succeed");
}
}

contract SelfdestructTester {
constructor() payable {}

fallback() external {
selfdestruct(payable(msg.sender));
}
}
1 change: 1 addition & 0 deletions crates/integration/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ test_spec!(layout_at, "LayoutAt", "LayoutAt.sol");
test_spec!(shift_arithmetic_right, "SAR", "SAR.sol");
test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");

fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate {
Expand Down
13 changes: 13 additions & 0 deletions crates/llvm-context/src/polkavm/evm/return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,16 @@ pub fn invalid(context: &mut Context) -> anyhow::Result<()> {
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
Ok(())
}

/// Translates the `selfdestruct` instruction.
pub fn selfdestruct<'ctx>(
context: &mut Context<'ctx>,
address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> {
let address_pointer = context.build_address_argument_store(address)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::TERMINATE,
&[address_pointer.to_int(context).into()],
);
Ok(())
}
23 changes: 0 additions & 23 deletions crates/resolc/src/tests/unit/unsupported_opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,3 @@ contract ExternalCodeCopy {

build_solidity(sources(&[("test.sol", code)])).unwrap();
}

#[test]
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
fn selfdestruct_yul() {
let solidity = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MinimalDestructible {
address payable public owner;

constructor() {
owner = payable(msg.sender);
}

function destroy() public {
require(msg.sender == owner, "Only the owner can call this function.");
selfdestruct(owner);
}
}"#;

build_solidity(sources(&[("test.sol", solidity)])).unwrap();
}
2 changes: 2 additions & 0 deletions crates/runtime-api/src/polkavm_imports.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,6 @@ POLKAVM_IMPORT(void, set_immutable_data, uint32_t, uint32_t);

POLKAVM_IMPORT(uint32_t, set_storage_or_clear, uint32_t, uint32_t, uint32_t)

POLKAVM_IMPORT(void, terminate, uint32_t)

POLKAVM_IMPORT(void, value_transferred, uint32_t)
5 changes: 4 additions & 1 deletion crates/runtime-api/src/polkavm_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ pub static SET_IMMUTABLE_DATA: &str = "set_immutable_data";

pub static SET_STORAGE: &str = "set_storage_or_clear";

pub static TERMINATE: &str = "terminate";

pub static VALUE_TRANSFERRED: &str = "value_transferred";

/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 32] = [
pub static IMPORTS: [&str; 33] = [
ADDRESS,
BALANCE,
BALANCE_OF,
Expand Down Expand Up @@ -100,6 +102,7 @@ pub static IMPORTS: [&str; 32] = [
RETURNDATASIZE,
SET_IMMUTABLE_DATA,
SET_STORAGE,
TERMINATE,
VALUE_TRANSFERRED,
];

Expand Down
15 changes: 8 additions & 7 deletions crates/yul/src/parser/statement/expression/function_call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,14 @@ impl FunctionCall {
Name::Invalid => {
revive_llvm_context::polkavm_evm_return::invalid(context).map(|_| None)
}
Name::SelfDestruct => {
let arguments = self.pop_arguments_llvm::<1>(context)?;
revive_llvm_context::polkavm_evm_return::selfdestruct(
context,
arguments[0].into_int_value(),
)
.map(|_| None)
}

Name::Log0 => {
let arguments = self.pop_arguments_llvm::<2>(context)?;
Expand Down Expand Up @@ -962,13 +970,6 @@ impl FunctionCall {
location
)
}
Name::SelfDestruct => {
let _arguments = self.pop_arguments_llvm::<1>(context)?;
anyhow::bail!(
"{} The `SELFDESTRUCT` instruction is not supported",
location
)
}
}
}

Expand Down
Loading