Bläddra i källkod

Add support for soroban authorization framework (#1748)

This PR aims to support Soroban authorization framework: 
-
https://github.com/stellar/stellar-protocol/blob/master/core/cap-0046-11.md
-
https://developers.stellar.org/docs/build/smart-contracts/example-contracts/auth

Here are some keynotes to consider:

1- Unlike Solidity, Soroban performs its auth checks using the host. For
instance, Soroban doesn't have the construct of `if msg.sender == addr`,
however this check is done via a host function `addr.require_auth`.

2- Another function was added `authAsCurrContract`. This is needed for
deeper calls. To understand this, take a look at
https://github.com/stellar/soroban-examples/tree/main/deep_contract_auth.
What I have done here actually is test the auth framework using the
above provided example of nested calls.

---------

Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
salaheldinsoliman 8 månader sedan
förälder
incheckning
6c93fb5755

+ 1 - 1
Cargo.toml

@@ -71,7 +71,7 @@ forge-fmt = { path = "fmt", optional = true }
 # We don't use ethers-core directly, but need the correct version for the
 # build to work.
 ethers-core = { version = "2.0.10", optional = true }
-soroban-sdk = { version = "22.0.0-rc.3.2", features = ["testutils"], optional = true }
+soroban-sdk = { version = "22.0.7", features = ["testutils"], optional = true }
 
 [dev-dependencies]
 num-derive = "0.4"

+ 12 - 0
integration/soroban/a.sol

@@ -0,0 +1,12 @@
+contract a {
+    function call_b (address b, address c) public returns (uint64) {
+        address addr = address(this);
+        // authorize contract c to be called, with function name "get_num" and "a" as an arg.
+        // get_num calls a.require_auth()
+        auth.authAsCurrContract(c, "get_num", addr);
+        bytes payload = abi.encode("increment", addr, c);
+        (bool suc, bytes returndata) = b.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+        return result;
+    }
+}

+ 13 - 0
integration/soroban/a_invalid.sol

@@ -0,0 +1,13 @@
+/// Same as a.sol, but without a call to auth.authAsCurrContract
+contract a_invalid {
+    function call_b (address b, address c) public returns (uint64) {
+        address addr = address(this);
+        // authorize contract c to be called, with function name "get_num" and "a" as an arg.
+        // get_num calls a.require_auth()
+        //auth.authAsCurrContract(c, "get_num", addr);
+        bytes payload = abi.encode("increment", addr, c);
+        (bool suc, bytes returndata) = b.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+        return result;
+    }
+}

+ 61 - 0
integration/soroban/auth_framework.spec.js

@@ -0,0 +1,61 @@
+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';
+import { assert } from 'console';
+
+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('Auth Framework', () => {
+  let keypair, a, b, c, a_invalid;
+
+  before(async () => {
+    console.log('Setting up cross contract tests...');
+
+    keypair = StellarSdk.Keypair.fromSecret(readFileSync('alice.txt', 'utf8').trim());
+    a = new StellarSdk.Contract(readContractAddress('a.txt'));
+    b = new StellarSdk.Contract(readContractAddress('b.txt'));
+    c = new StellarSdk.Contract(readContractAddress('c.txt'));
+    a_invalid = new StellarSdk.Contract(readContractAddress('a_invalid.txt'));
+  });
+
+  it('calls a', async () => {
+
+    
+    let values = [
+        b.address().toScVal(),
+        c.address().toScVal()
+    ];
+
+
+    let res = await call_contract_function("call_b", server, keypair, a, ...values);
+
+
+    expect(res.returnValue().value().toString()).to.equal("22");
+    
+  });
+
+  it ('call falis with invalid `a` contract', async () => {
+    
+    
+    let values = [
+        b.address().toScVal(),
+        c.address().toScVal()
+    ];
+
+    let res = await call_contract_function("call_b", server, keypair, a_invalid, ...values);
+
+    assert(res.toString().includes("recording authorization only] encountered authorization not tied to the root contract invocation for an address. Use `require_auth()` in the top invocation or enable non-root authorization."));
+
+  });
+
+
+});

+ 17 - 0
integration/soroban/b.sol

@@ -0,0 +1,17 @@
+contract b {
+ 
+    uint64 public instance counter = 20;
+
+    function increment(address a, address c) public returns (uint64) {
+
+        a.requireAuth();
+        bytes payload = abi.encode("get_num", a);
+        (bool suc, bytes returndata) = c.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+
+        counter = counter + result;
+
+        return counter;
+       
+    }
+} 

+ 6 - 0
integration/soroban/c.sol

@@ -0,0 +1,6 @@
+contract c {
+    function get_num(address a) public returns (uint64) {
+        a.requireAuth();
+        return 2;
+    }
+}

+ 4 - 3
src/codegen/encoding/mod.rs

@@ -11,7 +11,7 @@
 mod borsh_encoding;
 mod buffer_validator;
 pub(super) mod scale_encoding;
-mod soroban_encoding;
+pub mod soroban_encoding;
 
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::codegen::encoding::borsh_encoding::BorshEncoding;
@@ -41,7 +41,8 @@ pub(super) fn abi_encode(
     packed: bool,
 ) -> (Expression, Expression) {
     if ns.target == Target::Soroban {
-        return soroban_encode(loc, args, ns, vartab, cfg, packed);
+        let ret = soroban_encode(loc, args, ns, vartab, cfg, packed);
+        return (ret.0, ret.1);
     }
     let mut encoder = create_encoder(ns, packed);
     let size = calculate_size_args(&mut encoder, &args, ns, vartab, cfg);
@@ -1431,7 +1432,7 @@ pub(crate) trait AbiEncoding {
                 self.get_expr_size(arg_no, &loaded, ns, vartab, cfg)
             }
             Type::StorageRef(_, r) => {
-                let var = load_storage(&Codegen, r, expr.clone(), cfg, vartab, None);
+                let var = load_storage(&Codegen, r, expr.clone(), cfg, vartab, None, ns);
                 let size = self.get_expr_size(arg_no, &var, ns, vartab, cfg);
                 self.storage_cache_insert(arg_no, var.clone());
                 size

+ 192 - 43
src/codegen/encoding/soroban_encoding.rs

@@ -21,7 +21,7 @@ pub fn soroban_encode(
     vartab: &mut Vartable,
     cfg: &mut ControlFlowGraph,
     packed: bool,
-) -> (Expression, Expression) {
+) -> (Expression, Expression, Vec<Expression>) {
     let mut encoder = create_encoder(ns, packed);
 
     let size = 8 * args.len(); // 8 bytes per argument
@@ -61,9 +61,9 @@ pub fn soroban_encode(
         var_no: encoded_bytes,
     };
 
-    for (arg_no, item) in args.iter().enumerate() {
-        println!("item {:?}", item);
+    let mut encoded_items = Vec::new();
 
+    for (arg_no, item) in args.iter().enumerate() {
         let obj = vartab.temp_name(format!("obj_{arg_no}").as_str(), &Type::Uint(64));
 
         let transformer = match item.ty() {
@@ -72,10 +72,16 @@ pub fn soroban_encode(
                     pointer: Box::new(item.clone()),
                 };
 
+                let inp_extend = Expression::ZeroExt {
+                    loc: Loc::Codegen,
+                    ty: Type::Uint(64),
+                    expr: Box::new(inp),
+                };
+
                 let encoded = Expression::ShiftLeft {
                     loc: Loc::Codegen,
                     ty: Uint(64),
-                    left: Box::new(inp),
+                    left: Box::new(inp_extend),
                     right: Box::new(Expression::NumberLiteral {
                         loc: Loc::Codegen,
                         ty: Type::Uint(64),
@@ -95,31 +101,62 @@ pub fn soroban_encode(
                     }),
                 };
 
-                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 {
+                let len = match item {
+                    Expression::AllocDynamicBytes { size, .. } => {
+                        let sesa = Expression::ShiftLeft {
                             loc: Loc::Codegen,
-                            ty: Type::Uint(64),
-                            value: BigInt::from(32),
-                        }),
-                    };
+                            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 {
+                        Expression::Add {
                             loc: Loc::Codegen,
                             ty: Type::Uint(64),
-                            value: BigInt::from(4),
-                        }),
+                            overflowing: true,
+                            left: Box::new(sesa),
+                            right: Box::new(Expression::NumberLiteral {
+                                loc: Loc::Codegen,
+                                ty: Type::Uint(64),
+                                value: BigInt::from(4),
+                            }),
+                        }
                     }
-                } else {
-                    unreachable!()
+                    Expression::BytesLiteral { loc, ty: _, value } => {
+                        let len = Expression::NumberLiteral {
+                            loc: *loc,
+                            ty: Type::Uint(64),
+                            value: BigInt::from(value.len() as u64),
+                        };
+
+                        let len = Expression::ShiftLeft {
+                            loc: *loc,
+                            ty: Type::Uint(64),
+                            left: Box::new(len),
+                            right: Box::new(Expression::NumberLiteral {
+                                loc: *loc,
+                                ty: Type::Uint(64),
+                                value: BigInt::from(32),
+                            }),
+                        };
+
+                        Expression::Add {
+                            loc: *loc,
+                            ty: Type::Uint(64),
+                            left: Box::new(len),
+                            right: Box::new(Expression::NumberLiteral {
+                                loc: *loc,
+                                ty: Type::Uint(64),
+                                value: BigInt::from(4),
+                            }),
+                            overflowing: false,
+                        }
+                    }
+                    _ => unreachable!(),
                 };
 
                 Instr::Call {
@@ -162,12 +199,114 @@ pub fn soroban_encode(
                 }
             }
             Type::Address(_) => {
-                // pass the address as is
-                Instr::Set {
-                    loc: *loc,
-                    res: obj,
-                    expr: item.clone(),
-                }
+                let instr = if let Expression::Cast { loc, ty: _, expr } = item {
+                    let address_literal = expr;
+
+                    let pointer = Expression::VectorData {
+                        pointer: address_literal.clone(),
+                    };
+
+                    let pointer_extend = Expression::ZeroExt {
+                        loc: *loc,
+                        ty: Type::Uint(64),
+                        expr: Box::new(pointer),
+                    };
+
+                    let encoded = Expression::ShiftLeft {
+                        loc: *loc,
+                        ty: Uint(64),
+                        left: Box::new(pointer_extend),
+                        right: Box::new(Expression::NumberLiteral {
+                            loc: *loc,
+                            ty: Type::Uint(64),
+                            value: BigInt::from(32),
+                        }),
+                    };
+
+                    let encoded = Expression::Add {
+                        loc: *loc,
+                        ty: Type::Uint(64),
+                        overflowing: true,
+                        left: Box::new(encoded),
+                        right: Box::new(Expression::NumberLiteral {
+                            loc: *loc,
+                            ty: Type::Uint(64),
+                            value: BigInt::from(4),
+                        }),
+                    };
+
+                    let len = if let Expression::BytesLiteral { loc, ty: _, value } =
+                        *address_literal.clone()
+                    {
+                        let len = Expression::NumberLiteral {
+                            loc,
+                            ty: Type::Uint(64),
+                            value: BigInt::from(value.len() as u64),
+                        };
+
+                        let len = Expression::ShiftLeft {
+                            loc,
+                            ty: Type::Uint(64),
+                            left: Box::new(len),
+                            right: Box::new(Expression::NumberLiteral {
+                                loc,
+                                ty: Type::Uint(64),
+                                value: BigInt::from(32),
+                            }),
+                        };
+
+                        Expression::Add {
+                            loc,
+                            ty: Type::Uint(64),
+                            left: Box::new(len),
+                            right: Box::new(Expression::NumberLiteral {
+                                loc,
+                                ty: Type::Uint(64),
+                                value: BigInt::from(4),
+                            }),
+                            overflowing: false,
+                        }
+                    } else {
+                        todo!()
+                    };
+
+                    let str_key_temp = vartab.temp_name("str_key", &Type::Uint(64));
+                    let str_key_var = Expression::Variable {
+                        loc: *loc,
+                        ty: Type::Uint(64),
+                        var_no: str_key_temp,
+                    };
+
+                    let soroban_str_key = Instr::Call {
+                        res: vec![str_key_temp],
+                        return_tys: vec![Type::Uint(64)],
+                        call: crate::codegen::cfg::InternalCallTy::HostFunction {
+                            name: HostFunctions::StringNewFromLinearMemory.name().to_string(),
+                        },
+                        args: vec![encoded.clone(), len.clone()],
+                    };
+
+                    cfg.add(vartab, soroban_str_key);
+
+                    let address_object = Instr::Call {
+                        res: vec![obj],
+                        return_tys: vec![Type::Uint(64)],
+                        call: crate::codegen::cfg::InternalCallTy::HostFunction {
+                            name: HostFunctions::StrKeyToAddr.name().to_string(),
+                        },
+                        args: vec![str_key_var],
+                    };
+
+                    address_object
+                } else {
+                    Instr::Set {
+                        loc: *loc,
+                        res: obj,
+                        expr: item.clone(),
+                    }
+                };
+
+                instr
             }
             // FIXME: Implement encoding/decoding for i128
             Type::Int(128) => Instr::Set {
@@ -184,6 +323,8 @@ pub fn soroban_encode(
             var_no: obj,
         };
 
+        encoded_items.push(var.clone());
+
         cfg.add(vartab, transformer);
 
         let advance = encoder.encode(&var, &buffer, &offset, arg_no, ns, vartab, cfg);
@@ -196,11 +337,11 @@ pub fn soroban_encode(
         };
     }
 
-    (buffer, size_expr)
+    (buffer, size_expr, encoded_items)
 }
 
 pub fn soroban_decode(
-    loc: &Loc,
+    _loc: &Loc,
     buffer: &Expression,
     _types: &[Type],
     _ns: &Namespace,
@@ -216,19 +357,27 @@ pub fn soroban_decode(
         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,
-    };
+    let decoded_val = soroban_decode_arg(loaded_val);
 
     returns.push(decoded_val);
 
     returns
 }
+
+pub fn soroban_decode_arg(item: Expression) -> Expression {
+    match item.ty() {
+        Type::Uint(64) => Expression::ShiftRight {
+            loc: Loc::Codegen,
+            ty: Type::Uint(64),
+            left: Box::new(item.clone()),
+            right: Box::new(Expression::NumberLiteral {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                value: BigInt::from(8),
+            }),
+            signed: false,
+        },
+        Type::Address(_) => item,
+        _ => todo!(),
+    }
+}

+ 539 - 23
src/codegen/expression.rs

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use super::encoding::{abi_decode, abi_encode};
+use super::encoding::soroban_encoding::soroban_decode_arg;
+use super::encoding::{abi_decode, abi_encode, soroban_encoding::soroban_encode};
 use super::revert::{
     assert_failure, expr_assert, log_runtime_error, require, PanicCode, SolidityError,
 };
@@ -16,7 +17,7 @@ use crate::codegen::array_boundary::handle_array_assign;
 use crate::codegen::constructor::call_constructor;
 use crate::codegen::events::new_event_emitter;
 use crate::codegen::unused_variable::should_remove_assignment;
-use crate::codegen::{Builtin, Expression};
+use crate::codegen::{Builtin, Expression, HostFunctions};
 use crate::sema::ast::ExternalCallAccounts;
 use crate::sema::{
     ast,
@@ -62,7 +63,7 @@ pub fn expression(
             let storage_type = storage_type(expr, ns);
             let storage = expression(expr, cfg, contract_no, func, ns, vartab, opt);
 
-            load_storage(loc, ty, storage, cfg, vartab, storage_type)
+            load_storage(loc, ty, storage, cfg, vartab, storage_type, ns)
         }
         ast::Expression::Add {
             loc,
@@ -545,7 +546,7 @@ pub fn expression(
                                 elem_ty: elem_ty.clone(),
                             }
                         } else {
-                            load_storage(loc, &ns.storage_type(), array, cfg, vartab, None)
+                            load_storage(loc, &ns.storage_type(), array, cfg, vartab, None, ns)
                         }
                     }
                     ArrayLength::Fixed(length) => {
@@ -1250,6 +1251,7 @@ fn post_incdec(
             cfg,
             vartab,
             storage_type.clone(),
+            ns,
         ),
         _ => v,
     };
@@ -1316,14 +1318,22 @@ fn post_incdec(
 
             match var.ty() {
                 Type::StorageRef(..) => {
+                    let mut value = Expression::Variable {
+                        loc: *loc,
+                        ty: ty.clone(),
+                        var_no: res,
+                    };
+                    // If the target is Soroban, encode the value before storing it in storage.
+                    if ns.target == Target::Soroban {
+                        value = soroban_encode(&value.loc(), vec![value], ns, vartab, cfg, false).2
+                            [0]
+                        .clone();
+                    }
+
                     cfg.add(
                         vartab,
                         Instr::SetStorage {
-                            value: Expression::Variable {
-                                loc: *loc,
-                                ty: ty.clone(),
-                                var_no: res,
-                            },
+                            value,
                             ty: ty.clone(),
                             storage: dest,
                             storage_type,
@@ -1383,6 +1393,7 @@ fn pre_incdec(
             cfg,
             vartab,
             storage_type.clone(),
+            ns,
         ),
         _ => v,
     };
@@ -1437,14 +1448,22 @@ fn pre_incdec(
 
             match var.ty() {
                 Type::StorageRef(..) => {
+                    let mut value = Expression::Variable {
+                        loc: *loc,
+                        ty: ty.clone(),
+                        var_no: res,
+                    };
+
+                    if ns.target == Target::Soroban {
+                        value = soroban_encode(&value.loc(), vec![value], ns, vartab, cfg, false).2
+                            [0]
+                        .clone();
+                    }
+
                     cfg.add(
                         vartab,
                         Instr::SetStorage {
-                            value: Expression::Variable {
-                                loc: *loc,
-                                ty: ty.clone(),
-                                var_no: res,
-                            },
+                            value,
                             ty: ty.clone(),
                             storage: dest,
                             storage_type: storage_type.clone(),
@@ -2215,6 +2234,29 @@ fn expr_builtin(
                 };
             }
 
+            // In soroban, address is retrieved via a host function call
+            if ns.target == Target::Soroban {
+                let address_var_no = vartab.temp_anonymous(&Type::Uint(64));
+                let address_var = Expression::Variable {
+                    loc: *loc,
+                    ty: Type::Address(false),
+                    var_no: address_var_no,
+                };
+
+                let retrieve_address = Instr::Call {
+                    res: vec![address_var_no],
+                    return_tys: vec![Type::Uint(64)],
+                    call: InternalCallTy::HostFunction {
+                        name: HostFunctions::GetCurrentContractAddress.name().to_string(),
+                    },
+                    args: vec![],
+                };
+
+                cfg.add(vartab, retrieve_address);
+
+                return address_var;
+            }
+
             // In emit, GetAddress returns a pointer to the address
             let codegen_expr = Expression::Builtin {
                 loc: *loc,
@@ -2328,6 +2370,458 @@ fn expr_builtin(
 
             code(loc, *contract_no, ns, opt)
         }
+        ast::Builtin::RequireAuth => {
+            let var_temp = vartab.temp(
+                &pt::Identifier {
+                    name: "auth".to_owned(),
+                    loc: *loc,
+                },
+                &Type::Bool,
+            );
+
+            let var = Expression::Variable {
+                loc: *loc,
+                ty: Type::Address(false),
+                var_no: var_temp,
+            };
+            let expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
+
+            let expr = if let Type::StorageRef(_, _) = args[0].ty() {
+                let expr_no = vartab.temp_anonymous(&Type::Address(false));
+                let expr = Expression::Variable {
+                    loc: Loc::Codegen,
+                    ty: Type::Address(false),
+                    var_no: expr_no,
+                };
+
+                let storage_load = Instr::LoadStorage {
+                    res: expr_no,
+                    ty: Type::Address(false),
+                    storage: expr.clone(),
+                    storage_type: None,
+                };
+
+                cfg.add(vartab, storage_load);
+
+                expr
+            } else {
+                expr
+            };
+
+            let instr = Instr::Call {
+                res: vec![var_temp],
+                return_tys: vec![Type::Void],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::RequireAuth.name().to_string(),
+                },
+                args: vec![expr],
+            };
+
+            cfg.add(vartab, instr);
+
+            var
+        }
+
+        // This is the trickiest host function to implement. The reason is takes `InvokerContractAuthEntry` enum as an argument.
+        // let x = SubContractInvocation {
+        //     context: ContractContext {
+        //         contract: c.clone(),
+        //         fn_name: symbol_short!("increment"),
+        //          args: vec![&env, current_contract.into_val(&env)],
+        //     },
+        //     sub_invocations: vec![&env],
+        //  };
+        //  let auth_context = auth::InvokerContractAuthEntry::Contract(x);
+        // Most of the logic done here is just to encode the above struct as the host expects it.
+        // FIXME: This uses a series of MapNewFromLinearMemory, and multiple inserts to create the struct.
+        // This is not efficient and should be optimized.
+        // Instead, we should use MapNewFromLinearMemory to create the struct in one go.
+        ast::Builtin::AuthAsCurrContract => {
+            let symbol_key_1 = Expression::BytesLiteral {
+                loc: Loc::Codegen,
+                ty: Type::String,
+                value: "contract".as_bytes().to_vec(),
+            };
+            let symbol_key_2 = Expression::BytesLiteral {
+                loc: Loc::Codegen,
+                ty: Type::String,
+                value: "fn_name".as_bytes().to_vec(),
+            };
+            let symbol_key_3 = Expression::BytesLiteral {
+                loc: Loc::Codegen,
+                ty: Type::String,
+                value: "args".as_bytes().to_vec(),
+            };
+
+            let symbols = soroban_encode(
+                loc,
+                vec![symbol_key_1, symbol_key_2, symbol_key_3],
+                ns,
+                vartab,
+                cfg,
+                false,
+            )
+            .2;
+
+            let contract_value = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
+            let fn_name_symbol = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
+
+            let symbol_string =
+                if let Expression::BytesLiteral { loc, ty: _, value } = fn_name_symbol {
+                    Expression::BytesLiteral {
+                        loc,
+                        ty: Type::String,
+                        value,
+                    }
+                } else {
+                    unreachable!()
+                };
+            let encode_func_symbol =
+                soroban_encode(loc, vec![symbol_string], ns, vartab, cfg, false).2[0].clone();
+
+            ///////////////////////////////////PREPARE ARGS FOR CONTEXT MAP////////////////////////////////////
+
+            let mut args_vec = Vec::new();
+            for arg in args.iter().skip(2) {
+                let arg = expression(arg, cfg, contract_no, func, ns, vartab, opt);
+                args_vec.push(arg);
+            }
+
+            let args_encoded = abi_encode(loc, args_vec.clone(), ns, vartab, cfg, false);
+
+            let args_buf = args_encoded.0;
+
+            let args_buf_ptr = Expression::VectorData {
+                pointer: Box::new(args_buf.clone()),
+            };
+
+            let args_buf_extended = Expression::ZeroExt {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                expr: Box::new(args_buf_ptr.clone()),
+            };
+
+            let args_buf_shifted = Expression::ShiftLeft {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                left: Box::new(args_buf_extended.clone()),
+                right: Box::new(Expression::NumberLiteral {
+                    loc: Loc::Codegen,
+                    ty: Type::Uint(64),
+                    value: BigInt::from(32),
+                }),
+            };
+
+            let args_buf_pos = Expression::Add {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                left: Box::new(args_buf_shifted.clone()),
+                right: Box::new(Expression::NumberLiteral {
+                    loc: Loc::Codegen,
+                    ty: Type::Uint(64),
+                    value: BigInt::from(4),
+                }),
+                overflowing: false,
+            };
+
+            let args_len = Expression::NumberLiteral {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                value: BigInt::from(args_vec.len()),
+            };
+            let args_len_encoded = Expression::ShiftLeft {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                left: Box::new(args_len.clone()),
+                right: Box::new(Expression::NumberLiteral {
+                    loc: Loc::Codegen,
+                    ty: Type::Uint(64),
+                    value: BigInt::from(32),
+                }),
+            };
+            let args_len_encoded = Expression::Add {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                left: Box::new(args_len_encoded.clone()),
+                right: Box::new(Expression::NumberLiteral {
+                    loc: Loc::Codegen,
+                    ty: Type::Uint(64),
+                    value: BigInt::from(4),
+                }),
+                overflowing: false,
+            };
+
+            let args_vec_var_no = vartab.temp_anonymous(&Type::Uint(64));
+            let args_vec_var = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: args_vec_var_no,
+            };
+
+            let vec_new_from_linear_mem = Instr::Call {
+                res: vec![args_vec_var_no],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VectorNewFromLinearMemory.name().to_string(),
+                },
+                args: vec![args_buf_pos.clone(), args_len_encoded],
+            };
+
+            cfg.add(vartab, vec_new_from_linear_mem);
+
+            let context_map = vartab.temp_anonymous(&Type::Uint(64));
+            let context_map_var = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: context_map,
+            };
+
+            let context_map_new = Instr::Call {
+                res: vec![context_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapNew.name().to_string(),
+                },
+                args: vec![],
+            };
+
+            cfg.add(vartab, context_map_new);
+
+            let context_map_put = Instr::Call {
+                res: vec![context_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapPut.name().to_string(),
+                },
+                args: vec![context_map_var.clone(), symbols[0].clone(), contract_value],
+            };
+
+            cfg.add(vartab, context_map_put);
+
+            let context_map_put_2 = Instr::Call {
+                res: vec![context_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapPut.name().to_string(),
+                },
+                args: vec![
+                    context_map_var.clone(),
+                    symbols[1].clone(),
+                    encode_func_symbol,
+                ],
+            };
+
+            cfg.add(vartab, context_map_put_2);
+
+            let context_map_put_3 = Instr::Call {
+                res: vec![context_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapPut.name().to_string(),
+                },
+                args: vec![
+                    context_map_var.clone(),
+                    symbols[2].clone(),
+                    args_vec_var.clone(),
+                ],
+            };
+
+            cfg.add(vartab, context_map_put_3);
+
+            ///////////////////////////////////////////////////////////////////////////////////
+
+            // Now forming "sub invocations" map
+            // FIXME: This should eventually be fixed to take other sub_invocations as arguments. For now, it is hardcoded to take an empty vector.
+
+            let key_1 = Expression::BytesLiteral {
+                loc: Loc::Codegen,
+                ty: Type::String,
+                value: "context".as_bytes().to_vec(),
+            };
+
+            let key_2 = Expression::BytesLiteral {
+                loc: Loc::Codegen,
+                ty: Type::String,
+                value: "sub_invocations".as_bytes().to_vec(),
+            };
+
+            let keys = soroban_encode(loc, vec![key_1, key_2], ns, vartab, cfg, false).2;
+
+            let sub_invocations_map = vartab.temp_anonymous(&Type::Uint(64));
+            let sub_invocations_map_var = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: sub_invocations_map,
+            };
+
+            let sub_invocations_map_new = Instr::Call {
+                res: vec![sub_invocations_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapNew.name().to_string(),
+                },
+                args: vec![],
+            };
+
+            cfg.add(vartab, sub_invocations_map_new);
+
+            let sub_invocations_map_put = Instr::Call {
+                res: vec![sub_invocations_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapPut.name().to_string(),
+                },
+                args: vec![
+                    sub_invocations_map_var.clone(),
+                    keys[0].clone(),
+                    context_map_var,
+                ],
+            };
+
+            cfg.add(vartab, sub_invocations_map_put);
+
+            let empy_vec_var = vartab.temp_anonymous(&Type::Uint(64));
+            let empty_vec_expr = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: empy_vec_var,
+            };
+            let empty_vec = Instr::Call {
+                res: vec![empy_vec_var],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VectorNew.name().to_string(),
+                },
+                args: vec![],
+            };
+
+            cfg.add(vartab, empty_vec);
+
+            let sub_invocations_map_put_2 = Instr::Call {
+                res: vec![sub_invocations_map],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::MapPut.name().to_string(),
+                },
+                args: vec![
+                    sub_invocations_map_var.clone(),
+                    keys[1].clone(),
+                    empty_vec_expr,
+                ],
+            };
+
+            cfg.add(vartab, sub_invocations_map_put_2);
+
+            ///////////////////////////////////////////////////////////////////////////////////
+
+            // now forming the enum. The enum is a VecObject[Symbol("Contract"), sub invokations map].
+            // FIXME: This should use VecNewFromLinearMemory to create the enum in one go.
+
+            let contract_capitalized = Expression::BytesLiteral {
+                loc: Loc::Codegen,
+                ty: Type::String,
+                value: "Contract".as_bytes().to_vec(),
+            };
+
+            let contract_capitalized =
+                soroban_encode(loc, vec![contract_capitalized], ns, vartab, cfg, false).2[0]
+                    .clone();
+
+            let enum_vec = vartab.temp_anonymous(&Type::Uint(64));
+            let enum_vec_var = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: enum_vec,
+            };
+
+            let enum_vec_new = Instr::Call {
+                res: vec![enum_vec],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VectorNew.name().to_string(),
+                },
+                args: vec![],
+            };
+
+            cfg.add(vartab, enum_vec_new);
+
+            let enum_vec_put = Instr::Call {
+                res: vec![enum_vec],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VecPushBack.name().to_string(),
+                },
+                args: vec![enum_vec_var.clone(), contract_capitalized],
+            };
+
+            cfg.add(vartab, enum_vec_put);
+
+            let enum_vec_put_2 = Instr::Call {
+                res: vec![enum_vec],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VecPushBack.name().to_string(),
+                },
+                args: vec![enum_vec_var.clone(), sub_invocations_map_var],
+            };
+
+            cfg.add(vartab, enum_vec_put_2);
+
+            ///////////////////////////////////////////////////////////////////////////////////
+            // now put the enum into a vec
+
+            let vec = vartab.temp_anonymous(&Type::Uint(64));
+            let vec_var = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: vec,
+            };
+
+            let vec_new = Instr::Call {
+                res: vec![vec],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VectorNew.name().to_string(),
+                },
+                args: vec![],
+            };
+
+            cfg.add(vartab, vec_new);
+
+            let vec_push_back = Instr::Call {
+                res: vec![vec],
+                return_tys: vec![Type::Uint(64)],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::VecPushBack.name().to_string(),
+                },
+                args: vec![vec_var.clone(), enum_vec_var],
+            };
+
+            cfg.add(vartab, vec_push_back);
+
+            ///////////////////////////////////////////////////////////////////////////////////
+            // now for the moment of truth - the call to the host function auth_as_curr_contract
+
+            let call_res = vartab.temp_anonymous(&Type::Uint(64));
+            let call_res_var = Expression::Variable {
+                loc: Loc::Codegen,
+                ty: Type::Uint(64),
+                var_no: call_res,
+            };
+
+            let auth_call = Instr::Call {
+                res: vec![call_res],
+                return_tys: vec![Type::Void],
+                call: InternalCallTy::HostFunction {
+                    name: HostFunctions::AuthAsCurrContract.name().to_string(),
+                },
+                args: vec![vec_var],
+            };
+
+            cfg.add(vartab, auth_call);
+
+            call_res_var
+        }
         ast::Builtin::ExtendTtl => {
             let mut arguments: Vec<Expression> = args
                 .iter()
@@ -2762,14 +3256,22 @@ pub fn assign_single(
                     }
                 }
                 Type::StorageRef(..) => {
+                    let mut value = Expression::Variable {
+                        loc: left.loc(),
+                        ty: ty.clone(),
+                        var_no: pos,
+                    };
+
+                    if ns.target == Target::Soroban {
+                        value = soroban_encode(&left.loc(), vec![value], ns, vartab, cfg, false).2
+                            [0]
+                        .clone();
+                    }
+
                     cfg.add(
                         vartab,
                         Instr::SetStorage {
-                            value: Expression::Variable {
-                                loc: left.loc(),
-                                ty: ty.clone(),
-                                var_no: pos,
-                            },
+                            value,
                             ty: ty.deref_any().clone(),
                             storage: dest,
                             storage_type,
@@ -3337,8 +3839,15 @@ fn array_subscript(
                         }
                     } else {
                         // TODO(Soroban): Storage type here is None, since arrays are not yet supported in Soroban
-                        let array_length =
-                            load_storage(loc, &Type::Uint(256), array.clone(), cfg, vartab, None);
+                        let array_length = load_storage(
+                            loc,
+                            &Type::Uint(256),
+                            array.clone(),
+                            cfg,
+                            vartab,
+                            None,
+                            ns,
+                        );
 
                         array = Expression::Keccak256 {
                             loc: *loc,
@@ -3686,6 +4195,7 @@ pub fn load_storage(
     cfg: &mut ControlFlowGraph,
     vartab: &mut Vartable,
     storage_type: Option<pt::StorageType>,
+    ns: &Namespace,
 ) -> Expression {
     let res = vartab.temp_anonymous(ty);
 
@@ -3695,14 +4205,20 @@ pub fn load_storage(
             res,
             ty: ty.clone(),
             storage,
-            storage_type,
+            storage_type: storage_type.clone(),
         },
     );
 
-    Expression::Variable {
+    let var = Expression::Variable {
         loc: *loc,
         ty: ty.clone(),
         var_no: res,
+    };
+
+    if ns.target == Target::Soroban {
+        soroban_decode_arg(var)
+    } else {
+        var
     }
 }
 

+ 30 - 1
src/codegen/mod.rs

@@ -46,6 +46,7 @@ use crate::sema::eval::eval_const_number;
 use crate::sema::Recurse;
 #[cfg(feature = "wasm_opt")]
 use contract_build::OptimizationPasses;
+use encoding::soroban_encoding::soroban_encode;
 use num_bigint::{BigInt, Sign};
 use num_rational::BigRational;
 use num_traits::{FromPrimitive, Zero};
@@ -103,9 +104,18 @@ pub enum HostFunctions {
     SymbolNewFromLinearMemory,
     VectorNew,
     VectorNewFromLinearMemory,
+    MapNewFromLinearMemory,
     Call,
     ObjToU64,
     ObjFromU64,
+    RequireAuth,
+    AuthAsCurrContract,
+    MapNew,
+    MapPut,
+    VecPushBack,
+    StringNewFromLinearMemory,
+    StrKeyToAddr,
+    GetCurrentContractAddress,
 }
 
 impl HostFunctions {
@@ -122,6 +132,15 @@ impl HostFunctions {
             HostFunctions::Call => "d._",
             HostFunctions::ObjToU64 => "i.0",
             HostFunctions::ObjFromU64 => "i._",
+            HostFunctions::RequireAuth => "a.0",
+            HostFunctions::AuthAsCurrContract => "a.3",
+            HostFunctions::MapNewFromLinearMemory => "m.9",
+            HostFunctions::MapNew => "m._",
+            HostFunctions::MapPut => "m.0",
+            HostFunctions::VecPushBack => "v.6",
+            HostFunctions::StringNewFromLinearMemory => "b.i",
+            HostFunctions::StrKeyToAddr => "a.1",
+            HostFunctions::GetCurrentContractAddress => "x.7",
         }
     }
 }
@@ -308,7 +327,13 @@ fn storage_initializer(contract_no: usize, ns: &mut Namespace, opt: &Options) ->
                 None,
             );
 
-            let value = expression(init, &mut cfg, contract_no, None, ns, &mut vartab, opt);
+            let mut value = expression(init, &mut cfg, contract_no, None, ns, &mut vartab, opt);
+
+            if ns.target == Target::Soroban {
+                value = soroban_encode(&value.loc(), vec![value], ns, &mut vartab, &mut cfg, false)
+                    .2[0]
+                    .clone();
+            }
 
             cfg.add(
                 &mut vartab,
@@ -1798,6 +1823,8 @@ pub enum Builtin {
     WriteUint256LE,
     WriteBytes,
     Concat,
+    RequireAuth,
+    AuthAsCurrContract,
     ExtendTtl,
     ExtendInstanceTtl,
 }
@@ -1862,6 +1889,8 @@ impl From<&ast::Builtin> for Builtin {
             ast::Builtin::PrevRandao => Builtin::PrevRandao,
             ast::Builtin::ContractCode => Builtin::ContractCode,
             ast::Builtin::StringConcat | ast::Builtin::BytesConcat => Builtin::Concat,
+            ast::Builtin::RequireAuth => Builtin::RequireAuth,
+            ast::Builtin::AuthAsCurrContract => Builtin::AuthAsCurrContract,
             ast::Builtin::ExtendTtl => Builtin::ExtendTtl,
             ast::Builtin::ExtendInstanceTtl => Builtin::ExtendInstanceTtl,
             _ => panic!("Builtin should not be in the cfg"),

+ 3 - 2
src/codegen/storage.rs

@@ -99,7 +99,7 @@ pub fn storage_slots_array_push(
     let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
 
     // TODO(Soroban): Storage type here is None, since arrays are not yet supported in Soroban
-    let expr = load_storage(loc, &slot_ty, var_expr.clone(), cfg, vartab, None);
+    let expr = load_storage(loc, &slot_ty, var_expr.clone(), cfg, vartab, None, ns);
 
     cfg.add(
         vartab,
@@ -213,7 +213,7 @@ pub fn storage_slots_array_pop(
     let ty = args[0].ty();
     let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
     // TODO(Soroban): Storage type here is None, since arrays are not yet supported in Soroban
-    let expr = load_storage(loc, &length_ty, var_expr.clone(), cfg, vartab, None);
+    let expr = load_storage(loc, &length_ty, var_expr.clone(), cfg, vartab, None, ns);
 
     cfg.add(
         vartab,
@@ -328,6 +328,7 @@ pub fn storage_slots_array_pop(
             cfg,
             vartab,
             None,
+            ns,
         );
 
         cfg.add(

+ 2 - 2
src/emit/expression.rs

@@ -128,7 +128,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
         }
         Expression::BytesLiteral { value: bs, ty, .. } => {
             // If the type of a BytesLiteral is a String, embedd the bytes in the binary.
-            if ty == &Type::String {
+            if ty == &Type::String || ty == &Type::Address(true) {
                 let data = bin.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.
@@ -2154,7 +2154,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
             let data = bin.vector_bytes(ptr);
             let res = bin
                 .builder
-                .build_ptr_to_int(data, bin.context.i64_type(), "sesa");
+                .build_ptr_to_int(data, bin.context.i32_type(), "ptr_as_int32");
 
             res.unwrap().into()
         }

+ 36 - 0
src/emit/soroban/mod.rs

@@ -71,6 +71,33 @@ impl HostFunctions {
                 .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),
+            HostFunctions::RequireAuth => bin.context.i64_type().fn_type(&[ty.into()], false),
+            HostFunctions::AuthAsCurrContract => {
+                bin.context.i64_type().fn_type(&[ty.into()], false)
+            }
+            HostFunctions::MapNewFromLinearMemory => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into(), ty.into()], false),
+
+            HostFunctions::MapNew => bin.context.i64_type().fn_type(&[], false),
+
+            HostFunctions::MapPut => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into(), ty.into()], false),
+
+            HostFunctions::VecPushBack => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into()], false),
+
+            HostFunctions::StringNewFromLinearMemory => bin
+                .context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into()], false),
+            HostFunctions::StrKeyToAddr => bin.context.i64_type().fn_type(&[ty.into()], false),
+            HostFunctions::GetCurrentContractAddress => bin.context.i64_type().fn_type(&[], false),
         }
     }
 }
@@ -302,6 +329,15 @@ impl SorobanTarget {
             HostFunctions::ObjToU64,
             HostFunctions::ObjFromU64,
             HostFunctions::PutContractData,
+            HostFunctions::RequireAuth,
+            HostFunctions::AuthAsCurrContract,
+            HostFunctions::MapNewFromLinearMemory,
+            HostFunctions::MapNew,
+            HostFunctions::MapPut,
+            HostFunctions::VecPushBack,
+            HostFunctions::StringNewFromLinearMemory,
+            HostFunctions::StrKeyToAddr,
+            HostFunctions::GetCurrentContractAddress,
         ];
 
         for func in &host_functions {

+ 2 - 0
src/sema/ast.rs

@@ -1779,6 +1779,8 @@ pub enum Builtin {
     TypeInterfaceId,
     TypeRuntimeCode,
     TypeCreatorCode,
+    RequireAuth,
+    AuthAsCurrContract,
     ExtendTtl,
     ExtendInstanceTtl,
 }

+ 41 - 2
src/sema/builtin.rs

@@ -36,7 +36,7 @@ pub struct Prototype {
 }
 
 // A list of all Solidity builtins functions
-pub static BUILTIN_FUNCTIONS: Lazy<[Prototype; 28]> = Lazy::new(|| {
+pub static BUILTIN_FUNCTIONS: Lazy<[Prototype; 29]> = Lazy::new(|| {
     [
         Prototype {
             builtin: Builtin::ExtendInstanceTtl,
@@ -358,6 +358,17 @@ pub static BUILTIN_FUNCTIONS: Lazy<[Prototype; 28]> = Lazy::new(|| {
             doc: "Concatenate bytes",
             constant: true,
         },
+        Prototype {
+            builtin: Builtin::AuthAsCurrContract,
+            namespace: Some("auth"),
+            method: vec![],
+            name: "authAsCurrContract",
+            params: vec![],
+            ret: vec![],
+            target: vec![Target::Soroban],
+            doc: "Authorizes sub-contract calls for the next contract call on behalf of the current contract.",
+            constant: false,
+        },
     ]
 });
 
@@ -558,7 +569,7 @@ pub static BUILTIN_VARIABLE: Lazy<[Prototype; 17]> = Lazy::new(|| {
 });
 
 // A list of all Solidity builtins methods
-pub static BUILTIN_METHODS: Lazy<[Prototype; 28]> = Lazy::new(|| {
+pub static BUILTIN_METHODS: Lazy<[Prototype; 29]> = Lazy::new(|| {
     [
         Prototype {
             builtin: Builtin::ExtendTtl,
@@ -869,6 +880,17 @@ pub static BUILTIN_METHODS: Lazy<[Prototype; 28]> = Lazy::new(|| {
             doc: "Write the contents of a bytes array (without its length) to the specified offset",
             constant: false,
         },
+        Prototype {
+            builtin: Builtin::RequireAuth,
+            namespace: None,
+            method: vec![Type::Address(false), Type::StorageRef(false, Box::new(Type::Address(false)))],
+            name: "requireAuth",
+            params: vec![],
+            ret: vec![],
+            target: vec![Target::Soroban],
+            doc: "Checks if the address has authorized the invocation of the current contract function with all the arguments of the invocation. Traps if the invocation hasn't been authorized.",
+            constant: false,
+        },
     ]
 });
 
@@ -1110,6 +1132,23 @@ pub(super) fn resolve_namespace_call(
         });
     }
 
+    if name == "authAsCurrContract" {
+        let mut resolved_args = Vec::new();
+
+        for arg in args {
+            let expr = expression(arg, context, ns, symtable, diagnostics, ResolveTo::Unknown)?;
+
+            resolved_args.push(expr);
+        }
+
+        return Ok(Expression::Builtin {
+            loc: *loc,
+            tys: Vec::new(),
+            kind: Builtin::AuthAsCurrContract,
+            args: resolved_args,
+        });
+    }
+
     // The abi.* functions need special handling, others do not
     if namespace != "abi" && namespace != "string" {
         return resolve_call(

+ 6 - 0
src/sema/expression/literals.rs

@@ -302,6 +302,12 @@ pub(super) fn address_literal(
                 Err(())
             }
         }
+    } else if ns.target == Target::Soroban {
+        Ok(Expression::BytesLiteral {
+            loc: *loc,
+            ty: Type::Address(true),
+            value: address.to_string().into_bytes(),
+        })
     } else {
         diagnostics.push(Diagnostic::error(
             *loc,

+ 146 - 0
tests/soroban_testcases/auth.rs

@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::build_solidity;
+use soroban_sdk::{
+    symbol_short,
+    testutils::{AuthorizedFunction, AuthorizedInvocation},
+    Address, IntoVal, Val,
+};
+
+#[test]
+fn require_auth() {
+    let runtime = build_solidity(
+        r#"contract auth {
+
+    address public owner = address"GDRIX624OGPQEX264NY72UKOJQUASHU3PYKL6DDPGSTWXWJSBOTR6N7W";
+
+ 
+    uint64 public instance counter = 20;
+
+    function increment() public returns (uint64) {
+
+        owner.requireAuth();
+
+        counter = counter + 1;
+
+        return counter;
+       
+    }
+} "#,
+        |_| {},
+    );
+
+    runtime.env.mock_all_auths();
+
+    let authed_addr = Address::from_str(
+        &runtime.env,
+        "GDRIX624OGPQEX264NY72UKOJQUASHU3PYKL6DDPGSTWXWJSBOTR6N7W",
+    );
+
+    let addr = runtime.contracts.first().unwrap();
+    let res = runtime.invoke_contract(addr, "increment", vec![]);
+    let expected: Val = 21_u64.into_val(&runtime.env);
+
+    assert!(expected.shallow_eq(&res));
+
+    let auths = runtime.env.auths();
+
+    let authed_invokation = AuthorizedInvocation {
+        function: AuthorizedFunction::Contract((
+            addr.clone(),
+            symbol_short!("increment"),
+            soroban_sdk::vec![&runtime.env],
+        )),
+        sub_invocations: vec![],
+    };
+
+    assert_eq!(auths, vec![(authed_addr.clone(), authed_invokation)]);
+}
+
+/// This is a demo of a deeper chain of cross contract calls.
+/// A -> B -> C. The auth_as_curr_contract only takes the C contract invokation
+/// Because the B call is authorized by default.
+/// A soroban example could be found at: https://github.com/stellar/soroban-examples/blob/main/deep_contract_auth/src/lib.rs
+#[test]
+fn auth_as_curr_contract() {
+    let mut runtime = build_solidity(
+        r#"contract a {
+    function call_b (address b, address c) public returns (uint64) {
+        address addr = address(this);
+        // authorize contract c to be called, with function name "get_num" and "a" as an arg.
+        // get_num calls a.require_auth()
+        auth.authAsCurrContract(c, "get_num", addr);
+        bytes payload = abi.encode("increment", addr, c);
+        (bool suc, bytes returndata) = b.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+        return result;
+    }
+}"#,
+        |_| {},
+    );
+
+    let b = runtime.deploy_contract(
+        r#"contract b {
+ 
+    uint64 public instance counter = 20;
+
+    function increment(address a, address c) public returns (uint64) {
+
+        a.requireAuth();
+        bytes payload = abi.encode("get_num", a);
+        (bool suc, bytes returndata) = c.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+
+        counter = counter + 2;
+
+        return counter;
+       
+    }
+} "#,
+    );
+
+    let c = runtime.deploy_contract(
+        r#"contract c {
+    function get_num(address a) public returns (uint64) {
+        a.requireAuth();
+        return 2;
+    }
+}"#,
+    );
+
+    // same as a, but with the auth_as_curr_contract line commented out.
+    let a_invalid = runtime.deploy_contract(
+        r#"contract a {
+    function call_b (address b, address c) public returns (uint64) {
+        address addr = address(this);
+        // authorize contract c to be called, with function name "get_num" and "a" as an arg.
+        // get_num calls a.require_auth()
+        // auth.authAsCurrContract(c, "get_num", addr);
+        bytes payload = abi.encode("increment", addr, c);
+        (bool suc, bytes returndata) = b.call(payload);
+        uint64 result = abi.decode(returndata, (uint64));
+        return result;
+    }
+}"#,
+    );
+
+    let a = &runtime.contracts[0];
+
+    let ret = runtime.invoke_contract(
+        a,
+        "call_b",
+        vec![b.into_val(&runtime.env), c.into_val(&runtime.env)],
+    );
+
+    let expected: Val = 22_u64.into_val(&runtime.env);
+
+    assert!(expected.shallow_eq(&ret));
+
+    let errors = runtime.invoke_contract_expect_error(
+        &a_invalid,
+        "call_b",
+        vec![b.into_val(&runtime.env), c.into_val(&runtime.env)],
+    );
+
+    assert!(errors[0].contains("Failed Diagnostic Event (not emitted)] contract:CAJXGFIU32R2SF4BVXV2EB2XSSUPUBQMNXWJWB5GYS7WE76TFPPR7Q7P, topics:[log], data:[\"VM call trapped with HostError\", get_num, Error(Auth, InvalidAction)"))
+}

+ 1 - 0
tests/soroban_testcases/mod.rs

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