Skip to content
Open
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
14 changes: 14 additions & 0 deletions src/sema/expression/function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,20 @@ fn try_type_method(
return Err(());
}

if ns.target == Target::Soroban {
diagnostics.push(Diagnostic::error(
*loc,
format!(
"method '{}' is not available on Soroban. Soroban contracts \
do not have a native value-transfer model; move assets through \
the Stellar Asset Contract (SAC) or the token interface instead.",
func.name
),
));

return Err(());
}

if !is_payable {
diagnostics.push(Diagnostic::error(
*loc,
Expand Down
1 change: 1 addition & 0 deletions tests/soroban_testcases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ mod timestamp;
mod token;
mod ttl;
mod unsupported_parameters;
mod value_transfer;
97 changes: 97 additions & 0 deletions tests/soroban_testcases/value_transfer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: Apache-2.0

use solang::codegen::Options;
use solang::file_resolver::FileResolver;
use solang::sema::ast::Namespace;
use solang::{compile, Target};
use solang_parser::diagnostics::Level;
use std::ffi::OsStr;

fn compile_soroban(src: &str) -> Namespace {
let tmp_file = OsStr::new("test.sol");
let mut cache = FileResolver::default();
cache.set_file_contents(tmp_file.to_str().unwrap(), src.to_string());
let opt = inkwell::OptimizationLevel::Default;

let (_, ns) = compile(
tmp_file,
&mut cache,
Target::Soroban,
&Options {
opt_level: opt.into(),
log_runtime_errors: true,
log_prints: true,
#[cfg(feature = "wasm_opt")]
wasm_opt: Some(contract_build::OptimizationPasses::Z),
soroban_version: None,
..Default::default()
},
std::vec!["unknown".to_string()],
"0.0.1",
);

ns
}

// Regression test for https://github.com/hyperledger-solang/solang/issues/1909
// Previously this would panic in codegen (`Found IntValue` at
// `src/emit/instructions.rs:989`) while lowering `Instr::ValueTransfer`. Soroban
// has no native value-transfer model, so the call should be rejected at sema
// time with an actionable diagnostic.
#[test]
fn address_transfer_is_rejected_on_soroban() {
let ns = compile_soroban(
r#"
contract C {
function run() external payable {
payable(address(this)).transfer(uint256(0));
}
}
"#,
);

let errors = ns
.diagnostics
.iter()
.filter(|diagnostic| diagnostic.level == Level::Error)
.collect::<Vec<_>>();

assert!(
errors
.iter()
.any(|diagnostic| diagnostic.message
== "method 'transfer' is not available on Soroban. Soroban contracts \
do not have a native value-transfer model; move assets through \
the Stellar Asset Contract (SAC) or the token interface instead."),
"expected a Soroban-specific rejection of `transfer`, got: {errors:?}",
);
}

#[test]
fn address_send_is_rejected_on_soroban() {
let ns = compile_soroban(
r#"
contract C {
function run() external payable {
payable(address(this)).send(uint256(0));
}
}
"#,
);

let errors = ns
.diagnostics
.iter()
.filter(|diagnostic| diagnostic.level == Level::Error)
.collect::<Vec<_>>();

assert!(
errors
.iter()
.any(|diagnostic| diagnostic.message
== "method 'send' is not available on Soroban. Soroban contracts \
do not have a native value-transfer model; move assets through \
the Stellar Asset Contract (SAC) or the token interface instead."),
"expected a Soroban-specific rejection of `send`, got: {errors:?}",
);
}