소스 검색

Add support for Soroban external calls 🎉 (#1679)

This PR adds support for Soroban external calls. This is the first step
to support value transfers from inside Soroban contracts.

---------

Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
salaheldinsoliman 9 달 전
부모
커밋
07ad1a12b0
39개의 변경된 파일1069개의 추가작업 그리고 201개의 파일을 삭제
  1. 6 1
      .github/workflows/test.yml
  2. 2 0
      .gitignore
  3. 5 1
      integration/soroban/.gitignore
  4. 6 0
      integration/soroban/callee.sol
  5. 8 0
      integration/soroban/caller.sol
  6. 3 2
      integration/soroban/counter.spec.js
  7. 65 0
      integration/soroban/cross_contract.spec.js
  8. 1 1
      integration/soroban/runtime_error.spec.js
  9. 23 0
      integration/soroban/rust/Cargo.toml
  10. 15 0
      integration/soroban/rust/contracts/hello-world/Cargo.toml
  11. 15 0
      integration/soroban/rust/contracts/hello-world/src/lib.rs
  12. 8 2
      integration/soroban/setup.js
  13. 24 12
      integration/soroban/storage_types.spec.js
  14. 23 5
      integration/soroban/test_helpers.js
  15. 17 0
      src/codegen/cfg.rs
  16. 2 1
      src/codegen/constant_folding.rs
  17. 45 24
      src/codegen/dispatch/soroban.rs
  18. 9 0
      src/codegen/encoding/mod.rs
  19. 234 0
      src/codegen/encoding/soroban_encoding.rs
  20. 34 1
      src/codegen/mod.rs
  21. 88 6
      src/emit/binary.rs
  22. 14 2
      src/emit/expression.rs
  23. 97 47
      src/emit/instructions.rs
  24. 1 1
      src/emit/mod.rs
  25. 1 1
      src/emit/polkadot/target.rs
  26. 5 3
      src/emit/solana/target.rs
  27. 69 33
      src/emit/soroban/mod.rs
  28. 128 46
      src/emit/soroban/target.rs
  29. 9 1
      src/emit/strings.rs
  30. 3 8
      src/linker/soroban_wasm.rs
  31. 21 0
      src/lir/converter/expression.rs
  32. 3 0
      src/lir/converter/mod.rs
  33. 4 0
      src/lir/expressions.rs
  34. 1 0
      src/lir/lir_type.rs
  35. 5 0
      src/lir/printer/expression.rs
  36. 3 0
      src/lir/printer/instruction.rs
  37. 17 3
      tests/soroban.rs
  38. 54 0
      tests/soroban_testcases/cross_contract_calls.rs
  39. 1 0
      tests/soroban_testcases/mod.rs

+ 6 - 1
.github/workflows/test.yml

@@ -325,6 +325,8 @@ jobs:
       with:
       with:
         node-version: '16'
         node-version: '16'
     - uses: dtolnay/rust-toolchain@1.81.0
     - uses: dtolnay/rust-toolchain@1.81.0
+      with:
+        target: wasm32-unknown-unknown
     - uses: actions/download-artifact@v4.1.8
     - uses: actions/download-artifact@v4.1.8
       with:
       with:
         name: solang-linux-x86-64
         name: solang-linux-x86-64
@@ -335,7 +337,7 @@ jobs:
         echo "$(pwd)/bin" >> $GITHUB_PATH
         echo "$(pwd)/bin" >> $GITHUB_PATH
     
     
     - name: Install Soroban
     - name: Install Soroban
-      run: cargo install --locked soroban-cli --version 22.0.0
+      run: cargo install --locked stellar-cli --version 22.0.0
     - name: Add cargo install location to PATH
     - name: Add cargo install location to PATH
       run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
       run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
     - run: npm install
     - run: npm install
@@ -343,6 +345,9 @@ jobs:
     - name: Build Solang contracts
     - name: Build Solang contracts
       run: npm run build
       run: npm run build
       working-directory: ./integration/soroban
       working-directory: ./integration/soroban
+    - name: Build rust contracts
+      run: soroban contract build --profile release-with-logs
+      working-directory: ./integration/soroban/rust/contracts
     - name: Setup Soroban enivronment
     - name: Setup Soroban enivronment
       run: npm run setup
       run: npm run setup
       working-directory: ./integration/soroban
       working-directory: ./integration/soroban

+ 2 - 0
.gitignore

@@ -9,3 +9,5 @@ Cargo.lock
 
 
 .helix/
 .helix/
 .vscode/
 .vscode/
+
+/test_snapshots

+ 5 - 1
integration/soroban/.gitignore

@@ -6,4 +6,8 @@
 node_modules
 node_modules
 package-lock.json
 package-lock.json
 *.txt
 *.txt
-*.toml
+*.wasm
+*.abi
+target/
+.stellar
+

+ 6 - 0
integration/soroban/callee.sol

@@ -0,0 +1,6 @@
+contract callee {
+    function add (uint64 a, uint64 b, uint64 c) public returns (uint64) {
+        print("add called in Solidity");
+        return a + b +c;
+    }
+}

+ 8 - 0
integration/soroban/caller.sol

@@ -0,0 +1,8 @@
+contract caller {
+    function add (address addr, uint64 a, uint64 b, uint64 c) public returns (uint64) {
+        bytes payload = abi.encode("add", a, b, c);
+        (bool suc, bytes returndata) = addr.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+        return result;
+    }
+}

+ 3 - 2
integration/soroban/counter.spec.js

@@ -35,7 +35,8 @@ describe('Counter', () => {
   it('get correct initial counter', async () => {
   it('get correct initial counter', async () => {
     // get the count
     // get the count
     let count = await call_contract_function("count", server, keypair, contract);
     let count = await call_contract_function("count", server, keypair, contract);
-    expect(count.toString()).eq("10");
+    console.log(count.returnValue().value());
+    expect(count.returnValue().value().toString()).eq("10");
   });
   });
 
 
   it('increment counter', async () => {
   it('increment counter', async () => {
@@ -44,7 +45,7 @@ describe('Counter', () => {
 
 
     // get the count
     // get the count
     let count = await call_contract_function("count", server, keypair, contract);
     let count = await call_contract_function("count", server, keypair, contract);
-    expect(count.toString()).eq("11");
+    expect(count.returnValue().value().toString()).eq("11");
   });
   });
 });
 });
 
 

+ 65 - 0
integration/soroban/cross_contract.spec.js

@@ -0,0 +1,65 @@
+import * as StellarSdk from '@stellar/stellar-sdk';
+import { readFileSync } from 'fs';
+import { expect } from 'chai';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import { call_contract_function, extractLogEvent } from './test_helpers.js';
+
+const __filename = fileURLToPath(import.meta.url);
+const dirname = path.dirname(__filename);
+const server = new StellarSdk.SorobanRpc.Server("https://soroban-testnet.stellar.org:443");
+
+function readContractAddress(filename) {
+  return readFileSync(path.join(dirname, '.soroban', 'contract-ids', filename), 'utf8').trim();
+}
+
+describe('Cross Contract Calls', () => {
+  let keypair, caller, callee, calleeRust;
+
+  before(async () => {
+    console.log('Setting up cross contract tests...');
+
+    keypair = StellarSdk.Keypair.fromSecret(readFileSync('alice.txt', 'utf8').trim());
+    caller = new StellarSdk.Contract(readContractAddress('caller.txt'));
+    callee = new StellarSdk.Contract(readContractAddress('callee.txt'));
+    calleeRust = new StellarSdk.Contract(readContractAddress('hello_world.txt'));
+  });
+
+  it('calls Rust contract', async () => {
+    let addr = calleeRust.address().toScVal();
+    let values = [
+      new StellarSdk.xdr.Uint64(BigInt(1)),
+      new StellarSdk.xdr.Uint64(BigInt(2)),
+      new StellarSdk.xdr.Uint64(BigInt(0))
+    ].map(StellarSdk.xdr.ScVal.scvU64);
+
+    let res = await call_contract_function("add", server, keypair, caller, addr, ...values);
+    let returnValue = res.returnValue().value().toString();
+
+    console.log(returnValue);
+    expect(returnValue).to.equal("3");
+
+    let logMessages = extractLogEvent(res.diagnosticEvents()).logMessages;
+    console.log(logMessages);
+    expect(logMessages[0]).to.contain('Soroban SDK add function called!');
+  });
+
+  it('calls Solidity contract', async () => {
+    let addr = callee.address().toScVal();
+    let values = [
+      new StellarSdk.xdr.Uint64(BigInt(1)),
+      new StellarSdk.xdr.Uint64(BigInt(2)),
+      new StellarSdk.xdr.Uint64(BigInt(0))
+    ].map(StellarSdk.xdr.ScVal.scvU64);
+
+    let res = await call_contract_function("add", server, keypair, caller, addr, ...values);
+    let returnValue = res.returnValue().value().toString();
+
+    console.log(returnValue);
+    expect(returnValue).to.equal("3");
+
+    let logMessages = extractLogEvent(res.diagnosticEvents()).logMessages;
+    console.log(logMessages);
+    expect(logMessages[0]).to.contain('add called in Solidity');
+  });
+});

+ 1 - 1
integration/soroban/runtime_error.spec.js

@@ -35,7 +35,7 @@ describe('Runtime Error', () => {
     await call_contract_function("decrement", server, keypair, contract);
     await call_contract_function("decrement", server, keypair, contract);
   });
   });
 
 
-  it('get correct initial counter', async () => {
+  it('prints error', async () => {
 
 
     // decrement the counter again, resulting in a runtime error
     // decrement the counter again, resulting in a runtime error
     let res = await call_contract_function("decrement", server, keypair, contract);
     let res = await call_contract_function("decrement", server, keypair, contract);

+ 23 - 0
integration/soroban/rust/Cargo.toml

@@ -0,0 +1,23 @@
+[workspace]
+resolver = "2"
+members = [
+  "contracts/*",
+]
+
+[workspace.dependencies]
+soroban-sdk = "22"
+
+[profile.release]
+opt-level = "z"
+overflow-checks = true
+debug = 0
+strip = "symbols"
+debug-assertions = false
+panic = "abort"
+codegen-units = 1
+lto = true
+
+# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile
+[profile.release-with-logs]
+inherits = "release"
+debug-assertions = true

+ 15 - 0
integration/soroban/rust/contracts/hello-world/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "hello-world"
+version = "0.0.0"
+edition = "2021"
+publish = false
+
+[lib]
+crate-type = ["cdylib"]
+doctest = false
+
+[dependencies]
+soroban-sdk = { workspace = true }
+
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }

+ 15 - 0
integration/soroban/rust/contracts/hello-world/src/lib.rs

@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#![no_std]
+use soroban_sdk::{contract, contractimpl, Env, log};
+
+#[contract]
+pub struct Contract;
+
+#[contractimpl]
+impl Contract {
+    pub fn add(env: Env, a: u64, b: u64, c: u64) -> u64 {
+        log!(&env,"Soroban SDK add function called!");
+        a + b + c
+    }
+}

+ 8 - 2
integration/soroban/setup.js

@@ -21,7 +21,7 @@ function exe(command) {
 }
 }
 
 
 function generate_alice() {
 function generate_alice() {
-  exe(`${soroban} keys generate alice --network testnet`);
+  exe(`${soroban} keys generate alice --network testnet --overwrite`);
 
 
   // get the secret key of alice and put it in alice.txt
   // get the secret key of alice and put it in alice.txt
   exe(`${soroban} keys show alice > alice.txt`);
   exe(`${soroban} keys show alice > alice.txt`);
@@ -43,7 +43,13 @@ function deploy_all() {
   const contractsDir = path.join(dirname, '.soroban', 'contract-ids');
   const contractsDir = path.join(dirname, '.soroban', 'contract-ids');
   mkdirSync(contractsDir, { recursive: true });
   mkdirSync(contractsDir, { recursive: true });
 
 
-  const wasmFiles = readdirSync(`${dirname}`).filter(file => file.endsWith('.wasm'));
+  let wasmFiles = readdirSync(`${dirname}`).filter(file => file.endsWith('.wasm'));
+  console.log(dirname);
+  
+  let rust_wasm = path.join('rust','target','wasm32-unknown-unknown', 'release-with-logs', 'hello_world.wasm');
+
+  // add rust wasm file to the list of wasm files
+  wasmFiles.push(rust_wasm);
 
 
   wasmFiles.forEach(wasmFile => {
   wasmFiles.forEach(wasmFile => {
     deploy(path.join(dirname, wasmFile));
     deploy(path.join(dirname, wasmFile));

+ 24 - 12
integration/soroban/storage_types.spec.js

@@ -33,16 +33,20 @@ describe('StorageTypes', () => {
 
 
   it('check initial values', async () => {
   it('check initial values', async () => {
     // Check initial values of all storage variables
     // Check initial values of all storage variables
-    let sesa = await call_contract_function("sesa", server, keypair, contract);
+    let res = await call_contract_function("sesa", server, keypair, contract);
+    let sesa = res.returnValue().value();
     expect(sesa.toString()).eq("1");
     expect(sesa.toString()).eq("1");
 
 
-    let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
+    res = await call_contract_function("sesa1", server, keypair, contract);
+    let sesa1 = res.returnValue().value();
     expect(sesa1.toString()).eq("1");
     expect(sesa1.toString()).eq("1");
 
 
-    let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
+    res = await call_contract_function("sesa2", server, keypair, contract);
+    let sesa2 = res.returnValue().value();
     expect(sesa2.toString()).eq("2");
     expect(sesa2.toString()).eq("2");
 
 
-    let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
+    res = await call_contract_function("sesa3", server, keypair, contract);
+    let sesa3 = res.returnValue().value();
     expect(sesa3.toString()).eq("2");
     expect(sesa3.toString()).eq("2");
   });
   });
 
 
@@ -51,16 +55,20 @@ describe('StorageTypes', () => {
     await call_contract_function("inc", server, keypair, contract);
     await call_contract_function("inc", server, keypair, contract);
 
 
     // Check the incremented values
     // Check the incremented values
-    let sesa = await call_contract_function("sesa", server, keypair, contract);
+    let res = await call_contract_function("sesa", server, keypair, contract);
+    let sesa = res.returnValue().value();
     expect(sesa.toString()).eq("2");
     expect(sesa.toString()).eq("2");
 
 
-    let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
+    res = await call_contract_function("sesa1", server, keypair, contract);
+    let sesa1 = res.returnValue().value();
     expect(sesa1.toString()).eq("2");
     expect(sesa1.toString()).eq("2");
 
 
-    let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
+    res = await call_contract_function("sesa2", server, keypair, contract);
+    let sesa2 = res.returnValue().value();
     expect(sesa2.toString()).eq("3");
     expect(sesa2.toString()).eq("3");
 
 
-    let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
+    res = await call_contract_function("sesa3", server, keypair, contract);
+    let sesa3 = res.returnValue().value();
     expect(sesa3.toString()).eq("3");
     expect(sesa3.toString()).eq("3");
   });
   });
 
 
@@ -69,16 +77,20 @@ describe('StorageTypes', () => {
     await call_contract_function("dec", server, keypair, contract);
     await call_contract_function("dec", server, keypair, contract);
 
 
     // Check the decremented values
     // Check the decremented values
-    let sesa = await call_contract_function("sesa", server, keypair, contract);
+    let res = await call_contract_function("sesa", server, keypair, contract);
+    let sesa = res.returnValue().value();
     expect(sesa.toString()).eq("1");
     expect(sesa.toString()).eq("1");
 
 
-    let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
+    res = await call_contract_function("sesa1", server, keypair, contract);
+    let sesa1 = res.returnValue().value();
     expect(sesa1.toString()).eq("1");
     expect(sesa1.toString()).eq("1");
 
 
-    let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
+    res = await call_contract_function("sesa2", server, keypair, contract);
+    let sesa2 = res.returnValue().value();
     expect(sesa2.toString()).eq("2");
     expect(sesa2.toString()).eq("2");
 
 
-    let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
+    res = await call_contract_function("sesa3", server, keypair, contract);
+    let sesa3 = res.returnValue().value();
     expect(sesa3.toString()).eq("2");
     expect(sesa3.toString()).eq("2");
   });
   });
 });
 });

+ 23 - 5
integration/soroban/test_helpers.js

@@ -1,13 +1,13 @@
 import * as StellarSdk from '@stellar/stellar-sdk';
 import * as StellarSdk from '@stellar/stellar-sdk';
 
 
-export async function call_contract_function(method, server, keypair, contract) {
+export async function call_contract_function(method, server, keypair, contract, ... params) {
     let res = null;
     let res = null;
 
 
     try {
     try {
         let builtTransaction = new StellarSdk.TransactionBuilder(await server.getAccount(keypair.publicKey()), {
         let builtTransaction = new StellarSdk.TransactionBuilder(await server.getAccount(keypair.publicKey()), {
             fee: StellarSdk.BASE_FEE,
             fee: StellarSdk.BASE_FEE,
             networkPassphrase: StellarSdk.Networks.TESTNET,
             networkPassphrase: StellarSdk.Networks.TESTNET,
-        }).addOperation(contract.call(method)).setTimeout(30).build();
+        }).addOperation(contract.call(method, ...params)).setTimeout(30).build();
 
 
         let preparedTransaction = await server.prepareTransaction(builtTransaction);
         let preparedTransaction = await server.prepareTransaction(builtTransaction);
 
 
@@ -34,9 +34,8 @@ export async function call_contract_function(method, server, keypair, contract)
                 }
                 }
                 // Extract and return the return value from the contract
                 // Extract and return the return value from the contract
                 let transactionMeta = getResponse.resultMetaXdr;
                 let transactionMeta = getResponse.resultMetaXdr;
-                let returnValue = transactionMeta.v3().sorobanMeta().returnValue();
-                console.log(`Transaction result: ${returnValue.value()}`);
-                res = returnValue.value();
+                let returnValue = transactionMeta.v3().sorobanMeta();
+                res = returnValue;
             } else {
             } else {
                 throw `Transaction failed: ${getResponse.resultXdr}`;
                 throw `Transaction failed: ${getResponse.resultXdr}`;
             }
             }
@@ -62,3 +61,22 @@ export async function call_contract_function(method, server, keypair, contract)
 
 
     return res;
     return res;
 }
 }
+
+export function extractLogEvent(diagnosticEvents) {
+    // Convert events into human-readable format
+    const humanReadableEvents = StellarSdk.humanizeEvents(diagnosticEvents);
+
+    // Find the log event
+    const logEvent = humanReadableEvents.find(event =>
+        event.type === "diagnostic" && event.topics.includes("log")
+    );
+
+    if (logEvent) {
+        return {
+            contractId: logEvent.contractId || "Unknown Contract",
+            logMessages: Array.isArray(logEvent.data) ? logEvent.data : [logEvent.data]
+        };
+    }
+
+    return null; // No log event found
+}

+ 17 - 0
src/codegen/cfg.rs

@@ -372,6 +372,7 @@ pub enum InternalCallTy {
     Static { cfg_no: usize },
     Static { cfg_no: usize },
     Dynamic(Expression),
     Dynamic(Expression),
     Builtin { ast_func_no: usize },
     Builtin { ast_func_no: usize },
+    HostFunction { name: String },
 }
 }
 
 
 #[derive(Clone, PartialEq, Eq)]
 #[derive(Clone, PartialEq, Eq)]
@@ -984,6 +985,9 @@ impl ControlFlowGraph {
             Expression::GetRef { expr, .. } => {
             Expression::GetRef { expr, .. } => {
                 format!("(deref {}", self.expr_to_string(contract, ns, expr))
                 format!("(deref {}", self.expr_to_string(contract, ns, expr))
             }
             }
+            Expression::VectorData { pointer } => {
+                format!("pointer pos {}", self.expr_to_string(contract, ns, pointer))
+            }
         }
         }
     }
     }
 
 
@@ -1172,6 +1176,19 @@ impl ControlFlowGraph {
                     .collect::<Vec<String>>()
                     .collect::<Vec<String>>()
                     .join(", ")
                     .join(", ")
             ),
             ),
+            Instr::Call { res, call: InternalCallTy::HostFunction { name }, args, .. } => {
+                format!("{} = call host function {} {}",
+                        res.iter()
+                            .map(|local| format!("%{}", self.vars[local].id.name))
+                            .collect::<Vec<String>>()
+                            .join(", "),
+                        name,
+                        args.iter()
+                            .map(|expr| self.expr_to_string(contract, ns, expr))
+                            .collect::<Vec<String>>()
+                            .join(", ")
+                )
+            }
             Instr::ExternalCall {
             Instr::ExternalCall {
                 success,
                 success,
                 address,
                 address,

+ 2 - 1
src/codegen/constant_folding.rs

@@ -665,7 +665,8 @@ fn expression(
         | Expression::Undefined { .. }
         | Expression::Undefined { .. }
         | Expression::FormatString { .. }
         | Expression::FormatString { .. }
         | Expression::GetRef { .. }
         | Expression::GetRef { .. }
-        | Expression::InternalFunctionCfg { .. } => (expr.clone(), false),
+        | Expression::InternalFunctionCfg { .. }
+        | Expression::VectorData { .. } => (expr.clone(), false),
         // nothing else is permitted in cfg
         // nothing else is permitted in cfg
         _ => panic!("expr should not be in cfg: {expr:?}"),
         _ => panic!("expr should not be in cfg: {expr:?}"),
     }
     }

+ 45 - 24
src/codegen/dispatch/soroban.rs

@@ -3,7 +3,7 @@
 use num_bigint::BigInt;
 use num_bigint::BigInt;
 use solang_parser::pt::{self};
 use solang_parser::pt::{self};
 
 
-use crate::sema::ast;
+use crate::sema::ast::{self, Function};
 use crate::{
 use crate::{
     codegen::{
     codegen::{
         cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy},
         cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy},
@@ -54,6 +54,7 @@ pub fn function_dispatch(
 
 
         wrapper_cfg.returns = vec![return_type].into();
         wrapper_cfg.returns = vec![return_type].into();
         wrapper_cfg.public = true;
         wrapper_cfg.public = true;
+        wrapper_cfg.function_no = cfg.function_no;
 
 
         let mut vartab = Vartable::from_symbol_table(&function.symtable, ns.next_id);
         let mut vartab = Vartable::from_symbol_table(&function.symtable, ns.next_id);
 
 
@@ -80,29 +81,7 @@ pub fn function_dispatch(
             res: call_returns,
             res: call_returns,
             call: InternalCallTy::Static { cfg_no },
             call: InternalCallTy::Static { cfg_no },
             return_tys,
             return_tys,
-            args: function
-                .params
-                .iter()
-                .enumerate()
-                .map(|(i, p)| Expression::ShiftRight {
-                    loc: pt::Loc::Codegen,
-                    ty: Type::Uint(64),
-                    left: Expression::FunctionArg {
-                        loc: p.loc,
-                        ty: p.ty.clone(),
-                        arg_no: i,
-                    }
-                    .into(),
-                    right: Expression::NumberLiteral {
-                        loc: pt::Loc::Codegen,
-                        ty: Type::Uint(64),
-                        value: BigInt::from(8_u64),
-                    }
-                    .into(),
-
-                    signed: false,
-                })
-                .collect(),
+            args: decode_args(function, ns),
         };
         };
 
 
         wrapper_cfg.add(&mut vartab, placeholder);
         wrapper_cfg.add(&mut vartab, placeholder);
@@ -156,3 +135,45 @@ pub fn function_dispatch(
 
 
     wrapper_cfgs
     wrapper_cfgs
 }
 }
+
+fn decode_args(function: &Function, _ns: &Namespace) -> Vec<Expression> {
+    let mut args = Vec::new();
+
+    for (i, arg) in function.params.iter().enumerate() {
+        let arg = match arg.ty {
+            Type::Uint(64) => Expression::ShiftRight {
+                loc: arg.loc,
+                ty: Type::Uint(64),
+                left: Box::new(Expression::FunctionArg {
+                    loc: arg.loc,
+                    ty: arg.ty.clone(),
+                    arg_no: i,
+                }),
+                right: Box::new(Expression::NumberLiteral {
+                    loc: arg.loc,
+                    ty: Type::Uint(64),
+                    value: BigInt::from(8_u64),
+                }),
+                signed: false,
+            },
+
+            Type::Address(_) => Expression::FunctionArg {
+                loc: arg.loc,
+                ty: arg.ty.clone(),
+                arg_no: i,
+            },
+
+            // TODO: implement encoding/decoding for Int 128
+            Type::Int(128) => Expression::FunctionArg {
+                loc: arg.loc,
+                ty: arg.ty.clone(),
+                arg_no: i,
+            },
+            _ => unimplemented!(),
+        };
+
+        args.push(arg);
+    }
+
+    args
+}

+ 9 - 0
src/codegen/encoding/mod.rs

@@ -11,10 +11,12 @@
 mod borsh_encoding;
 mod borsh_encoding;
 mod buffer_validator;
 mod buffer_validator;
 pub(super) mod scale_encoding;
 pub(super) mod scale_encoding;
+mod soroban_encoding;
 
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::codegen::encoding::borsh_encoding::BorshEncoding;
 use crate::codegen::encoding::borsh_encoding::BorshEncoding;
 use crate::codegen::encoding::scale_encoding::ScaleEncoding;
 use crate::codegen::encoding::scale_encoding::ScaleEncoding;
+use crate::codegen::encoding::soroban_encoding::{soroban_decode, soroban_encode};
 use crate::codegen::expression::load_storage;
 use crate::codegen::expression::load_storage;
 use crate::codegen::vartable::Vartable;
 use crate::codegen::vartable::Vartable;
 use crate::codegen::{Builtin, Expression};
 use crate::codegen::{Builtin, Expression};
@@ -38,6 +40,9 @@ pub(super) fn abi_encode(
     cfg: &mut ControlFlowGraph,
     cfg: &mut ControlFlowGraph,
     packed: bool,
     packed: bool,
 ) -> (Expression, Expression) {
 ) -> (Expression, Expression) {
+    if ns.target == Target::Soroban {
+        return soroban_encode(loc, args, ns, vartab, cfg, packed);
+    }
     let mut encoder = create_encoder(ns, packed);
     let mut encoder = create_encoder(ns, packed);
     let size = calculate_size_args(&mut encoder, &args, ns, vartab, cfg);
     let size = calculate_size_args(&mut encoder, &args, ns, vartab, cfg);
     let encoded_bytes = vartab.temp_name("abi_encoded", &Type::DynamicBytes);
     let encoded_bytes = vartab.temp_name("abi_encoded", &Type::DynamicBytes);
@@ -90,6 +95,10 @@ pub(super) fn abi_decode(
     cfg: &mut ControlFlowGraph,
     cfg: &mut ControlFlowGraph,
     buffer_size_expr: Option<Expression>,
     buffer_size_expr: Option<Expression>,
 ) -> Vec<Expression> {
 ) -> Vec<Expression> {
+    if ns.target == Target::Soroban {
+        return soroban_decode(loc, buffer, types, ns, vartab, cfg, buffer_size_expr);
+    }
+
     let buffer_size = vartab.temp_anonymous(&Uint(32));
     let buffer_size = vartab.temp_anonymous(&Uint(32));
     if let Some(length_expression) = buffer_size_expr {
     if let Some(length_expression) = buffer_size_expr {
         cfg.add(
         cfg.add(

+ 234 - 0
src/codegen/encoding/soroban_encoding.rs

@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::encoding::create_encoder;
+use crate::codegen::vartable::Vartable;
+use crate::codegen::Expression;
+use crate::codegen::HostFunctions;
+use crate::sema::ast::{Namespace, RetrieveType, Type, Type::Uint};
+use num_bigint::BigInt;
+use num_traits::Zero;
+use solang_parser::pt::Loc;
+
+/// Soroban encoder works a little differently than the other encoders.
+/// For an external call, Soroban first needs to convert values into Soroban ScVals.
+/// Each ScVal is 64 bits long, and encoded either via a host function or shifting bits.
+/// For this reason, the soroban encoder is implemented as separate from the other encoders.
+pub fn soroban_encode(
+    loc: &Loc,
+    args: Vec<Expression>,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    packed: bool,
+) -> (Expression, Expression) {
+    let mut encoder = create_encoder(ns, packed);
+
+    let size = 8 * args.len(); // 8 bytes per argument
+
+    let size_expr = Expression::NumberLiteral {
+        loc: *loc,
+        ty: Uint(64),
+        value: size.into(),
+    };
+    let encoded_bytes = vartab.temp_name("abi_encoded", &Type::Bytes(size as u8));
+
+    let expr = Expression::AllocDynamicBytes {
+        loc: *loc,
+        ty: Type::Bytes(size as u8),
+        size: size_expr.clone().into(),
+        initializer: Some(vec![]),
+    };
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: *loc,
+            res: encoded_bytes,
+            expr,
+        },
+    );
+
+    let mut offset = Expression::NumberLiteral {
+        loc: *loc,
+        ty: Uint(64),
+        value: BigInt::zero(),
+    };
+
+    let buffer = Expression::Variable {
+        loc: *loc,
+        ty: Type::Bytes(size as u8),
+        var_no: encoded_bytes,
+    };
+
+    for (arg_no, item) in args.iter().enumerate() {
+        println!("item {:?}", item);
+
+        let obj = vartab.temp_name(format!("obj_{arg_no}").as_str(), &Type::Uint(64));
+
+        let transformer = match item.ty() {
+            Type::String => {
+                let inp = Expression::VectorData {
+                    pointer: Box::new(item.clone()),
+                };
+
+                let encoded = Expression::ShiftLeft {
+                    loc: Loc::Codegen,
+                    ty: Uint(64),
+                    left: Box::new(inp),
+                    right: Box::new(Expression::NumberLiteral {
+                        loc: Loc::Codegen,
+                        ty: Type::Uint(64),
+                        value: BigInt::from(32),
+                    }),
+                };
+
+                let encoded = Expression::Add {
+                    loc: Loc::Codegen,
+                    ty: Type::Uint(64),
+                    overflowing: true,
+                    left: Box::new(encoded),
+                    right: Box::new(Expression::NumberLiteral {
+                        loc: Loc::Codegen,
+                        ty: Type::Uint(64),
+                        value: BigInt::from(4),
+                    }),
+                };
+
+                let len = if let Expression::AllocDynamicBytes { size, .. } = item {
+                    let sesa = Expression::ShiftLeft {
+                        loc: Loc::Codegen,
+                        ty: Uint(64),
+                        left: Box::new(size.clone().cast(&Type::Uint(64), ns)),
+                        right: Box::new(Expression::NumberLiteral {
+                            loc: Loc::Codegen,
+                            ty: Type::Uint(64),
+                            value: BigInt::from(32),
+                        }),
+                    };
+
+                    Expression::Add {
+                        loc: Loc::Codegen,
+                        ty: Type::Uint(64),
+                        overflowing: true,
+                        left: Box::new(sesa),
+                        right: Box::new(Expression::NumberLiteral {
+                            loc: Loc::Codegen,
+                            ty: Type::Uint(64),
+                            value: BigInt::from(4),
+                        }),
+                    }
+                } else {
+                    unreachable!()
+                };
+
+                Instr::Call {
+                    res: vec![obj],
+                    return_tys: vec![Type::Uint(64)],
+                    call: crate::codegen::cfg::InternalCallTy::HostFunction {
+                        name: HostFunctions::SymbolNewFromLinearMemory.name().to_string(),
+                    },
+                    args: vec![encoded, len],
+                }
+            }
+            Type::Uint(64) => {
+                let shift_left = Expression::ShiftLeft {
+                    loc: *loc,
+                    ty: Type::Uint(64),
+                    left: Box::new(item.clone()),
+                    right: Box::new(Expression::NumberLiteral {
+                        loc: *loc,
+                        ty: Type::Uint(64),
+                        value: BigInt::from(8),
+                    }),
+                };
+
+                let added = Expression::Add {
+                    loc: *loc,
+                    ty: Type::Uint(64),
+                    left: Box::new(shift_left),
+                    right: Box::new(Expression::NumberLiteral {
+                        loc: *loc,
+                        ty: Type::Uint(64),
+                        value: BigInt::from(6),
+                    }),
+                    overflowing: false,
+                };
+
+                Instr::Set {
+                    loc: *loc,
+                    res: obj,
+                    expr: added,
+                }
+            }
+            Type::Address(_) => {
+                // pass the address as is
+                Instr::Set {
+                    loc: *loc,
+                    res: obj,
+                    expr: item.clone(),
+                }
+            }
+            // FIXME: Implement encoding/decoding for i128
+            Type::Int(128) => Instr::Set {
+                loc: *loc,
+                res: obj,
+                expr: item.clone(),
+            },
+            _ => todo!("Type not yet supported"),
+        };
+
+        let var = Expression::Variable {
+            loc: *loc,
+            ty: Type::Uint(64),
+            var_no: obj,
+        };
+
+        cfg.add(vartab, transformer);
+
+        let advance = encoder.encode(&var, &buffer, &offset, arg_no, ns, vartab, cfg);
+        offset = Expression::Add {
+            loc: *loc,
+            ty: Uint(64),
+            overflowing: false,
+            left: offset.into(),
+            right: advance.into(),
+        };
+    }
+
+    (buffer, size_expr)
+}
+
+pub fn soroban_decode(
+    loc: &Loc,
+    buffer: &Expression,
+    _types: &[Type],
+    _ns: &Namespace,
+    _vartab: &mut Vartable,
+    _cfg: &mut ControlFlowGraph,
+    _buffer_size_expr: Option<Expression>,
+) -> Vec<Expression> {
+    let mut returns = Vec::new();
+
+    let loaded_val = Expression::Load {
+        loc: Loc::Codegen,
+        ty: Type::Uint(64),
+        expr: Box::new(buffer.clone()),
+    };
+
+    let decoded_val = Expression::ShiftRight {
+        loc: *loc,
+        ty: Type::Uint(64),
+        left: Box::new(loaded_val.clone()),
+        right: Box::new(Expression::NumberLiteral {
+            loc: *loc,
+            ty: Type::Uint(64),
+            value: BigInt::from(8),
+        }),
+        signed: false,
+    };
+
+    returns.push(decoded_val);
+
+    returns
+}

+ 34 - 1
src/codegen/mod.rs

@@ -94,6 +94,34 @@ impl From<inkwell::OptimizationLevel> for OptimizationLevel {
     }
     }
 }
 }
 
 
+pub enum HostFunctions {
+    PutContractData,
+    GetContractData,
+    LogFromLinearMemory,
+    SymbolNewFromLinearMemory,
+    VectorNew,
+    VectorNewFromLinearMemory,
+    Call,
+    ObjToU64,
+    ObjFromU64,
+}
+
+impl HostFunctions {
+    pub fn name(&self) -> &str {
+        match self {
+            HostFunctions::PutContractData => "l._",
+            HostFunctions::GetContractData => "l.1",
+            HostFunctions::LogFromLinearMemory => "x._",
+            HostFunctions::SymbolNewFromLinearMemory => "b.j",
+            HostFunctions::VectorNew => "v._",
+            HostFunctions::VectorNewFromLinearMemory => "v.g",
+            HostFunctions::Call => "d._",
+            HostFunctions::ObjToU64 => "i.0",
+            HostFunctions::ObjFromU64 => "i._",
+        }
+    }
+}
+
 #[derive(Clone, Debug, PartialEq)]
 #[derive(Clone, Debug, PartialEq)]
 pub struct Options {
 pub struct Options {
     pub dead_storage: bool,
     pub dead_storage: bool,
@@ -660,6 +688,9 @@ pub enum Expression {
         pointer: Box<Expression>,
         pointer: Box<Expression>,
         bytes_offset: Box<Expression>,
         bytes_offset: Box<Expression>,
     },
     },
+    VectorData {
+        pointer: Box<Expression>,
+    },
 }
 }
 
 
 impl CodeLocation for Expression {
 impl CodeLocation for Expression {
@@ -716,7 +747,8 @@ impl CodeLocation for Expression {
             Expression::InternalFunctionCfg { .. }
             Expression::InternalFunctionCfg { .. }
             | Expression::Poison
             | Expression::Poison
             | Expression::Undefined { .. }
             | Expression::Undefined { .. }
-            | Expression::AdvancePointer { .. } => pt::Loc::Codegen,
+            | Expression::AdvancePointer { .. }
+            | Expression::VectorData { .. } => pt::Loc::Codegen,
         }
         }
     }
     }
 }
 }
@@ -868,6 +900,7 @@ impl RetrieveType for Expression {
 
 
             Expression::AdvancePointer { .. } => Type::BufferPointer,
             Expression::AdvancePointer { .. } => Type::BufferPointer,
             Expression::FormatString { .. } => Type::String,
             Expression::FormatString { .. } => Type::String,
+            Expression::VectorData { .. } => Type::Uint(64),
             Expression::Poison => unreachable!("Expression does not have a type"),
             Expression::Poison => unreachable!("Expression does not have a type"),
         }
         }
     }
     }

+ 88 - 6
src/emit/binary.rs

@@ -32,7 +32,9 @@ use inkwell::targets::{CodeModel, FileType, RelocMode};
 use inkwell::types::{
 use inkwell::types::{
     ArrayType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StringRadix,
     ArrayType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StringRadix,
 };
 };
-use inkwell::values::{BasicValueEnum, FunctionValue, GlobalValue, IntValue, PointerValue};
+use inkwell::values::{
+    BasicValue, BasicValueEnum, FunctionValue, GlobalValue, IntValue, PointerValue,
+};
 use inkwell::AddressSpace;
 use inkwell::AddressSpace;
 use inkwell::IntPredicate;
 use inkwell::IntPredicate;
 use inkwell::OptimizationLevel;
 use inkwell::OptimizationLevel;
@@ -166,6 +168,8 @@ pub struct Binary<'a> {
     /// No initializer for vector_new
     /// No initializer for vector_new
     pub(crate) vector_init_empty: PointerValue<'a>,
     pub(crate) vector_init_empty: PointerValue<'a>,
     global_constant_strings: RefCell<HashMap<Vec<u8>, PointerValue<'a>>>,
     global_constant_strings: RefCell<HashMap<Vec<u8>, PointerValue<'a>>>,
+
+    pub return_data: RefCell<Option<PointerValue<'a>>>,
 }
 }
 
 
 impl<'a> Binary<'a> {
 impl<'a> Binary<'a> {
@@ -435,6 +439,7 @@ impl<'a> Binary<'a> {
                 .ptr_type(AddressSpace::default())
                 .ptr_type(AddressSpace::default())
                 .const_null(),
                 .const_null(),
             global_constant_strings: RefCell::new(HashMap::new()),
             global_constant_strings: RefCell::new(HashMap::new()),
+            return_data: RefCell::new(None),
         }
         }
     }
     }
 
 
@@ -936,7 +941,12 @@ impl<'a> Binary<'a> {
                         .custom_width_int_type(ns.value_length as u32 * 8),
                         .custom_width_int_type(ns.value_length as u32 * 8),
                 ),
                 ),
                 Type::Contract(_) | Type::Address(_) => {
                 Type::Contract(_) | Type::Address(_) => {
-                    BasicTypeEnum::ArrayType(self.address_type(ns))
+                    // Soroban addresses are 64 bit wide integer that represents a refrenece for the real Address on the Host side.
+                    if ns.target == Target::Soroban {
+                        BasicTypeEnum::IntType(self.context.i64_type())
+                    } else {
+                        BasicTypeEnum::ArrayType(self.address_type(ns))
+                    }
                 }
                 }
                 Type::Bytes(n) => {
                 Type::Bytes(n) => {
                     BasicTypeEnum::IntType(self.context.custom_width_int_type(*n as u32 * 8))
                     BasicTypeEnum::IntType(self.context.custom_width_int_type(*n as u32 * 8))
@@ -1033,7 +1043,73 @@ impl<'a> Binary<'a> {
         size: IntValue<'a>,
         size: IntValue<'a>,
         elem_size: IntValue<'a>,
         elem_size: IntValue<'a>,
         init: Option<&Vec<u8>>,
         init: Option<&Vec<u8>>,
-    ) -> PointerValue<'a> {
+        ty: &Type,
+        ns: &Namespace,
+    ) -> BasicValueEnum<'a> {
+        if self.target == Target::Soroban {
+            if matches!(ty, Type::Bytes(_)) {
+                let n = if let Type::Bytes(n) = ty {
+                    n
+                } else {
+                    unreachable!()
+                };
+
+                let data = self
+                    .builder
+                    .build_alloca(self.context.i64_type().array_type((*n / 8) as u32), "data")
+                    .unwrap();
+
+                let ty = self.context.struct_type(
+                    &[data.get_type().into(), self.context.i64_type().into()],
+                    false,
+                );
+
+                // Start with an undefined struct value
+                let mut struct_value = ty.get_undef();
+
+                // Insert `data` into the first field of the struct
+                struct_value = self
+                    .builder
+                    .build_insert_value(struct_value, data, 0, "insert_data")
+                    .unwrap()
+                    .into_struct_value();
+
+                // Insert `size` into the second field of the struct
+                struct_value = self
+                    .builder
+                    .build_insert_value(struct_value, size, 1, "insert_size")
+                    .unwrap()
+                    .into_struct_value();
+
+                // Return the constructed struct value
+                return struct_value.into();
+            } else if matches!(ty, Type::String) {
+                let bs = init.as_ref().unwrap();
+
+                let data = self.emit_global_string("const_string", bs, true);
+
+                // A constant string, or array, is represented by a struct with two fields: a pointer to the data, and its length.
+                let ty = self.context.struct_type(
+                    &[
+                        self.llvm_type(&Type::Bytes(bs.len() as u8), ns)
+                            .ptr_type(AddressSpace::default())
+                            .into(),
+                        self.context.i64_type().into(),
+                    ],
+                    false,
+                );
+
+                return ty
+                    .const_named_struct(&[
+                        data.into(),
+                        self.context
+                            .i64_type()
+                            .const_int(bs.len() as u64, false)
+                            .into(),
+                    ])
+                    .as_basic_value_enum();
+            }
+        }
         if let Some(init) = init {
         if let Some(init) = init {
             if init.is_empty() {
             if init.is_empty() {
                 return self
                 return self
@@ -1041,7 +1117,8 @@ impl<'a> Binary<'a> {
                     .get_struct_type("struct.vector")
                     .get_struct_type("struct.vector")
                     .unwrap()
                     .unwrap()
                     .ptr_type(AddressSpace::default())
                     .ptr_type(AddressSpace::default())
-                    .const_null();
+                    .const_null()
+                    .as_basic_value_enum();
             }
             }
         }
         }
 
 
@@ -1060,7 +1137,6 @@ impl<'a> Binary<'a> {
             .try_as_basic_value()
             .try_as_basic_value()
             .left()
             .left()
             .unwrap()
             .unwrap()
-            .into_pointer_value()
     }
     }
 
 
     /// Number of element in a vector
     /// Number of element in a vector
@@ -1069,13 +1145,19 @@ impl<'a> Binary<'a> {
             // slice
             // slice
             let slice = vector.into_struct_value();
             let slice = vector.into_struct_value();
 
 
+            let len_type = if self.target == Target::Soroban {
+                self.context.i64_type()
+            } else {
+                self.context.i32_type()
+            };
+
             self.builder
             self.builder
                 .build_int_truncate(
                 .build_int_truncate(
                     self.builder
                     self.builder
                         .build_extract_value(slice, 1, "slice_len")
                         .build_extract_value(slice, 1, "slice_len")
                         .unwrap()
                         .unwrap()
                         .into_int_value(),
                         .into_int_value(),
-                    self.context.i32_type(),
+                    len_type,
                     "len",
                     "len",
                 )
                 )
                 .unwrap()
                 .unwrap()

+ 14 - 2
src/emit/expression.rs

@@ -1581,7 +1581,9 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
                     .into()
                     .into()
             } else {
             } else {
                 let elem = match ty {
                 let elem = match ty {
-                    Type::Slice(_) | Type::String | Type::DynamicBytes => Type::Bytes(1),
+                    Type::Slice(_) | Type::String | Type::DynamicBytes | Type::Bytes(_) => {
+                        Type::Bytes(1)
+                    }
                     _ => ty.array_elem(),
                     _ => ty.array_elem(),
                 };
                 };
 
 
@@ -1593,7 +1595,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
                     .unwrap()
                     .unwrap()
                     .const_cast(bin.context.i32_type(), false);
                     .const_cast(bin.context.i32_type(), false);
 
 
-                bin.vector_new(size, elem_size, initializer.as_ref()).into()
+                bin.vector_new(size, elem_size, initializer.as_ref(), ty, ns)
             }
             }
         }
         }
         Expression::Builtin {
         Expression::Builtin {
@@ -2147,6 +2149,16 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
             advanced.into()
             advanced.into()
         }
         }
 
 
+        Expression::VectorData { pointer } => {
+            let ptr = expression(target, bin, pointer, vartab, function, ns);
+            let data = bin.vector_bytes(ptr);
+            let res = bin
+                .builder
+                .build_ptr_to_int(data, bin.context.i64_type(), "sesa");
+
+            res.unwrap().into()
+        }
+
         Expression::RationalNumberLiteral { .. }
         Expression::RationalNumberLiteral { .. }
         | Expression::Undefined { .. }
         | Expression::Undefined { .. }
         | Expression::Poison
         | Expression::Poison

+ 97 - 47
src/emit/instructions.rs

@@ -753,6 +753,30 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
                 }
                 }
             }
             }
         }
         }
+        Instr::Call {
+            res,
+            call: InternalCallTy::HostFunction { name },
+            args,
+            ..
+        } => {
+            let parms = args
+                .iter()
+                .map(|p| expression(target, bin, p, &w.vars, function, ns).into())
+                .collect::<Vec<BasicMetadataValueEnum>>();
+
+            let call = bin.module.get_function(name).unwrap();
+
+            let ret = bin
+                .builder
+                .build_call(call, &parms, "")
+                .unwrap()
+                .try_as_basic_value()
+                .left();
+
+            if let Some(value) = ret {
+                w.vars.get_mut(&res[0]).unwrap().value = value;
+            }
+        }
         Instr::Constructor {
         Instr::Constructor {
             success,
             success,
             res,
             res,
@@ -903,19 +927,22 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
 
 
             let address = if let Some(address) = address {
             let address = if let Some(address) = address {
                 let address = expression(target, bin, address, &w.vars, function, ns);
                 let address = expression(target, bin, address, &w.vars, function, ns);
+                if ns.target == Target::Soroban {
+                    Some(address)
+                } else {
+                    let addr = bin.build_array_alloca(
+                        function,
+                        bin.context.i8_type(),
+                        bin.context
+                            .i32_type()
+                            .const_int(ns.address_length as u64, false),
+                        "address",
+                    );
 
 
-                let addr = bin.build_array_alloca(
-                    function,
-                    bin.context.i8_type(),
-                    bin.context
-                        .i32_type()
-                        .const_int(ns.address_length as u64, false),
-                    "address",
-                );
-
-                bin.builder.build_store(addr, address).unwrap();
+                    bin.builder.build_store(addr, address).unwrap();
 
 
-                Some(addr)
+                    Some(addr.as_basic_value_enum())
+                }
             } else {
             } else {
                 None
                 None
             };
             };
@@ -1012,45 +1039,68 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
             let offset = expression(target, bin, offset, &w.vars, function, ns).into_int_value();
             let offset = expression(target, bin, offset, &w.vars, function, ns).into_int_value();
             let emit_value = expression(target, bin, value, &w.vars, function, ns);
             let emit_value = expression(target, bin, value, &w.vars, function, ns);
 
 
-            let start = unsafe {
-                bin.builder
-                    .build_gep(bin.context.i8_type(), data, &[offset], "start")
-                    .unwrap()
-            };
-
-            let is_bytes = if let Type::Bytes(n) = value.ty().unwrap_user_type(ns) {
-                n
-            } else if value.ty() == Type::FunctionSelector {
-                ns.target.selector_length()
-            } else {
-                0
-            };
-
-            if is_bytes > 1 {
-                let value_ptr = bin.build_alloca(
-                    function,
-                    emit_value.into_int_value().get_type(),
-                    &format!("bytes{is_bytes}"),
-                );
-                bin.builder
-                    .build_store(value_ptr, emit_value.into_int_value())
-                    .unwrap();
-                bin.builder
-                    .build_call(
-                        bin.module.get_function("__leNtobeN").unwrap(),
-                        &[
-                            value_ptr.into(),
-                            start.into(),
-                            bin.context
-                                .i32_type()
-                                .const_int(is_bytes as u64, false)
-                                .into(),
-                        ],
-                        "",
+            if ns.target == Target::Soroban {
+                let new_offset = bin
+                    .builder
+                    .build_int_unsigned_div(
+                        offset,
+                        bin.context.i64_type().const_int(8, false),
+                        "new_offset",
                     )
                     )
                     .unwrap();
                     .unwrap();
-            } else {
+                let start = unsafe {
+                    bin.builder
+                        .build_gep(
+                            bin.context.i64_type().array_type(1),
+                            data,
+                            &[bin.context.i64_type().const_zero(), new_offset],
+                            "start",
+                        )
+                        .unwrap()
+                };
+
                 bin.builder.build_store(start, emit_value).unwrap();
                 bin.builder.build_store(start, emit_value).unwrap();
+            } else {
+                let start = unsafe {
+                    bin.builder
+                        .build_gep(bin.context.i8_type(), data, &[offset], "start")
+                        .unwrap()
+                };
+
+                let is_bytes = if let Type::Bytes(n) = value.ty().unwrap_user_type(ns) {
+                    n
+                } else if value.ty() == Type::FunctionSelector {
+                    ns.target.selector_length()
+                } else {
+                    0
+                };
+
+                if is_bytes > 1 {
+                    let value_ptr = bin.build_alloca(
+                        function,
+                        emit_value.into_int_value().get_type(),
+                        &format!("bytes{is_bytes}"),
+                    );
+                    bin.builder
+                        .build_store(value_ptr, emit_value.into_int_value())
+                        .unwrap();
+                    bin.builder
+                        .build_call(
+                            bin.module.get_function("__leNtobeN").unwrap(),
+                            &[
+                                value_ptr.into(),
+                                start.into(),
+                                bin.context
+                                    .i32_type()
+                                    .const_int(is_bytes as u64, false)
+                                    .into(),
+                            ],
+                            "",
+                        )
+                        .unwrap();
+                } else {
+                    bin.builder.build_store(start, emit_value).unwrap();
+                }
             }
             }
         }
         }
         Instr::MemCopy {
         Instr::MemCopy {

+ 1 - 1
src/emit/mod.rs

@@ -260,7 +260,7 @@ pub trait TargetRuntime<'a> {
         success: Option<&mut BasicValueEnum<'b>>,
         success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         payload_len: IntValue<'b>,
-        address: Option<PointerValue<'b>>,
+        address: Option<BasicValueEnum<'b>>,
         contract_args: ContractArgs<'b>,
         contract_args: ContractArgs<'b>,
         ty: CallTy,
         ty: CallTy,
         ns: &Namespace,
         ns: &Namespace,

+ 1 - 1
src/emit/polkadot/target.rs

@@ -927,7 +927,7 @@ impl<'a> TargetRuntime<'a> for PolkadotTarget {
         success: Option<&mut BasicValueEnum<'b>>,
         success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         payload_len: IntValue<'b>,
-        address: Option<PointerValue<'b>>,
+        address: Option<BasicValueEnum<'b>>,
         contract_args: ContractArgs<'b>,
         contract_args: ContractArgs<'b>,
         call_type: ast::CallTy,
         call_type: ast::CallTy,
         ns: &ast::Namespace,
         ns: &ast::Namespace,

+ 5 - 3
src/emit/solana/target.rs

@@ -825,7 +825,9 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
                         .unwrap()
                         .unwrap()
                         .into_int_value();
                         .into_int_value();
 
 
-                    dest = binary.vector_new(length, elem_size, None);
+                    dest = binary
+                        .vector_new(length, elem_size, None, elem_ty, ns)
+                        .into_pointer_value();
                 };
                 };
 
 
                 let elem_size = elem_ty.solana_storage_size(ns).to_u64().unwrap();
                 let elem_size = elem_ty.solana_storage_size(ns).to_u64().unwrap();
@@ -1493,7 +1495,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         _success: Option<&mut BasicValueEnum<'b>>,
         _success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         payload_len: IntValue<'b>,
-        address: Option<PointerValue<'b>>,
+        address: Option<BasicValueEnum<'b>>,
         mut contract_args: ContractArgs<'b>,
         mut contract_args: ContractArgs<'b>,
         _ty: ast::CallTy,
         _ty: ast::CallTy,
         ns: &ast::Namespace,
         ns: &ast::Namespace,
@@ -1512,7 +1514,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
             ))
             ))
         };
         };
 
 
-        contract_args.program_id = Some(address);
+        contract_args.program_id = Some(address.into_pointer_value());
         self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args, ns);
         self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args, ns);
     }
     }
 
 

+ 69 - 33
src/emit/soroban/mod.rs

@@ -3,7 +3,7 @@
 pub(super) mod target;
 pub(super) mod target;
 use crate::codegen::{
 use crate::codegen::{
     cfg::{ASTFunction, ControlFlowGraph},
     cfg::{ASTFunction, ControlFlowGraph},
-    Options, STORAGE_INITIALIZER,
+    HostFunctions, Options, STORAGE_INITIALIZER,
 };
 };
 
 
 use crate::emit::cfg::emit_cfg;
 use crate::emit::cfg::emit_cfg;
@@ -12,6 +12,7 @@ use funty::Fundamental;
 use inkwell::{
 use inkwell::{
     context::Context,
     context::Context,
     module::{Linkage, Module},
     module::{Linkage, Module},
+    types::FunctionType,
 };
 };
 use soroban_sdk::xdr::{
 use soroban_sdk::xdr::{
     Limited, Limits, ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion, ScSpecEntry,
     Limited, Limits, ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion, ScSpecEntry,
@@ -25,9 +26,41 @@ const SOROBAN_ENV_INTERFACE_VERSION: ScEnvMetaEntryInterfaceVersion =
         protocol: 22,
         protocol: 22,
         pre_release: 0,
         pre_release: 0,
     };
     };
-pub const PUT_CONTRACT_DATA: &str = "l._";
-pub const GET_CONTRACT_DATA: &str = "l.1";
-pub const LOG_FROM_LINEAR_MEMORY: &str = "x._";
+
+impl HostFunctions {
+    pub fn function_signature<'b>(&self, bin: &Binary<'b>) -> FunctionType<'b> {
+        let ty = bin.context.i64_type();
+        match self {
+            HostFunctions::PutContractData => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into(), ty.into()], false),
+            HostFunctions::GetContractData => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into()], false),
+            HostFunctions::LogFromLinearMemory => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into(), ty.into(), ty.into()], false),
+            HostFunctions::SymbolNewFromLinearMemory => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into()], false),
+            HostFunctions::VectorNew => bin.context.i64_type().fn_type(&[], false),
+            HostFunctions::Call => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into(), ty.into()], false),
+            HostFunctions::VectorNewFromLinearMemory => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into()], false),
+            HostFunctions::ObjToU64 => bin.context.i64_type().fn_type(&[ty.into()], false),
+            HostFunctions::ObjFromU64 => bin.context.i64_type().fn_type(&[ty.into()], false),
+        }
+    }
+}
 
 
 pub struct SorobanTarget;
 pub struct SorobanTarget;
 
 
@@ -174,8 +207,18 @@ impl SorobanTarget {
                             .unwrap_or_else(|| i.to_string())
                             .unwrap_or_else(|| i.to_string())
                             .try_into()
                             .try_into()
                             .expect("function input name exceeds limit"),
                             .expect("function input name exceeds limit"),
-                        type_: ScSpecTypeDef::U64, // TODO: Map type.
-                        doc: StringM::default(),   // TODO: Add doc.
+                        type_: match p.ty {
+                            ast::Type::Uint(32) => ScSpecTypeDef::U32,
+                            ast::Type::Uint(64) => ScSpecTypeDef::U64,
+                            ast::Type::Int(128) => ScSpecTypeDef::I128,
+                            ast::Type::Bool => ScSpecTypeDef::Bool,
+                            ast::Type::Address(_) => ScSpecTypeDef::Address,
+                            ast::Type::Bytes(_) => ScSpecTypeDef::Bytes,
+                            ast::Type::String => ScSpecTypeDef::String,
+                            //ast::Type::Val => ScSpecTypeDef::Address,
+                            _ => panic!("unsupported input type {:?}", p.ty),
+                        }, // TODO: Map type.
+                        doc: StringM::default(), // TODO: Add doc.
                     })
                     })
                     .collect::<Vec<_>>()
                     .collect::<Vec<_>>()
                     .try_into()
                     .try_into()
@@ -233,33 +276,26 @@ impl SorobanTarget {
     }
     }
 
 
     fn declare_externals(binary: &mut Binary) {
     fn declare_externals(binary: &mut Binary) {
-        let ty = binary.context.i64_type();
-        let function_ty_1 = binary
-            .context
-            .i64_type()
-            .fn_type(&[ty.into(), ty.into(), ty.into()], false);
-        let function_ty = binary
-            .context
-            .i64_type()
-            .fn_type(&[ty.into(), ty.into()], false);
-
-        let log_function_ty = binary
-            .context
-            .i64_type()
-            .fn_type(&[ty.into(), ty.into(), ty.into(), ty.into()], false);
-
-        binary
-            .module
-            .add_function(PUT_CONTRACT_DATA, function_ty_1, Some(Linkage::External));
-        binary
-            .module
-            .add_function(GET_CONTRACT_DATA, function_ty, Some(Linkage::External));
-
-        binary.module.add_function(
-            LOG_FROM_LINEAR_MEMORY,
-            log_function_ty,
-            Some(Linkage::External),
-        );
+        let host_functions = [
+            HostFunctions::PutContractData,
+            HostFunctions::GetContractData,
+            HostFunctions::LogFromLinearMemory,
+            HostFunctions::SymbolNewFromLinearMemory,
+            HostFunctions::VectorNew,
+            HostFunctions::Call,
+            HostFunctions::VectorNewFromLinearMemory,
+            HostFunctions::ObjToU64,
+            HostFunctions::ObjFromU64,
+            HostFunctions::PutContractData,
+        ];
+
+        for func in &host_functions {
+            binary.module.add_function(
+                func.name(),
+                func.function_signature(binary),
+                Some(Linkage::External),
+            );
+        }
     }
     }
 
 
     fn emit_initializer(binary: &mut Binary, _ns: &ast::Namespace) {
     fn emit_initializer(binary: &mut Binary, _ns: &ast::Namespace) {

+ 128 - 46
src/emit/soroban/target.rs

@@ -3,9 +3,7 @@
 use crate::codegen::cfg::HashTy;
 use crate::codegen::cfg::HashTy;
 use crate::codegen::Expression;
 use crate::codegen::Expression;
 use crate::emit::binary::Binary;
 use crate::emit::binary::Binary;
-use crate::emit::soroban::{
-    SorobanTarget, GET_CONTRACT_DATA, LOG_FROM_LINEAR_MEMORY, PUT_CONTRACT_DATA,
-};
+use crate::emit::soroban::{HostFunctions, SorobanTarget};
 use crate::emit::ContractArgs;
 use crate::emit::ContractArgs;
 use crate::emit::{TargetRuntime, Variable};
 use crate::emit::{TargetRuntime, Variable};
 use crate::emit_context;
 use crate::emit_context;
@@ -48,7 +46,7 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
         let storage_type = storage_type_to_int(storage_type);
         let storage_type = storage_type_to_int(storage_type);
         emit_context!(binary);
         emit_context!(binary);
         let ret = call!(
         let ret = call!(
-            GET_CONTRACT_DATA,
+            HostFunctions::GetContractData.name(),
             &[
             &[
                 slot.as_basic_value_enum()
                 slot.as_basic_value_enum()
                     .into_int_value()
                     .into_int_value()
@@ -85,7 +83,10 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
 
 
         let storage_type = storage_type_to_int(storage_type);
         let storage_type = storage_type_to_int(storage_type);
 
 
-        let function_value = binary.module.get_function(PUT_CONTRACT_DATA).unwrap();
+        let function_value = binary
+            .module
+            .get_function(HostFunctions::PutContractData.name())
+            .unwrap();
 
 
         let value = binary
         let value = binary
             .builder
             .builder
@@ -103,7 +104,7 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
                         .const_int(storage_type, false)
                         .const_int(storage_type, false)
                         .into(),
                         .into(),
                 ],
                 ],
-                PUT_CONTRACT_DATA,
+                HostFunctions::PutContractData.name(),
             )
             )
             .unwrap()
             .unwrap()
             .try_as_basic_value()
             .try_as_basic_value()
@@ -258,51 +259,21 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
                 .builder
                 .builder
                 .build_ptr_to_int(string, bin.context.i64_type(), "msg_pos")
                 .build_ptr_to_int(string, bin.context.i64_type(), "msg_pos")
                 .unwrap();
                 .unwrap();
-            let msg_pos = msg_pos.const_cast(bin.context.i64_type(), false);
-
             let length = length.const_cast(bin.context.i64_type(), false);
             let length = length.const_cast(bin.context.i64_type(), false);
 
 
-            let eight = bin.context.i64_type().const_int(8, false);
-            let four = bin.context.i64_type().const_int(4, false);
-            let zero = bin.context.i64_type().const_int(0, false);
-            let thirty_two = bin.context.i64_type().const_int(32, false);
-
-            // XDR encode msg_pos and length
-            let msg_pos_encoded = bin
-                .builder
-                .build_left_shift(msg_pos, thirty_two, "temp")
-                .unwrap();
-            let msg_pos_encoded = bin
-                .builder
-                .build_int_add(msg_pos_encoded, four, "msg_pos_encoded")
-                .unwrap();
-
-            let length_encoded = bin
-                .builder
-                .build_left_shift(length, thirty_two, "temp")
-                .unwrap();
-            let length_encoded = bin
-                .builder
-                .build_int_add(length_encoded, four, "length_encoded")
-                .unwrap();
-
-            let zero_encoded = bin.builder.build_left_shift(zero, eight, "temp").unwrap();
-
-            let eight_encoded = bin.builder.build_left_shift(eight, eight, "temp").unwrap();
-            let eight_encoded = bin
-                .builder
-                .build_int_add(eight_encoded, four, "eight_encoded")
-                .unwrap();
+            let msg_pos_encoded = encode_value(msg_pos, 32, 4, bin);
+            let length_encoded = encode_value(length, 32, 4, bin);
 
 
-            let call_res = bin
-                .builder
+            bin.builder
                 .build_call(
                 .build_call(
-                    bin.module.get_function(LOG_FROM_LINEAR_MEMORY).unwrap(),
+                    bin.module
+                        .get_function(HostFunctions::LogFromLinearMemory.name())
+                        .unwrap(),
                     &[
                     &[
                         msg_pos_encoded.into(),
                         msg_pos_encoded.into(),
                         length_encoded.into(),
                         length_encoded.into(),
                         msg_pos_encoded.into(),
                         msg_pos_encoded.into(),
-                        four.into(),
+                        encode_value(bin.context.i64_type().const_zero(), 32, 4, bin).into(),
                     ],
                     ],
                     "log",
                     "log",
                 )
                 )
@@ -364,13 +335,106 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
         success: Option<&mut BasicValueEnum<'b>>,
         success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         payload_len: IntValue<'b>,
-        address: Option<PointerValue<'b>>,
+        address: Option<BasicValueEnum<'b>>,
         contract_args: ContractArgs<'b>,
         contract_args: ContractArgs<'b>,
         ty: CallTy,
         ty: CallTy,
         ns: &Namespace,
         ns: &Namespace,
         loc: Loc,
         loc: Loc,
     ) {
     ) {
-        unimplemented!()
+        let offset = bin.context.i64_type().const_int(0, false);
+
+        let start = unsafe {
+            bin.builder
+                .build_gep(
+                    bin.context.i64_type().array_type(1),
+                    payload,
+                    &[bin.context.i64_type().const_zero(), offset],
+                    "start",
+                )
+                .unwrap()
+        };
+
+        let symbol = bin
+            .builder
+            .build_load(bin.context.i64_type(), start, "symbol")
+            .unwrap()
+            .into_int_value();
+
+        let args_len = bin
+            .builder
+            .build_int_unsigned_div(
+                payload_len,
+                bin.context.i64_type().const_int(8, false),
+                "args_len",
+            )
+            .unwrap();
+
+        let args_len = bin
+            .builder
+            .build_int_sub(
+                args_len,
+                bin.context.i64_type().const_int(1, false),
+                "args_len",
+            )
+            .unwrap();
+
+        let args_len_encoded = encode_value(args_len, 32, 4, bin);
+
+        let offset = bin.context.i64_type().const_int(1, false);
+        let args_ptr = unsafe {
+            bin.builder
+                .build_gep(
+                    bin.context.i64_type().array_type(1),
+                    payload,
+                    &[bin.context.i64_type().const_zero(), offset],
+                    "start",
+                )
+                .unwrap()
+        };
+
+        let args_ptr_to_int = bin
+            .builder
+            .build_ptr_to_int(args_ptr, bin.context.i64_type(), "args_ptr")
+            .unwrap();
+
+        let args_ptr_encoded = encode_value(args_ptr_to_int, 32, 4, bin);
+
+        let vec_object = bin
+            .builder
+            .build_call(
+                bin.module
+                    .get_function(HostFunctions::VectorNewFromLinearMemory.name())
+                    .unwrap(),
+                &[args_ptr_encoded.into(), args_len_encoded.into()],
+                "vec_object",
+            )
+            .unwrap()
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_int_value();
+
+        let call_res = bin
+            .builder
+            .build_call(
+                bin.module.get_function(HostFunctions::Call.name()).unwrap(),
+                &[address.unwrap().into(), symbol.into(), vec_object.into()],
+                "call",
+            )
+            .unwrap()
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_int_value();
+
+        let allocate_i64 = bin
+            .builder
+            .build_alloca(bin.context.i64_type(), "allocate_i64")
+            .unwrap();
+
+        bin.builder.build_store(allocate_i64, call_res).unwrap();
+
+        *bin.return_data.borrow_mut() = Some(allocate_i64);
     }
     }
 
 
     /// send value to address
     /// send value to address
@@ -401,7 +465,7 @@ impl<'a> TargetRuntime<'a> for SorobanTarget {
 
 
     /// Return the return data from an external call (either revert error or return values)
     /// Return the return data from an external call (either revert error or return values)
     fn return_data<'b>(&self, bin: &Binary<'b>, function: FunctionValue<'b>) -> PointerValue<'b> {
     fn return_data<'b>(&self, bin: &Binary<'b>, function: FunctionValue<'b>) -> PointerValue<'b> {
-        unimplemented!()
+        bin.return_data.borrow().unwrap()
     }
     }
 
 
     /// Return the value we received
     /// Return the value we received
@@ -460,3 +524,21 @@ fn storage_type_to_int(storage_type: &Option<StorageType>) -> u64 {
         1
         1
     }
     }
 }
 }
+
+fn encode_value<'a>(value: IntValue<'a>, shift: u64, add: u64, bin: &'a Binary) -> IntValue<'a> {
+    let shifted = bin
+        .builder
+        .build_left_shift(
+            value,
+            bin.context.i64_type().const_int(shift, false),
+            "temp",
+        )
+        .unwrap();
+    bin.builder
+        .build_int_add(
+            shifted,
+            bin.context.i64_type().const_int(add, false),
+            "encoded",
+        )
+        .unwrap()
+}

+ 9 - 1
src/emit/strings.rs

@@ -95,7 +95,15 @@ pub(super) fn format_string<'a, T: TargetRuntime<'a> + ?Sized>(
     }
     }
 
 
     // allocate the string and
     // allocate the string and
-    let vector = bin.vector_new(length, bin.context.i32_type().const_int(1, false), None);
+    let vector = bin
+        .vector_new(
+            length,
+            bin.context.i32_type().const_int(1, false),
+            None,
+            &Type::String,
+            ns,
+        )
+        .into_pointer_value();
 
 
     let output_start = bin.vector_bytes(vector.into());
     let output_start = bin.vector_bytes(vector.into());
 
 

+ 3 - 8
src/linker/soroban_wasm.rs

@@ -11,8 +11,6 @@ use wasm_encoder::{
 };
 };
 use wasmparser::{Global, Import, Parser, Payload::*, SectionLimited, TypeRef};
 use wasmparser::{Global, Import, Parser, Payload::*, SectionLimited, TypeRef};
 
 
-use crate::emit::soroban::{GET_CONTRACT_DATA, LOG_FROM_LINEAR_MEMORY, PUT_CONTRACT_DATA};
-
 pub fn link(input: &[u8], name: &str) -> Vec<u8> {
 pub fn link(input: &[u8], name: &str) -> Vec<u8> {
     let dir = tempdir().expect("failed to create temp directory for linking");
     let dir = tempdir().expect("failed to create temp directory for linking");
 
 
@@ -27,11 +25,12 @@ pub fn link(input: &[u8], name: &str) -> Vec<u8> {
         .expect("failed to write object file to temp file");
         .expect("failed to write object file to temp file");
 
 
     let mut command_line = vec![
     let mut command_line = vec![
-        CString::new("-O3").unwrap(),
         CString::new("--no-entry").unwrap(),
         CString::new("--no-entry").unwrap(),
         CString::new("--allow-undefined").unwrap(),
         CString::new("--allow-undefined").unwrap(),
         CString::new("--gc-sections").unwrap(),
         CString::new("--gc-sections").unwrap(),
         CString::new("--global-base=0").unwrap(),
         CString::new("--global-base=0").unwrap(),
+        CString::new("--initial-memory=1048576").unwrap(), // 1 MiB initial memory
+        CString::new("--max-memory=1048576").unwrap(),
     ];
     ];
     command_line.push(CString::new("--export-dynamic").unwrap());
     command_line.push(CString::new("--export-dynamic").unwrap());
 
 
@@ -95,11 +94,7 @@ fn generate_import_section(section: SectionLimited<Import>, module: &mut Module)
             }),
             }),
             _ => panic!("unexpected WASM import section {:?}", import),
             _ => panic!("unexpected WASM import section {:?}", import),
         };
         };
-        let module_name = match import.name {
-            GET_CONTRACT_DATA | PUT_CONTRACT_DATA => "l",
-            LOG_FROM_LINEAR_MEMORY => "x",
-            _ => panic!("got func {:?}", import),
-        };
+        let module_name = import.name.split('.').next().unwrap();
         // parse the import name to all string after the the first dot
         // parse the import name to all string after the the first dot
         let import_name = import.name.split('.').nth(1).unwrap();
         let import_name = import.name.split('.').nth(1).unwrap();
         imports.import(module_name, import_name, import_type);
         imports.import(module_name, import_name, import_type);

+ 21 - 0
src/lir/converter/expression.rs

@@ -420,9 +420,30 @@ impl Converter<'_> {
                 bytes_offset,
                 bytes_offset,
                 ..
                 ..
             } => self.advance_pointer(dest, pointer, bytes_offset, vartable, results),
             } => self.advance_pointer(dest, pointer, bytes_offset, vartable, results),
+
+            codegen::Expression::VectorData { pointer } => {
+                self.pointer_position(dest, pointer, vartable, results)
+            }
         }
         }
     }
     }
 
 
+    fn pointer_position(
+        &self,
+        dest: &Operand,
+        pointer: &codegen::Expression,
+        vartable: &mut Vartable,
+        results: &mut Vec<Instruction>,
+    ) {
+        let pointer_op = self.to_operand_and_insns(pointer, vartable, results);
+        results.push(Instruction::Set {
+            loc: Loc::Codegen,
+            res: dest.get_id_or_error(),
+            expr: Expression::VectorData {
+                pointer: Box::new(pointer_op),
+            },
+        });
+    }
+
     fn advance_pointer(
     fn advance_pointer(
         &self,
         &self,
         dest: &Operand,
         dest: &Operand,

+ 3 - 0
src/lir/converter/mod.rs

@@ -183,6 +183,9 @@ impl<'input> Converter<'input> {
                 let tmp = self.to_operand_and_insns(expr, vartable, result);
                 let tmp = self.to_operand_and_insns(expr, vartable, result);
                 InternalCallTy::Dynamic(tmp)
                 InternalCallTy::Dynamic(tmp)
             }
             }
+            cfg::InternalCallTy::HostFunction { name: sesa } => {
+                InternalCallTy::HostFunction { name: sesa.clone() }
+            }
         }
         }
     }
     }
 
 

+ 4 - 0
src/lir/expressions.rs

@@ -231,6 +231,10 @@ pub enum Expression {
     ReturnData {
     ReturnData {
         loc: Loc,
         loc: Loc,
     },
     },
+
+    VectorData {
+        pointer: Box<Operand>,
+    },
 }
 }
 
 
 impl fmt::Display for BinaryOperator {
 impl fmt::Display for BinaryOperator {

+ 1 - 0
src/lir/lir_type.rs

@@ -55,6 +55,7 @@ pub enum InternalCallTy {
     Static { cfg_no: usize },
     Static { cfg_no: usize },
     Dynamic(Operand),
     Dynamic(Operand),
     Builtin { ast_func_no: usize },
     Builtin { ast_func_no: usize },
+    HostFunction { name: String },
 }
 }
 
 
 #[derive(Clone, Debug)]
 #[derive(Clone, Debug)]

+ 5 - 0
src/lir/printer/expression.rs

@@ -267,6 +267,11 @@ impl Printer<'_> {
                 });
                 });
                 write!(f, ")").unwrap();
                 write!(f, ")").unwrap();
             }
             }
+            Expression::VectorData { pointer } => {
+                write!(f, "ptr_pos(").unwrap();
+                self.print_rhs_operand(f, pointer);
+                write!(f, ")").unwrap();
+            }
         }
         }
     }
     }
 }
 }

+ 3 - 0
src/lir/printer/instruction.rs

@@ -249,6 +249,9 @@ impl Printer<'_> {
                         write!(f, "function#{}", cfg_no).unwrap()
                         write!(f, "function#{}", cfg_no).unwrap()
                     }
                     }
                     InternalCallTy::Dynamic(op) => self.print_rhs_operand(f, op),
                     InternalCallTy::Dynamic(op) => self.print_rhs_operand(f, op),
+                    InternalCallTy::HostFunction { name } => {
+                        write!(f, "host_function#{}", name).unwrap()
+                    }
                 };
                 };
 
 
                 write!(f, "(").unwrap();
                 write!(f, "(").unwrap();

+ 17 - 3
tests/soroban.rs

@@ -5,6 +5,7 @@ pub mod soroban_testcases;
 
 
 use solang::codegen::Options;
 use solang::codegen::Options;
 use solang::file_resolver::FileResolver;
 use solang::file_resolver::FileResolver;
+use solang::sema::ast::Namespace;
 use solang::sema::diagnostics::Diagnostics;
 use solang::sema::diagnostics::Diagnostics;
 use solang::{compile, Target};
 use solang::{compile, Target};
 use soroban_sdk::testutils::Logs;
 use soroban_sdk::testutils::Logs;
@@ -19,6 +20,11 @@ pub struct SorobanEnv {
 }
 }
 
 
 pub fn build_solidity(src: &str) -> SorobanEnv {
 pub fn build_solidity(src: &str) -> SorobanEnv {
+    let (wasm_blob, ns) = build_wasm(src);
+    SorobanEnv::new_with_contract(wasm_blob).insert_diagnostics(ns.diagnostics)
+}
+
+fn build_wasm(src: &str) -> (Vec<u8>, Namespace) {
     let tmp_file = OsStr::new("test.sol");
     let tmp_file = OsStr::new("test.sol");
     let mut cache = FileResolver::default();
     let mut cache = FileResolver::default();
     cache.set_file_contents(tmp_file.to_str().unwrap(), src.to_string());
     cache.set_file_contents(tmp_file.to_str().unwrap(), src.to_string());
@@ -40,10 +46,8 @@ pub fn build_solidity(src: &str) -> SorobanEnv {
         std::vec!["unknown".to_string()],
         std::vec!["unknown".to_string()],
         "0.0.1",
         "0.0.1",
     );
     );
-    ns.print_diagnostics_in_plain(&cache, false);
     assert!(!wasm.is_empty());
     assert!(!wasm.is_empty());
-    let wasm_blob = wasm[0].0.clone();
-    SorobanEnv::new_with_contract(wasm_blob).insert_diagnostics(ns.diagnostics)
+    (wasm[0].0.clone(), ns)
 }
 }
 
 
 impl SorobanEnv {
 impl SorobanEnv {
@@ -106,6 +110,16 @@ impl SorobanEnv {
 
 
         self.env.logs().all()
         self.env.logs().all()
     }
     }
+
+    pub fn deploy_contract(&mut self, src: &str) -> Address {
+        let wasm = build_wasm(src).0;
+
+        let addr = self.register_contract(wasm);
+
+        self.contracts.push(addr.clone());
+
+        addr
+    }
 }
 }
 
 
 impl Default for SorobanEnv {
 impl Default for SorobanEnv {

+ 54 - 0
tests/soroban_testcases/cross_contract_calls.rs

@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::build_solidity;
+use soroban_sdk::{IntoVal, Val};
+
+#[test]
+fn simple_cross_contract() {
+    let mut runtime = build_solidity(
+        r#"contract math {
+        function max(uint64 a, uint64 b) public returns (uint64) {
+            if (a > b) {
+                return a;
+            } else {
+                return b;
+            }
+        }
+    }"#,
+    );
+
+    let caller = runtime.deploy_contract(
+        r#"contract mcaller {
+    function call_max(
+        address addr,
+        uint64 a,
+        uint64 b
+    ) public returns (uint64) {
+        bytes payload = abi.encode("max", a, b);
+        (bool success, bytes returndata) = addr.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+        return result;
+    }
+}
+"#,
+    );
+
+    let arg: Val = 3_u64.into_val(&runtime.env);
+    let arg2: Val = 1_u64.into_val(&runtime.env);
+
+    let addr = runtime.contracts.first().unwrap();
+    let res = runtime.invoke_contract(addr, "max", vec![arg, arg2]);
+    println!("first res {:?}", res);
+
+    let expected: Val = 3_u64.into_val(&runtime.env);
+    assert!(expected.shallow_eq(&res));
+
+    let res = runtime.invoke_contract(
+        &caller,
+        "call_max",
+        vec![addr.into_val(&runtime.env), arg, arg2],
+    );
+
+    println!("second res {:?}", res);
+    assert!(expected.shallow_eq(&res));
+}

+ 1 - 0
tests/soroban_testcases/mod.rs

@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
+mod cross_contract_calls;
 mod math;
 mod math;
 mod print;
 mod print;
 mod storage;
 mod storage;