soroban.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // SPDX-License-Identifier: Apache-2.0
  2. #[cfg(feature = "soroban")]
  3. use solang::codegen::Options;
  4. use solang::file_resolver::FileResolver;
  5. use solang::sema::ast::Namespace;
  6. use solang::sema::diagnostics::Diagnostics;
  7. use solang::{compile, Target};
  8. use soroban_sdk::testutils::Logs;
  9. use soroban_sdk::{vec, Address, ConstructorArgs, Env, Symbol, Val};
  10. use std::ffi::OsStr;
  11. pub mod soroban_testcases;
  12. // TODO: register accounts, related balances, events, etc.
  13. pub struct SorobanEnv {
  14. env: Env,
  15. contracts: Vec<Address>,
  16. compiler_diagnostics: Diagnostics,
  17. }
  18. pub fn build_solidity<F>(src: &str, configure_env: F) -> SorobanEnv
  19. where
  20. F: FnOnce(&mut SorobanEnv),
  21. {
  22. let (wasm_blob, ns) = build_wasm(src);
  23. SorobanEnv::new_with_contract(wasm_blob, configure_env).insert_diagnostics(ns.diagnostics)
  24. }
  25. fn build_wasm(src: &str) -> (Vec<u8>, Namespace) {
  26. let tmp_file = OsStr::new("test.sol");
  27. let mut cache = FileResolver::default();
  28. cache.set_file_contents(tmp_file.to_str().unwrap(), src.to_string());
  29. let opt = inkwell::OptimizationLevel::Default;
  30. let target = Target::Soroban;
  31. let (wasm, ns) = compile(
  32. tmp_file,
  33. &mut cache,
  34. target,
  35. &Options {
  36. opt_level: opt.into(),
  37. log_runtime_errors: true,
  38. log_prints: true,
  39. #[cfg(feature = "wasm_opt")]
  40. wasm_opt: Some(contract_build::OptimizationPasses::Z),
  41. soroban_version: None,
  42. ..Default::default()
  43. },
  44. std::vec!["unknown".to_string()],
  45. "0.0.1",
  46. );
  47. assert!(!wasm.is_empty());
  48. (wasm[0].0.clone(), ns)
  49. }
  50. impl SorobanEnv {
  51. pub fn new() -> Self {
  52. Self {
  53. env: Env::default(),
  54. contracts: Vec::new(),
  55. compiler_diagnostics: Diagnostics::default(),
  56. }
  57. }
  58. pub fn insert_diagnostics(mut self, diagnostics: Diagnostics) -> Self {
  59. self.compiler_diagnostics = diagnostics;
  60. self
  61. }
  62. pub fn new_with_contract<F>(contract_wasm: Vec<u8>, configure_env: F) -> Self
  63. where
  64. F: FnOnce(&mut SorobanEnv),
  65. {
  66. let mut env = Self::new();
  67. configure_env(&mut env);
  68. env.register_contract(contract_wasm);
  69. env
  70. }
  71. pub fn register_contract(&mut self, contract_wasm: Vec<u8>) -> Address {
  72. // For now, we keep using `register_contract_wasm`. To use `register`, we have to figure
  73. // out first what to pass for `constructor_args`
  74. #[allow(deprecated)]
  75. let addr = self
  76. .env
  77. .register_contract_wasm(None, contract_wasm.as_slice());
  78. self.contracts.push(addr.clone());
  79. addr
  80. }
  81. pub fn invoke_contract(&self, addr: &Address, function_name: &str, args: Vec<Val>) -> Val {
  82. let func = Symbol::new(&self.env, function_name);
  83. let mut args_soroban = vec![&self.env];
  84. for arg in args {
  85. args_soroban.push_back(arg)
  86. }
  87. println!("args: {args_soroban:?}");
  88. // To avoid running out of fuel
  89. self.env.cost_estimate().budget().reset_unlimited();
  90. self.env.invoke_contract(addr, &func, args_soroban)
  91. }
  92. /// Invoke a contract and expect an error. Returns the logs.
  93. pub fn invoke_contract_expect_error(
  94. &self,
  95. addr: &Address,
  96. function_name: &str,
  97. args: Vec<Val>,
  98. ) -> Vec<String> {
  99. let func = Symbol::new(&self.env, function_name);
  100. let mut args_soroban = vec![&self.env];
  101. for arg in args {
  102. args_soroban.push_back(arg)
  103. }
  104. let _ = self
  105. .env
  106. .try_invoke_contract::<Val, Val>(addr, &func, args_soroban);
  107. self.env.logs().all()
  108. }
  109. pub fn deploy_contract(&mut self, src: &str) -> Address {
  110. let wasm = build_wasm(src).0;
  111. let addr = self.register_contract(wasm);
  112. self.contracts.push(addr.clone());
  113. addr
  114. }
  115. pub fn deploy_contract_with_args<A>(&mut self, src: &str, args: A) -> Address
  116. where
  117. A: ConstructorArgs,
  118. {
  119. let wasm = build_wasm(src).0;
  120. let addr = self.env.register(wasm.as_slice(), args);
  121. self.contracts.push(addr.clone());
  122. addr
  123. }
  124. }
  125. impl Default for SorobanEnv {
  126. fn default() -> Self {
  127. Self::new()
  128. }
  129. }