diff --git a/src/sema/expression/function_call.rs b/src/sema/expression/function_call.rs index 756c0d8e6..6c0dd43b2 100644 --- a/src/sema/expression/function_call.rs +++ b/src/sema/expression/function_call.rs @@ -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, diff --git a/tests/soroban_testcases/mod.rs b/tests/soroban_testcases/mod.rs index e1df43ec2..15389f98d 100644 --- a/tests/soroban_testcases/mod.rs +++ b/tests/soroban_testcases/mod.rs @@ -22,3 +22,4 @@ mod timestamp; mod token; mod ttl; mod unsupported_parameters; +mod value_transfer; diff --git a/tests/soroban_testcases/value_transfer.rs b/tests/soroban_testcases/value_transfer.rs new file mode 100644 index 000000000..410ac5da4 --- /dev/null +++ b/tests/soroban_testcases/value_transfer.rs @@ -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::>(); + + 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::>(); + + 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:?}", + ); +}