Procházet zdrojové kódy

Utilize Borsh encoding for 'abi.encode' on Solana (#906)

* Implement Borsh encoding for ABI encoding

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>

* Add codegen tests

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>

* Add runtime tests

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>

* Wire up encoding for multidimensional dynamic arrays

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
Lucas Steuernagel před 3 roky
rodič
revize
7573dacf43

+ 1 - 0
Cargo.toml

@@ -64,6 +64,7 @@ ed25519-dalek = "1.0"
 path-slash = "0.1"
 pretty_assertions = "1.2"
 byte-slice-cast = "1.2.1"
+borsh = "0.9.3"
 
 [package.metadata.docs.rs]
 no-default-features = true

+ 62 - 0
src/codegen/cfg.rs

@@ -19,6 +19,7 @@ use num_traits::One;
 use solang_parser::pt;
 use solang_parser::pt::CodeLocation;
 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::ops::AddAssign;
 use std::str;
 use std::sync::Arc;
 use std::{fmt, fmt::Write};
@@ -158,6 +159,12 @@ pub enum Instr {
         offset: Expression,
         value: Expression,
     },
+    /// Copy bytes from source address to destination address
+    MemCopy {
+        source: Expression,
+        destination: Expression,
+        bytes: Expression,
+    },
     /// Do nothing
     Nop,
 }
@@ -273,6 +280,16 @@ impl Instr {
                 offset.recurse(cx, f);
             }
 
+            Instr::MemCopy {
+                source: from,
+                destination: to,
+                bytes,
+            } => {
+                from.recurse(cx, f);
+                to.recurse(cx, f);
+                bytes.recurse(cx, f);
+            }
+
             Instr::AssertFailure { expr: None }
             | Instr::Unreachable
             | Instr::Nop
@@ -764,6 +781,17 @@ impl ControlFlowGraph {
                     .join(", ")
             ),
             Expression::Undefined(_) => "undef".to_string(),
+            Expression::AdvancePointer {
+                pointer,
+                bytes_offset,
+                ..
+            } => {
+                format!(
+                    "(advance ptr: {}, by: {})",
+                    self.expr_to_string(contract, ns, pointer),
+                    self.expr_to_string(contract, ns, bytes_offset)
+                )
+            }
             Expression::GetRef(_, _, expr) => {
                 format!("(deref {}", self.expr_to_string(contract, ns, expr))
             }
@@ -1100,6 +1128,18 @@ impl ControlFlowGraph {
                     .join(", ")
             ),
             Instr::Nop => String::from("nop"),
+            Instr::MemCopy {
+                source: from,
+                destination: to,
+                bytes,
+            } => {
+                format!(
+                    "memcpy src: {}, dest: {}, bytes_len: {}",
+                    self.expr_to_string(contract, ns, from),
+                    self.expr_to_string(contract, ns, to),
+                    self.expr_to_string(contract, ns, bytes)
+                )
+            }
         }
     }
 
@@ -1849,4 +1889,26 @@ impl Namespace {
             Type::Uint(256)
         }
     }
+
+    /// Checks if struct contains only primitive types and returns its memory non-padded size
+    pub fn calculate_struct_non_padded_size(&self, struct_no: usize) -> Option<BigInt> {
+        let mut size = BigInt::from(0u8);
+        for field in &self.structs[struct_no].fields {
+            if !field.ty.is_primitive() {
+                // If a struct contains a non-primitive type, we cannot calculate its
+                // size during compile time
+                if let Type::Struct(no) = &field.ty {
+                    if let Some(struct_size) = self.calculate_struct_non_padded_size(*no) {
+                        size.add_assign(struct_size);
+                        continue;
+                    }
+                }
+                return None;
+            } else {
+                size.add_assign(field.ty.memory_size_of(self));
+            }
+        }
+
+        Some(size)
+    }
 }

+ 18 - 0
src/codegen/constant_folding.rs

@@ -357,6 +357,24 @@ fn expression(
                 )
             }
         }
+        Expression::AdvancePointer {
+            loc,
+            ty,
+            pointer,
+            bytes_offset: offset,
+        } => {
+            // Only the offset can be simplified
+            let offset = expression(offset, vars, cfg, ns);
+            (
+                Expression::AdvancePointer {
+                    loc: *loc,
+                    ty: ty.clone(),
+                    pointer: pointer.clone(),
+                    bytes_offset: Box::new(offset.0),
+                },
+                offset.1,
+            )
+        }
         Expression::Multiply(loc, ty, unchecked, left, right) => {
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);

+ 699 - 0
src/codegen/encoding/borsh_encoding.rs

@@ -0,0 +1,699 @@
+use crate::ast::{ArrayLength, Namespace, RetrieveType, Type};
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::encoding::{
+    calculate_size_args, finish_array_loop, increment_four, load_array_item, load_struct_member,
+    load_sub_array, set_array_loop, AbiEncoding,
+};
+use crate::codegen::vartable::Vartable;
+use crate::codegen::{Builtin, Expression};
+use num_bigint::BigInt;
+use num_traits::{One, Zero};
+use solang_parser::pt::Loc;
+use std::collections::HashMap;
+use std::ops::{AddAssign, MulAssign};
+
+/// This struct implements the trait Encoding for Borsh encoding
+pub(super) struct BorshEncoding {
+    /// The trait AbiEncoding has a 'cache_storage_loaded' function, which needs this HashMap to work.
+    /// Encoding happens in two steps. First, we look at each argument to calculate their size. If an
+    /// argument is a storage variable, we load it and save it to a local variable.
+    ///
+    /// During a second pass, we copy each argument to a buffer. To copy storage variables properly into
+    /// the buffer, we must load them from storage and save them in a local variable. As we have
+    /// already done this before, we can cache the Expression::Variable, containing the items we loaded before.
+    /// In addition, loading from storage can be an expensive operation if it done with large structs
+    /// or vectors. The has map contains (argument number, Expression::Variable)
+    ///
+    /// For more information, check the comment at function 'cache_storage_load' on encoding/mod.rs
+    storage_cache: HashMap<usize, Expression>,
+}
+
+impl AbiEncoding for BorshEncoding {
+    fn abi_encode(
+        &mut self,
+        loc: &Loc,
+        args: &[Expression],
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        let size = calculate_size_args(self, args, ns, vartab, cfg);
+
+        let encoded_bytes = vartab.temp_name("abi_encoded", &Type::DynamicBytes);
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: *loc,
+                res: encoded_bytes,
+                expr: Expression::AllocDynamicArray(*loc, Type::DynamicBytes, Box::new(size), None),
+            },
+        );
+
+        let mut offset = Expression::NumberLiteral(*loc, Type::Uint(32), BigInt::zero());
+        let buffer = Expression::Variable(*loc, Type::DynamicBytes, encoded_bytes);
+
+        for (arg_no, item) in args.iter().enumerate() {
+            let advance = self.encode(item, &buffer, &offset, arg_no, ns, vartab, cfg);
+            offset = Expression::Add(
+                Loc::Codegen,
+                Type::Uint(32),
+                false,
+                Box::new(offset),
+                Box::new(advance),
+            );
+        }
+
+        buffer
+    }
+
+    fn cache_storage_loaded(&mut self, arg_no: usize, expr: Expression) {
+        self.storage_cache.insert(arg_no, expr);
+    }
+
+    fn get_encoding_size(&self, expr: &Expression, ty: &Type, ns: &Namespace) -> Expression {
+        match ty {
+            Type::Enum(_)
+            | Type::Uint(_)
+            | Type::Int(_)
+            | Type::Contract(_)
+            | Type::Bool
+            | Type::Address(_)
+            | Type::Bytes(_) => {
+                let size = ty.memory_size_of(ns);
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), size)
+            }
+
+            Type::String | Type::DynamicBytes | Type::Slice(_) => {
+                // When encoding a variable length array, the total size is "length (u32)" + elements
+                let length = Expression::Builtin(
+                    Loc::Codegen,
+                    vec![Type::Uint(32)],
+                    Builtin::ArrayLength,
+                    vec![expr.clone()],
+                );
+                increment_four(length)
+            }
+
+            _ => unreachable!("Type should have the same size for all encoding schemes"),
+        }
+    }
+}
+
+impl BorshEncoding {
+    pub fn new() -> BorshEncoding {
+        BorshEncoding {
+            storage_cache: HashMap::new(),
+        }
+    }
+
+    /// Encode expression to buffer. Returns the size in bytes of the encoded item.
+    fn encode(
+        &mut self,
+        expr: &Expression,
+        buffer: &Expression,
+        offset: &Expression,
+        arg_no: usize,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        let expr_ty = expr.ty().unwrap_user_type(ns);
+
+        match &expr_ty {
+            Type::Contract(_) | Type::Address(_) => {
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: expr.clone(),
+                    },
+                );
+                Expression::NumberLiteral(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    BigInt::from(ns.address_length),
+                )
+            }
+
+            Type::Bool => {
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: expr.clone(),
+                    },
+                );
+
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(1u8))
+            }
+
+            Type::Uint(length) | Type::Int(length) => {
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: expr.clone(),
+                    },
+                );
+
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(length / 8))
+            }
+
+            Type::Value => {
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: expr.clone(),
+                    },
+                );
+
+                Expression::NumberLiteral(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    BigInt::from(ns.value_length),
+                )
+            }
+
+            Type::Bytes(length) => {
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: expr.clone(),
+                    },
+                );
+
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(*length))
+            }
+
+            Type::String | Type::DynamicBytes | Type::Slice(_) => {
+                let get_size = Expression::Builtin(
+                    Loc::Codegen,
+                    vec![Type::Uint(32)],
+                    Builtin::ArrayLength,
+                    vec![expr.clone()],
+                );
+                let array_length = vartab.temp_anonymous(&Type::Uint(32));
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: Loc::Codegen,
+                        res: array_length,
+                        expr: get_size,
+                    },
+                );
+
+                let var = Expression::Variable(Loc::Codegen, Type::Uint(32), array_length);
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: var.clone(),
+                    },
+                );
+
+                // ptr + offset + size_of_integer
+                let dest_address = Expression::AdvancePointer {
+                    loc: Loc::Codegen,
+                    ty: Type::BufferPointer,
+                    pointer: Box::new(buffer.clone()),
+                    bytes_offset: Box::new(increment_four(offset.clone())),
+                };
+
+                cfg.add(
+                    vartab,
+                    Instr::MemCopy {
+                        source: expr.clone(),
+                        destination: dest_address,
+                        bytes: var.clone(),
+                    },
+                );
+
+                increment_four(var)
+            }
+
+            Type::Enum(_) => {
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: expr.clone(),
+                    },
+                );
+
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::one())
+            }
+
+            Type::Struct(struct_no) => self.encode_struct(
+                expr,
+                buffer,
+                offset.clone(),
+                &expr_ty,
+                *struct_no,
+                arg_no,
+                ns,
+                vartab,
+                cfg,
+            ),
+
+            Type::Array(ty, dims) => self.encode_array(
+                expr, &expr_ty, ty, dims, arg_no, buffer, offset, ns, vartab, cfg,
+            ),
+
+            Type::UserType(_) | Type::Unresolved | Type::Rational | Type::Unreachable => {
+                unreachable!("Type should not exist in codegen")
+            }
+
+            Type::ExternalFunction { .. } => {
+                let selector = Expression::Builtin(
+                    Loc::Codegen,
+                    vec![Type::Uint(32)],
+                    Builtin::FunctionSelector,
+                    vec![expr.clone()],
+                );
+
+                let address = Expression::Builtin(
+                    Loc::Codegen,
+                    vec![Type::Address(false)],
+                    Builtin::ExternalFunctionAddress,
+                    vec![expr.clone()],
+                );
+
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: selector,
+                    },
+                );
+
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: increment_four(offset.clone()),
+                        value: address,
+                    },
+                );
+
+                let mut size = BigInt::from(4);
+                size.add_assign(BigInt::from(ns.address_length));
+
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), size)
+            }
+
+            Type::InternalFunction { .. }
+            | Type::Void
+            | Type::BufferPointer
+            | Type::Mapping(..) => unreachable!("This type cannot be encoded"),
+
+            Type::Ref(r) => {
+                if let Type::Struct(struct_no) = &**r {
+                    // Structs references should not be dereferenced
+                    return self.encode_struct(
+                        expr,
+                        buffer,
+                        offset.clone(),
+                        &expr_ty,
+                        *struct_no,
+                        arg_no,
+                        ns,
+                        vartab,
+                        cfg,
+                    );
+                }
+                let loaded = Expression::Load(Loc::Codegen, *r.clone(), Box::new(expr.clone()));
+                self.encode(&loaded, buffer, offset, arg_no, ns, vartab, cfg)
+            }
+
+            Type::StorageRef(..) => {
+                let loaded = self.storage_cache.remove(&arg_no).unwrap();
+                self.encode(&loaded, buffer, offset, arg_no, ns, vartab, cfg)
+            }
+        }
+    }
+
+    /// Encode an array and return its size in bytes
+    fn encode_array(
+        &mut self,
+        array: &Expression,
+        array_ty: &Type,
+        elem_ty: &Type,
+        dims: &Vec<ArrayLength>,
+        arg_no: usize,
+        buffer: &Expression,
+        offset: &Expression,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        // Check if we can MemCpy elements into the buffer
+        let direct_encoding = if array_ty.is_dynamic(ns) {
+            // If this is a dynamic array, we can only MemCpy if its elements are of
+            // any primitive type and we don't need to index it.
+            dims.len() == 1 && elem_ty.is_primitive()
+        } else {
+            // If the array is not dynamic, we can MemCpy elements if their are primitive.
+            elem_ty.is_primitive()
+        };
+
+        let size = if dims.is_empty() {
+            // Array has no dimension
+            cfg.add(
+                vartab,
+                Instr::WriteBuffer {
+                    buf: buffer.clone(),
+                    offset: offset.clone(),
+                    value: Expression::NumberLiteral(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        BigInt::from(0u8),
+                    ),
+                },
+            );
+
+            Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(4u8))
+        } else if direct_encoding {
+            // Calculate number of elements
+            let (bytes_size, offset) = if matches!(dims.last(), Some(&ArrayLength::Fixed(_))) {
+                let mut elem_no = BigInt::from(1u8);
+                for item in dims {
+                    assert!(matches!(item, &ArrayLength::Fixed(_)));
+                    elem_no.mul_assign(item.array_length().unwrap());
+                }
+
+                let bytes = elem_ty.memory_size_of(ns);
+                elem_no.mul_assign(&bytes);
+                (
+                    Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), elem_no),
+                    offset.clone(),
+                )
+            } else {
+                let arr_size = Expression::Builtin(
+                    Loc::Codegen,
+                    vec![Type::Uint(32)],
+                    Builtin::ArrayLength,
+                    vec![array.clone()],
+                );
+
+                let size_temp = vartab.temp_anonymous(&Type::Uint(32));
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: Loc::Codegen,
+                        res: size_temp,
+                        expr: arr_size,
+                    },
+                );
+
+                cfg.add(
+                    vartab,
+                    Instr::WriteBuffer {
+                        buf: buffer.clone(),
+                        offset: offset.clone(),
+                        value: Expression::Variable(Loc::Codegen, Type::Uint(32), size_temp),
+                    },
+                );
+
+                let size = Expression::Multiply(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    false,
+                    Box::new(Expression::Variable(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        size_temp,
+                    )),
+                    Box::new(Expression::NumberLiteral(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        elem_ty.memory_size_of(ns),
+                    )),
+                );
+
+                (size, increment_four(offset.clone()))
+            };
+
+            let dest_address = Expression::AdvancePointer {
+                loc: Loc::Codegen,
+                pointer: Box::new(buffer.clone()),
+                ty: Type::BufferPointer,
+                bytes_offset: Box::new(offset),
+            };
+
+            cfg.add(
+                vartab,
+                Instr::MemCopy {
+                    source: array.clone(),
+                    destination: dest_address,
+                    bytes: bytes_size.clone(),
+                },
+            );
+
+            // If the array is dynamic, we have written into the buffer its size (a uint32)
+            // and its elements
+            let dyn_dims = dims.iter().filter(|d| **d == ArrayLength::Dynamic).count();
+            if dyn_dims > 0 {
+                Expression::Add(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    false,
+                    Box::new(bytes_size),
+                    Box::new(Expression::NumberLiteral(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        BigInt::from(4 * dyn_dims),
+                    )),
+                )
+            } else {
+                bytes_size
+            }
+        } else {
+            // In all other cases, we must loop through the array
+            let mut indexes: Vec<usize> = Vec::new();
+            let offset_var = vartab.temp_anonymous(&Type::Uint(32));
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Loc::Codegen,
+                    res: offset_var,
+                    expr: offset.clone(),
+                },
+            );
+            self.encode_complex_array(
+                array,
+                arg_no,
+                dims,
+                buffer,
+                offset_var,
+                dims.len() - 1,
+                ns,
+                vartab,
+                cfg,
+                &mut indexes,
+            );
+
+            // Subtract the original offset from
+            // the offset variable to obtain the vector size in bytes
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Loc::Codegen,
+                    res: offset_var,
+                    expr: Expression::Subtract(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        false,
+                        Box::new(Expression::Variable(
+                            Loc::Codegen,
+                            Type::Uint(32),
+                            offset_var,
+                        )),
+                        Box::new(offset.clone()),
+                    ),
+                },
+            );
+            Expression::Variable(Loc::Codegen, Type::Uint(32), offset_var)
+        };
+
+        size
+    }
+
+    /// Encode a complex array.
+    /// This function indexes an array from its outer dimension to its inner one
+    fn encode_complex_array(
+        &mut self,
+        arr: &Expression,
+        arg_no: usize,
+        dims: &Vec<ArrayLength>,
+        buffer: &Expression,
+        offset_var: usize,
+        dimension: usize,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+        indexes: &mut Vec<usize>,
+    ) {
+        // If this dimension is dynamic, we must save its length before all elements
+        if dims[dimension] == ArrayLength::Dynamic {
+            // TODO: This is wired up for the support of dynamic multidimensional arrays, like
+            // TODO: 'int[3][][4] vec', but it needs testing, as soon as Solang works with them.
+            // TODO: A discussion about this is under way here: https://github.com/hyperledger-labs/solang/issues/932
+            // We only support dynamic arrays whose non-constant length is the outer one.
+            let (sub_array, _) = load_sub_array(
+                arr.clone(),
+                &dims[(dimension + 1)..dims.len()],
+                indexes,
+                true,
+            );
+
+            let size = Expression::Builtin(
+                Loc::Codegen,
+                vec![Type::Uint(32)],
+                Builtin::ArrayLength,
+                vec![sub_array],
+            );
+
+            let offset_expr = Expression::Variable(Loc::Codegen, Type::Uint(32), offset_var);
+            cfg.add(
+                vartab,
+                Instr::WriteBuffer {
+                    buf: buffer.clone(),
+                    offset: offset_expr.clone(),
+                    value: size,
+                },
+            );
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Loc::Codegen,
+                    res: offset_var,
+                    expr: increment_four(offset_expr),
+                },
+            );
+        }
+        let for_loop = set_array_loop(arr, dims, dimension, indexes, vartab, cfg);
+        cfg.set_basic_block(for_loop.body_block);
+        if 0 == dimension {
+            // If we are indexing the last dimension, we have an element, so we can encode it.
+            let deref = load_array_item(arr, dims, indexes);
+            let offset_expr = Expression::Variable(Loc::Codegen, Type::Uint(32), offset_var);
+            let elem_size = self.encode(&deref, buffer, &offset_expr, arg_no, ns, vartab, cfg);
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Loc::Codegen,
+                    res: offset_var,
+                    expr: Expression::Add(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        false,
+                        Box::new(elem_size),
+                        Box::new(offset_expr),
+                    ),
+                },
+            );
+        } else {
+            self.encode_complex_array(
+                arr,
+                arg_no,
+                dims,
+                buffer,
+                offset_var,
+                dimension - 1,
+                ns,
+                vartab,
+                cfg,
+                indexes,
+            )
+        };
+
+        finish_array_loop(&for_loop, vartab, cfg);
+    }
+
+    /// Encode a struct
+    fn encode_struct(
+        &mut self,
+        expr: &Expression,
+        buffer: &Expression,
+        mut offset: Expression,
+        expr_ty: &Type,
+        struct_no: usize,
+        arg_no: usize,
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression {
+        let size = if let Some(no_padding_size) = ns.calculate_struct_non_padded_size(struct_no) {
+            let padded_size = expr_ty.solana_storage_size(ns);
+            // If the size without padding equals the size with padding, we
+            // can memcpy this struct directly.
+            if padded_size.eq(&no_padding_size) {
+                let size = Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), no_padding_size);
+                let dest_address = Expression::AdvancePointer {
+                    loc: Loc::Codegen,
+                    ty: Type::BufferPointer,
+                    pointer: Box::new(buffer.clone()),
+                    bytes_offset: Box::new(offset),
+                };
+                cfg.add(
+                    vartab,
+                    Instr::MemCopy {
+                        source: expr.clone(),
+                        destination: dest_address,
+                        bytes: size.clone(),
+                    },
+                );
+                return size;
+            } else {
+                // This struct has a fixed size, but we cannot memcpy it due to
+                // its padding in memory
+                Some(Expression::NumberLiteral(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    no_padding_size,
+                ))
+            }
+        } else {
+            None
+        };
+
+        let qty = ns.structs[struct_no].fields.len();
+        let first_ty = ns.structs[struct_no].fields[0].ty.clone();
+        let loaded = load_struct_member(first_ty, expr.clone(), 0);
+
+        let mut advance = self.encode(&loaded, buffer, &offset, arg_no, ns, vartab, cfg);
+        let mut runtime_size = advance.clone();
+        for i in 1..qty {
+            let ith_type = ns.structs[struct_no].fields[i].ty.clone();
+            offset = Expression::Add(
+                Loc::Codegen,
+                Type::Uint(32),
+                false,
+                Box::new(offset.clone()),
+                Box::new(advance),
+            );
+            let loaded = load_struct_member(ith_type.clone(), expr.clone(), i);
+            // After fetching the struct member, we can encode it
+            advance = self.encode(&loaded, buffer, &offset, arg_no, ns, vartab, cfg);
+            runtime_size = Expression::Add(
+                Loc::Codegen,
+                Type::Uint(32),
+                false,
+                Box::new(runtime_size),
+                Box::new(advance.clone()),
+            );
+        }
+
+        size.unwrap_or(runtime_size)
+    }
+}

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

@@ -0,0 +1,581 @@
+mod borsh_encoding;
+
+use crate::ast::{ArrayLength, Namespace, RetrieveType, Type};
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::encoding::borsh_encoding::BorshEncoding;
+use crate::codegen::expression::load_storage;
+use crate::codegen::vartable::Vartable;
+use crate::codegen::{Builtin, Expression};
+use crate::Target;
+use num_bigint::BigInt;
+use solang_parser::pt::Loc;
+use std::ops::AddAssign;
+
+/// This trait should be implemented by all encoding methods (ethabi, Scale and Borsh), so that
+/// we have the same interface for creating encode and decode functions.
+pub(super) trait AbiEncoding {
+    /// Receive the arguments and returns the variable containing a byte array
+    fn abi_encode(
+        &mut self,
+        loc: &Loc,
+        args: &[Expression],
+        ns: &Namespace,
+        vartab: &mut Vartable,
+        cfg: &mut ControlFlowGraph,
+    ) -> Expression;
+
+    /// Cache items loaded from storage to reuse them later, so we avoid the expensive operation
+    /// of loading from storage twice. We need the data in two different passes: first to
+    /// calculate its size and then to copy it to the buffer.
+    ///
+    /// This function serves only to cache Expression::Variable, containing items loaded from storage.
+    /// Nothing else should be stored here. For more information, check the comment at
+    /// 'struct BorshEncoding' on borsh_encoding.rs
+    fn cache_storage_loaded(&mut self, arg_no: usize, expr: Expression);
+
+    /// Some types have sizes that are specific to each encoding scheme, so there is no way to generalize.
+    fn get_encoding_size(&self, expr: &Expression, ty: &Type, ns: &Namespace) -> Expression;
+}
+
+/// This function should return the correct encoder, given the target
+pub(super) fn create_encoder(ns: &Namespace) -> impl AbiEncoding {
+    match &ns.target {
+        Target::Solana => BorshEncoding::new(),
+        _ => unreachable!("Other types of encoding have not been implemented yet"),
+    }
+}
+
+/// Calculate the size of a set of arguments to encoding functions
+fn calculate_size_args<T: AbiEncoding>(
+    encoder: &mut T,
+    args: &[Expression],
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> Expression {
+    let mut size = get_expr_size(encoder, 0, &args[0], ns, vartab, cfg);
+    for (i, item) in args.iter().enumerate().skip(1) {
+        size = Expression::Add(
+            Loc::Codegen,
+            Type::Uint(32),
+            false,
+            Box::new(size),
+            Box::new(get_expr_size(encoder, i, item, ns, vartab, cfg)),
+        );
+    }
+
+    size
+}
+
+/// Calculate the size of a single codegen::Expression
+fn get_expr_size<T: AbiEncoding>(
+    encoder: &mut T,
+    arg_no: usize,
+    expr: &Expression,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> Expression {
+    let ty = expr.ty().unwrap_user_type(ns);
+    match &ty {
+        Type::Value => {
+            Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(ns.value_length))
+        }
+
+        Type::Struct(struct_no) => {
+            calculate_struct_size(encoder, arg_no, expr, *struct_no, ns, vartab, cfg)
+        }
+
+        Type::Array(ty, dims) => {
+            calculate_array_size(encoder, expr, ty, dims, arg_no, ns, vartab, cfg)
+        }
+
+        Type::UserType(_) | Type::Unresolved | Type::Rational => {
+            unreachable!("Type should not exist in codegen")
+        }
+
+        Type::ExternalFunction { .. } => {
+            let addr = Expression::Undefined(Type::Address(false));
+            let address_size = encoder.get_encoding_size(&addr, &Type::Address(false), ns);
+            if let Expression::NumberLiteral(_, _, mut number) = address_size {
+                number.add_assign(BigInt::from(4u8));
+                Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), number)
+            } else {
+                increment_four(address_size)
+            }
+        }
+
+        Type::InternalFunction { .. }
+        | Type::Void
+        | Type::Unreachable
+        | Type::BufferPointer
+        | Type::Mapping(..) => unreachable!("This type cannot be encoded"),
+
+        Type::Ref(r) => {
+            if let Type::Struct(struct_no) = &**r {
+                return calculate_struct_size(encoder, arg_no, expr, *struct_no, ns, vartab, cfg);
+            }
+            let loaded = Expression::Load(Loc::Codegen, *r.clone(), Box::new(expr.clone()));
+            get_expr_size(encoder, arg_no, &loaded, ns, vartab, cfg)
+        }
+
+        Type::StorageRef(_, r) => {
+            let var = load_storage(&Loc::Codegen, r, expr.clone(), cfg, vartab);
+            let size = get_expr_size(encoder, arg_no, &var, ns, vartab, cfg);
+            encoder.cache_storage_loaded(arg_no, var.clone());
+            size
+        }
+
+        _ => encoder.get_encoding_size(expr, &ty, ns),
+    }
+}
+
+/// Calculate the size of an array
+fn calculate_array_size<T: AbiEncoding>(
+    encoder: &mut T,
+    array: &Expression,
+    elem_ty: &Type,
+    dims: &Vec<ArrayLength>,
+    arg_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> Expression {
+    let dyn_dims = dims.iter().filter(|d| **d == ArrayLength::Dynamic).count();
+
+    // If the array does not have variable length elements,
+    // we can calculate its size using a simple multiplication (direct_assessment)
+    // i.e. 'uint8[3][] vec' has size vec.length*2*size_of(uint8)
+    // In cases like 'uint [3][][2] v' this is not possible, as v[0] and v[1] have different sizes
+    let direct_assessment =
+        dyn_dims == 0 || (dyn_dims == 1 && dims.last() == Some(&ArrayLength::Dynamic));
+
+    // Check if the array contains only fixed sized elements
+    let primitive_size = if elem_ty.is_primitive() && direct_assessment {
+        Some(elem_ty.memory_size_of(ns))
+    } else if let Type::Struct(struct_no) = elem_ty {
+        if direct_assessment {
+            ns.calculate_struct_non_padded_size(*struct_no)
+        } else {
+            None
+        }
+    } else {
+        None
+    };
+
+    let size_var = if let Some(compile_type_size) = primitive_size {
+        // If the array saves primitive-type elements, its size is sizeof(type)*vec.length
+        let mut size = if let ArrayLength::Fixed(dim) = &dims.last().unwrap() {
+            Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), dim.clone())
+        } else {
+            Expression::Builtin(
+                Loc::Codegen,
+                vec![Type::Uint(32)],
+                Builtin::ArrayLength,
+                vec![array.clone()],
+            )
+        };
+
+        for item in dims.iter().take(dims.len() - 1) {
+            let local_size = Expression::NumberLiteral(
+                Loc::Codegen,
+                Type::Uint(32),
+                item.array_length().unwrap().clone(),
+            );
+            size = Expression::Multiply(
+                Loc::Codegen,
+                Type::Uint(32),
+                false,
+                Box::new(size),
+                Box::new(local_size),
+            );
+        }
+
+        let type_size = Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), compile_type_size);
+        let size = Expression::Multiply(
+            Loc::Codegen,
+            Type::Uint(32),
+            false,
+            Box::new(size),
+            Box::new(type_size),
+        );
+        let size_var = vartab.temp_anonymous(&Type::Uint(32));
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Loc::Codegen,
+                res: size_var,
+                expr: size,
+            },
+        );
+
+        size_var
+    } else {
+        let size_var = vartab.temp_name(
+            format!("array_bytes_size_{}", arg_no).as_str(),
+            &Type::Uint(32),
+        );
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Loc::Codegen,
+                res: size_var,
+                expr: Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(0u8)),
+            },
+        );
+        let mut index_vec: Vec<usize> = Vec::new();
+        calculate_complex_array_size(
+            encoder,
+            arg_no,
+            array,
+            dims,
+            dims.len() - 1,
+            size_var,
+            ns,
+            &mut index_vec,
+            vartab,
+            cfg,
+        );
+        size_var
+    };
+
+    // Each dynamic dimension size occupies 4 bytes in the buffer
+    let dyn_dims = dims.iter().filter(|d| **d == ArrayLength::Dynamic).count();
+
+    if dyn_dims > 0 {
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Loc::Codegen,
+                res: size_var,
+                expr: Expression::Add(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    false,
+                    Box::new(Expression::Variable(Loc::Codegen, Type::Uint(32), size_var)),
+                    Box::new(Expression::NumberLiteral(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        BigInt::from(4 * dyn_dims),
+                    )),
+                ),
+            },
+        );
+    }
+
+    Expression::Variable(Loc::Codegen, Type::Uint(32), size_var)
+}
+
+/// Calculate the size of a complex array.
+/// This function indexes an array from its outer dimension to its inner one
+fn calculate_complex_array_size<T: AbiEncoding>(
+    encoder: &mut T,
+    arg_no: usize,
+    arr: &Expression,
+    dims: &Vec<ArrayLength>,
+    dimension: usize,
+    size_var_no: usize,
+    ns: &Namespace,
+    indexes: &mut Vec<usize>,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) {
+    let for_loop = set_array_loop(arr, dims, dimension, indexes, vartab, cfg);
+    cfg.set_basic_block(for_loop.body_block);
+    if 0 == dimension {
+        let deref = load_array_item(arr, dims, indexes);
+        let elem_size = get_expr_size(encoder, arg_no, &deref, ns, vartab, cfg);
+
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: Loc::Codegen,
+                res: size_var_no,
+                expr: Expression::Add(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    false,
+                    Box::new(Expression::Variable(
+                        Loc::Codegen,
+                        Type::Uint(32),
+                        size_var_no,
+                    )),
+                    Box::new(elem_size),
+                ),
+            },
+        );
+    } else {
+        calculate_complex_array_size(
+            encoder,
+            arg_no,
+            arr,
+            dims,
+            dimension - 1,
+            size_var_no,
+            ns,
+            indexes,
+            vartab,
+            cfg,
+        );
+    }
+
+    finish_array_loop(&for_loop, vartab, cfg);
+}
+
+/// Get the array length at dimension 'index'
+fn get_array_length(
+    arr: &Expression,
+    dims: &[ArrayLength],
+    indexes: &[usize],
+    dimension: usize,
+) -> Expression {
+    if let ArrayLength::Fixed(dim) = &dims[dimension] {
+        Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), dim.clone())
+    } else {
+        let (sub_array, _) = load_sub_array(
+            arr.clone(),
+            &dims[(dimension + 1)..dims.len()],
+            indexes,
+            true,
+        );
+
+        Expression::Builtin(
+            Loc::Codegen,
+            vec![Type::Uint(32)],
+            Builtin::ArrayLength,
+            vec![sub_array],
+        )
+    }
+}
+
+/// Retrieves the size of a struct
+fn calculate_struct_size<T: AbiEncoding>(
+    encoder: &mut T,
+    arg_no: usize,
+    expr: &Expression,
+    struct_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> Expression {
+    if let Some(struct_size) = ns.calculate_struct_non_padded_size(struct_no) {
+        return Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), struct_size);
+    }
+
+    let first_type = ns.structs[struct_no].fields[0].ty.clone();
+    let first_field = load_struct_member(first_type, expr.clone(), 0);
+    let mut size = get_expr_size(encoder, arg_no, &first_field, ns, vartab, cfg);
+    for i in 1..ns.structs[struct_no].fields.len() {
+        let ty = ns.structs[struct_no].fields[i].ty.clone();
+        let field = load_struct_member(ty.clone(), expr.clone(), i);
+        size = Expression::Add(
+            Loc::Codegen,
+            Type::Uint(32),
+            false,
+            Box::new(size.clone()),
+            Box::new(get_expr_size(encoder, arg_no, &field, ns, vartab, cfg)),
+        );
+    }
+
+    size
+}
+
+/// Loads an item from an array
+fn load_array_item(arr: &Expression, dims: &[ArrayLength], indexes: &[usize]) -> Expression {
+    let elem_ty = arr.ty().elem_ty();
+    let (deref, ty) = load_sub_array(arr.clone(), dims, indexes, false);
+    Expression::Subscript(
+        Loc::Codegen,
+        Type::Ref(Box::new(elem_ty)),
+        ty,
+        Box::new(deref),
+        Box::new(Expression::Variable(
+            Loc::Codegen,
+            Type::Uint(32),
+            *indexes.last().unwrap(),
+        )),
+    )
+}
+
+/// Dereferences a subarray. If we have 'int[3][][4] vec' and we need 'int[3][]',
+/// this function returns so.
+/// 'dims' should contain only the dimensions we want to index
+/// 'index' is the list of indexes to use
+/// 'index_first_dim' chooses whether to index the first dimension in dims
+fn load_sub_array(
+    mut arr: Expression,
+    dims: &[ArrayLength],
+    indexes: &[usize],
+    index_first_dim: bool,
+) -> (Expression, Type) {
+    let mut ty = arr.ty();
+    let elem_ty = ty.elem_ty();
+    let start = !index_first_dim as usize;
+    for i in (start..dims.len()).rev() {
+        let local_ty = Type::Array(Box::new(elem_ty.clone()), dims[0..i].to_vec());
+        arr = Expression::Subscript(
+            Loc::Codegen,
+            Type::Ref(Box::new(local_ty.clone())),
+            ty,
+            Box::new(arr),
+            Box::new(Expression::Variable(
+                Loc::Codegen,
+                Type::Uint(32),
+                indexes[indexes.len() - i - 1],
+            )),
+        );
+        ty = local_ty;
+    }
+
+    (arr, ty)
+}
+
+/// This struct manages for-loops created when iterating over arrays
+struct ForLoop {
+    pub cond_block: usize,
+    pub next_block: usize,
+    pub body_block: usize,
+    pub end_block: usize,
+    pub index: usize,
+}
+
+/// Set up the loop to iterate over an array
+fn set_array_loop(
+    arr: &Expression,
+    dims: &[ArrayLength],
+    dimension: usize,
+    indexes: &mut Vec<usize>,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) -> ForLoop {
+    let index_temp = vartab.temp_name(format!("for_i_{}", dimension).as_str(), &Type::Uint(32));
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Loc::Codegen,
+            res: index_temp,
+            expr: Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::from(0u8)),
+        },
+    );
+
+    indexes.push(index_temp);
+    let cond_block = cfg.new_basic_block("cond".to_string());
+    let next_block = cfg.new_basic_block("next".to_string());
+    let body_block = cfg.new_basic_block("body".to_string());
+    let end_block = cfg.new_basic_block("end_for".to_string());
+
+    vartab.new_dirty_tracker();
+    cfg.add(vartab, Instr::Branch { block: cond_block });
+    cfg.set_basic_block(cond_block);
+    let bound = get_array_length(arr, dims, indexes, dimension);
+    let cond_expr = Expression::UnsignedLess(
+        Loc::Codegen,
+        Box::new(Expression::Variable(
+            Loc::Codegen,
+            Type::Uint(32),
+            index_temp,
+        )),
+        Box::new(bound),
+    );
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: cond_expr,
+            true_block: body_block,
+            false_block: end_block,
+        },
+    );
+
+    ForLoop {
+        cond_block,
+        next_block,
+        body_block,
+        end_block,
+        index: index_temp,
+    }
+}
+
+/// Closes the for-loop when iterating over an array
+fn finish_array_loop(for_loop: &ForLoop, vartab: &mut Vartable, cfg: &mut ControlFlowGraph) {
+    cfg.add(
+        vartab,
+        Instr::Branch {
+            block: for_loop.next_block,
+        },
+    );
+    cfg.set_basic_block(for_loop.next_block);
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: Loc::Codegen,
+            res: for_loop.index,
+            expr: Expression::Add(
+                Loc::Codegen,
+                Type::Uint(32),
+                false,
+                Box::new(Expression::Variable(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    for_loop.index,
+                )),
+                Box::new(Expression::NumberLiteral(
+                    Loc::Codegen,
+                    Type::Uint(32),
+                    BigInt::from(1u8),
+                )),
+            ),
+        },
+    );
+    cfg.add(
+        vartab,
+        Instr::Branch {
+            block: for_loop.cond_block,
+        },
+    );
+    cfg.set_basic_block(for_loop.end_block);
+    let phis = vartab.pop_dirty_tracker();
+    cfg.set_phis(for_loop.next_block, phis.clone());
+    cfg.set_phis(for_loop.end_block, phis.clone());
+    cfg.set_phis(for_loop.cond_block, phis);
+}
+
+/// Loads a struct member
+fn load_struct_member(ty: Type, expr: Expression, field: usize) -> Expression {
+    if matches!(ty, Type::Struct(_)) {
+        // We should not dereference a struct.
+        return Expression::StructMember(
+            Loc::Codegen,
+            Type::Ref(Box::new(ty)),
+            Box::new(expr),
+            field,
+        );
+    }
+
+    Expression::Load(
+        Loc::Codegen,
+        ty.clone(),
+        Box::new(Expression::StructMember(
+            Loc::Codegen,
+            Type::Ref(Box::new(ty)),
+            Box::new(expr),
+            field,
+        )),
+    )
+}
+
+/// Increment an expression by four. This is useful because we save array sizes as uint32, so we
+/// need to increment the offset by four constantly.
+fn increment_four(expr: Expression) -> Expression {
+    Expression::Add(
+        Loc::Codegen,
+        Type::Uint(32),
+        false,
+        Box::new(expr),
+        Box::new(Expression::NumberLiteral(
+            Loc::Codegen,
+            Type::Uint(32),
+            BigInt::from(4u8),
+        )),
+    )
+}

+ 9 - 1
src/codegen/expression.rs

@@ -7,6 +7,8 @@ use super::{
     vartable::Vartable,
 };
 use crate::codegen::array_boundary::handle_array_assign;
+use crate::codegen::encoding::create_encoder;
+use crate::codegen::encoding::AbiEncoding;
 use crate::codegen::unused_variable::should_remove_assignment;
 use crate::codegen::{Builtin, Expression};
 use crate::sema::{
@@ -1283,7 +1285,13 @@ fn abi_encode(
     let args = args
         .iter()
         .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
-        .collect();
+        .collect::<Vec<Expression>>();
+
+    if ns.target == Target::Solana {
+        let mut encoder = create_encoder(ns);
+        return encoder.abi_encode(loc, &args, ns, vartab, cfg);
+    }
+
     let res = vartab.temp(
         &pt::Identifier {
             loc: *loc,

+ 29 - 4
src/codegen/mod.rs

@@ -2,6 +2,7 @@ mod array_boundary;
 pub mod cfg;
 mod constant_folding;
 mod dead_storage;
+mod encoding;
 mod expression;
 mod external_functions;
 mod reaching_definitions;
@@ -36,7 +37,7 @@ use num_bigint::{BigInt, Sign};
 use num_rational::BigRational;
 use num_traits::{FromPrimitive, Zero};
 use solang_parser::pt;
-use solang_parser::pt::{CodeLocation, Loc};
+use solang_parser::pt::CodeLocation;
 
 // The sizeof(struct account_data_header)
 pub const SOLANA_FIRST_OFFSET: u64 = 16;
@@ -425,10 +426,16 @@ pub enum Expression {
     Undefined(Type),
     Variable(pt::Loc, Type, usize),
     ZeroExt(pt::Loc, Type, Box<Expression>),
+    AdvancePointer {
+        loc: pt::Loc,
+        ty: Type,
+        pointer: Box<Expression>,
+        bytes_offset: Box<Expression>,
+    },
 }
 
 impl CodeLocation for Expression {
-    fn loc(&self) -> Loc {
+    fn loc(&self) -> pt::Loc {
         match self {
             Expression::AbiEncode { loc, .. }
             | Expression::StorageArrayLength { loc, .. }
@@ -483,7 +490,8 @@ impl CodeLocation for Expression {
             | Expression::BytesCast(loc, ..)
             | Expression::SignedMore(loc, ..)
             | Expression::UnsignedMore(loc, ..)
-            | Expression::ZeroExt(loc, ..) => *loc,
+            | Expression::ZeroExt(loc, ..)
+            | Expression::AdvancePointer { loc, .. } => *loc,
 
             Expression::InternalFunctionCfg(_) | Expression::Poison | Expression::Undefined(_) => {
                 pt::Loc::Codegen
@@ -528,6 +536,11 @@ impl Recurse for Expression {
             | Expression::Power(_, _, _, left, right)
             | Expression::Subscript(_, _, _, left, right)
             | Expression::Subtract(_, _, _, left, right)
+            | Expression::AdvancePointer {
+                pointer: left,
+                bytes_offset: right,
+                ..
+            }
             | Expression::Add(_, _, _, left, right) => {
                 left.recurse(cx, f);
                 right.recurse(cx, f);
@@ -629,7 +642,8 @@ impl RetrieveType for Expression {
             | Expression::AllocDynamicArray(_, ty, ..)
             | Expression::BytesCast(_, ty, ..)
             | Expression::RationalNumberLiteral(_, ty, ..)
-            | Expression::Subscript(_, ty, ..) => ty.clone(),
+            | Expression::Subscript(_, ty, ..)
+            | Expression::AdvancePointer { ty, .. } => ty.clone(),
 
             Expression::BoolLiteral(..)
             | Expression::MoreEqual(..)
@@ -1077,6 +1091,17 @@ impl Expression {
                     Box::new(filter(left, ctx)),
                     Box::new(filter(right, ctx)),
                 ),
+                Expression::AdvancePointer {
+                    loc,
+                    ty,
+                    pointer,
+                    bytes_offset: offset,
+                } => Expression::AdvancePointer {
+                    loc: *loc,
+                    ty: ty.clone(),
+                    pointer: Box::new(filter(pointer, ctx)),
+                    bytes_offset: Box::new(filter(offset, ctx)),
+                },
                 Expression::Not(loc, expr) => Expression::Not(*loc, Box::new(filter(expr, ctx))),
                 Expression::Complement(loc, ty, expr) => {
                     Expression::Complement(*loc, ty.clone(), Box::new(filter(expr, ctx)))

+ 19 - 1
src/codegen/subexpression_elimination/common_subexpression_tracker.rs

@@ -39,6 +39,8 @@ pub struct CommonSubExpressionTracker {
     cur_block: usize,
     new_cfg_instr: Vec<Instr>,
     parent_block_instr: Vec<(usize, Instr)>,
+    /// Map from variable number to common subexpression
+    mapped_variables: HashMap<usize, usize>,
     /// The CFG is a cyclic graph. In order properly find the lowest common block,
     /// we transformed it in a DAG, removing cycles from loops.
     cfg_dag: Vec<Vec<usize>>,
@@ -75,7 +77,7 @@ impl CommonSubExpressionTracker {
 
         self.inserted_subexpressions
             .insert(expr_type.clone(), self.len);
-        self.len += 1;
+
         self.common_subexpressions.push(CommonSubexpression {
             in_cfg: node.available_variable.is_available(),
             var_no: node.available_variable.get_var_number(),
@@ -89,6 +91,22 @@ impl CommonSubExpressionTracker {
                 None
             },
         });
+
+        if let Some(var_no) = node.available_variable.get_var_number() {
+            // If we encounter an expression like 'x = y+2', we can map 'x' to 'y+2', whenever possible.
+            self.mapped_variables.insert(var_no, self.len);
+        }
+
+        self.len += 1;
+    }
+
+    /// Invalidate a mapped variable
+    pub fn invalidate_mapped_variable(&mut self, var_no: &usize) {
+        if let Some(expr_id) = self.mapped_variables.remove(var_no) {
+            self.common_subexpressions[expr_id].var_loc = None;
+            self.common_subexpressions[expr_id].in_cfg = false;
+            self.common_subexpressions[expr_id].var_no = None;
+        }
     }
 
     /// Create variables in the CFG

+ 12 - 0
src/codegen/subexpression_elimination/expression.rs

@@ -133,6 +133,13 @@ impl Expression {
                 Expression::LessEqual(*loc, Box::new(left.clone()), Box::new(right.clone()))
             }
 
+            Expression::AdvancePointer { loc, ty, .. } => Expression::AdvancePointer {
+                loc: *loc,
+                ty: ty.clone(),
+                pointer: Box::new(left.clone()),
+                bytes_offset: Box::new(right.clone()),
+            },
+
             Expression::StringCompare(loc, left_exp, right_exp) => {
                 if !matches!(
                     (left_exp, right_exp),
@@ -242,6 +249,11 @@ impl Expression {
             | Expression::SignedLess(_, left, right)
             | Expression::UnsignedLess(_, left, right)
             | Expression::MoreEqual(_, left, right)
+            | Expression::AdvancePointer {
+                pointer: left,
+                bytes_offset: right,
+                ..
+            }
             | Expression::LessEqual(_, left, right) => Some((left, right)),
 
             _ => None,

+ 27 - 0
src/codegen/subexpression_elimination/instruction.rs

@@ -37,6 +37,7 @@ impl AvailableExpressionSet {
                         node.available_variable = AvailableVariable::Available(*res, *loc);
                     }
                 }
+                cst.invalidate_mapped_variable(res);
                 self.kill(*res);
             }
 
@@ -135,6 +136,16 @@ impl AvailableExpressionSet {
                 let _ = self.gen_expression(value, ave, cst);
             }
 
+            Instr::MemCopy {
+                source: from,
+                destination: to,
+                bytes,
+            } => {
+                let _ = self.gen_expression(from, ave, cst);
+                let _ = self.gen_expression(to, ave, cst);
+                let _ = self.gen_expression(bytes, ave, cst);
+            }
+
             Instr::AssertFailure { expr: None }
             | Instr::Unreachable
             | Instr::Nop
@@ -380,6 +391,22 @@ impl AvailableExpressionSet {
                 topic_tys: topic_tys.clone(),
             },
 
+            Instr::MemCopy {
+                source: from,
+                destination: to,
+                bytes,
+            } => Instr::MemCopy {
+                source: self.regenerate_expression(from, ave, cst).1,
+                destination: self.regenerate_expression(to, ave, cst).1,
+                bytes: self.regenerate_expression(bytes, ave, cst).1,
+            },
+
+            Instr::WriteBuffer { buf, offset, value } => Instr::WriteBuffer {
+                buf: self.regenerate_expression(buf, ave, cst).1,
+                offset: self.regenerate_expression(offset, ave, cst).1,
+                value: self.regenerate_expression(value, ave, cst).1,
+            },
+
             _ => instr.clone(),
         }
     }

+ 1 - 1
src/codegen/subexpression_elimination/mod.rs

@@ -94,7 +94,7 @@ pub fn common_sub_expression_elimination(cfg: &mut ControlFlowGraph, ns: &mut Na
     cst.set_dag(dag);
     sets.insert(0, AvailableExpressionSet::default());
 
-    // First pass: identify common subexpressions using available expressiona analysis
+    // First pass: identify common subexpressions using available expressions analysis
     for (block_no, cycle) in &visiting_order {
         let cur_block = &cfg.blocks[*block_no];
         ave.set_cur_block(*block_no);

+ 2 - 0
src/codegen/subexpression_elimination/operator.rs

@@ -31,6 +31,7 @@ pub enum Operator {
     NotEqual,
     StringConcat,
     StringCompare,
+    AdvancePointer,
     //Unary operations
     Not,
     ZeroExt(Type),
@@ -78,6 +79,7 @@ impl Expression {
             Expression::Complement(..) => Operator::Complement,
             Expression::StringCompare(..) => Operator::StringCompare,
             Expression::StringConcat(..) => Operator::StringConcat,
+            Expression::AdvancePointer { .. } => Operator::AdvancePointer,
             _ => {
                 unreachable!("Expression does not represent an operator.")
             }

+ 4 - 1
src/codegen/vector_to_slice.rs

@@ -80,7 +80,10 @@ fn find_writable_vectors(
 
                 apply_transfers(&block.transfers[instr_no], vars, writable);
             }
-            Instr::WriteBuffer { buf, .. } => {
+            Instr::MemCopy {
+                destination: buf, ..
+            }
+            | Instr::WriteBuffer { buf, .. } => {
                 if let Expression::Variable(_, _, var_no) = buf {
                     if let Some(entry) = vars.get_mut(var_no) {
                         writable.extend(entry.keys());

+ 50 - 0
src/emit/mod.rs

@@ -3048,6 +3048,25 @@ pub trait TargetRuntime<'a> {
                 self.format_string(bin, args, vartab, function, ns)
             }
 
+            Expression::AdvancePointer {
+                pointer,
+                bytes_offset,
+                ..
+            } => {
+                let pointer = if pointer.ty().is_dynamic_memory() {
+                    bin.vector_bytes(self.expression(bin, pointer, vartab, function, ns))
+                } else {
+                    self.expression(bin, pointer, vartab, function, ns)
+                        .into_pointer_value()
+                };
+                let offset = self
+                    .expression(bin, bytes_offset, vartab, function, ns)
+                    .into_int_value();
+                let advanced = unsafe { bin.builder.build_gep(pointer, &[offset], "adv_pointer") };
+
+                advanced.into()
+            }
+
             Expression::RationalNumberLiteral(..)
             | Expression::List(..)
             | Expression::Undefined(..)
@@ -4466,6 +4485,37 @@ pub trait TargetRuntime<'a> {
 
                         bin.builder.build_store(start, value);
                     }
+                    Instr::MemCopy {
+                        source: from,
+                        destination: to,
+                        bytes,
+                    } => {
+                        let src = if from.ty().is_dynamic_memory() {
+                            bin.vector_bytes(self.expression(bin, from, &w.vars, function, ns))
+                        } else {
+                            self.expression(bin, from, &w.vars, function, ns)
+                                .into_pointer_value()
+                        };
+
+                        let dest = self.expression(bin, to, &w.vars, function, ns);
+                        let size = self.expression(bin, bytes, &w.vars, function, ns);
+
+                        if matches!(bytes, Expression::NumberLiteral(..)) {
+                            let _ = bin.builder.build_memcpy(
+                                dest.into_pointer_value(),
+                                1,
+                                src,
+                                1,
+                                size.into_int_value(),
+                            );
+                        } else {
+                            bin.builder.build_call(
+                                bin.module.get_function("__memcpy").unwrap(),
+                                &[dest.into(), src.into(), size.into()],
+                                "",
+                            );
+                        }
+                    }
                 }
             }
         }

+ 12 - 0
src/sema/ast.rs

@@ -59,6 +59,10 @@ pub enum Type {
     Slice(Box<Type>),
     /// We could not resolve this type
     Unresolved,
+    /// When we advance a pointer, it cannot be any of the previous types.
+    /// e.g. Type::Bytes is a pointer to struct.vector. When we advance it, it is a pointer
+    /// to latter's data region
+    BufferPointer,
 }
 
 #[derive(PartialEq, Clone, Eq, Hash, Debug)]
@@ -98,6 +102,14 @@ impl Type {
             _ => unimplemented!("size of type not known"),
         }
     }
+
+    pub fn unwrap_user_type(self, ns: &Namespace) -> Type {
+        if let Type::UserType(type_no) = self {
+            ns.user_types[type_no].ty.clone()
+        } else {
+            self
+        }
+    }
 }
 
 #[derive(PartialEq, Clone, Debug, Copy)]

+ 10 - 1
src/sema/types.rs

@@ -852,6 +852,7 @@ impl Type {
             Type::Unreachable => "unreachable".to_owned(),
             Type::Slice(ty) => format!("{} slice", ty.to_string(ns)),
             Type::Unresolved => "unresolved".to_owned(),
+            Type::BufferPointer => "buffer_pointer".to_owned(),
         }
     }
 
@@ -935,6 +936,14 @@ impl Type {
         }
     }
 
+    /// Fetch the type of an array element
+    pub fn elem_ty(&self) -> Self {
+        match self {
+            Type::Array(ty, _) => *ty.clone(),
+            _ => unreachable!("Type is not an array"),
+        }
+    }
+
     /// Is this a reference type of fixed size
     pub fn is_fixed_reference_type(&self) -> bool {
         match self {
@@ -1346,7 +1355,7 @@ impl Type {
     /// Is this a reference to dynamic memory (arrays, strings)
     pub fn is_dynamic_memory(&self) -> bool {
         match self {
-            Type::String | Type::DynamicBytes => true,
+            Type::String | Type::DynamicBytes | Type::Slice(_) => true,
             Type::Array(_, dim) if dim.last() == Some(&ArrayLength::Dynamic) => true,
             Type::Ref(ty) => ty.is_dynamic_memory(),
             _ => false,

+ 7 - 6
tests/codegen_testcases/solidity/array_boundary_opt.sol

@@ -7,7 +7,8 @@ contract Array_bound_Test {
         uint256 size,
         uint32 size32
     ) public pure returns (uint256) {
-        // CHECK: ty:uint32 %array_length.temp.23 = (trunc uint32 (arg #1))
+        // CHECK: ty:uint32 %1.cse_temp = (trunc uint32 (arg #1))
+	    // CHECK: ty:uint32 %array_length.temp.23 = %1.cse_temp
         uint256[] a = new uint256[](size);
 
         // CHECK: ty:uint32 %array_length.temp.24 = (arg #2)
@@ -16,7 +17,7 @@ contract Array_bound_Test {
         // CHECK: ty:uint32 %array_length.temp.25 = uint32 20
         uint256[] d = new uint256[](20);
 
-        // CHECK: ty:uint32 %array_length.temp.23 = (%array_length.temp.23 + uint32 1)
+        // CHECK: ty:uint32 %array_length.temp.23 = (%1.cse_temp + uint32 1)
         a.push();
 
         // CHECK: ty:uint32 %array_length.temp.24 = ((arg #2) - uint32 1)
@@ -34,11 +35,11 @@ contract Array_bound_Test {
         bool[] b = new bool[](210);
 
         if (cond) {
-            // CHECK: ty:uint32 %array_length.temp.29 = uint32 211
+            // CHECK: ty:uint32 %array_length.temp.30 = uint32 211
             b.push(true);
         }
 
-        // CHECK: return %array_length.temp.29
+        // CHECK: return %array_length.temp.30
         return b.length;
     }
 
@@ -78,14 +79,14 @@ contract Array_bound_Test {
         int256[] vec = new int256[](10);
 
         for (int256 i = 0; i < 5; i++) {
-            // CHECK: branchcond (unsigned more %array_length.temp.39 > uint32 20), block5, block6
+            // CHECK: branchcond (unsigned more %array_length.temp.40 > uint32 20), block5, block6
             if (vec.length > 20) {
                 break;
             }
             vec.push(3);
         }
 
-        // CHECK: branchcond (%array_length.temp.39 == uint32 15), block7, block8
+        // CHECK: branchcond (%array_length.temp.40 == uint32 15), block7, block8
         assert(vec.length == 15);
     }
 

+ 193 - 0
tests/codegen_testcases/solidity/borsh_encoding_complex_types.sol

@@ -0,0 +1,193 @@
+// RUN: --target solana --emit cfg --no-strength-reduce
+
+contract EncodingTest {
+    struct NonConstantStruct {
+        uint64 a;
+        string[] b;
+    }
+
+    string[] non_cte_array;
+    NonConstantStruct[] complex_array;
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::nonCteArray
+    function nonCteArray() public view returns (bytes memory) {
+        bytes memory b = abi.encode(non_cte_array);
+
+        // CHECK: %temp.7 = load storage slot(uint32 16) ty:string[]
+        // CHECK: ty:uint32 %array_bytes_size_0.temp.8 = uint32 0
+	    // CHECK: ty:uint32 %for_i_0.temp.9 = uint32 0
+	    // CHECK: branch block1
+
+        // CHECK: block1: # cond
+        // CHECK: branchcond (unsigned less %for_i_0.temp.9 < (builtin ArrayLength (%temp.7))), block3, block4
+
+        // CHECK: block2: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.9 = (%for_i_0.temp.9 + uint32 1)
+	    // CHECK: branch block1
+
+        // CHECK: block3: # body
+        // CHECK: ty:uint32 %array_bytes_size_0.temp.8 = (%array_bytes_size_0.temp.8 + ((builtin ArrayLength ((load (subscript string[] %temp.7[%for_i_0.temp.9])))) + uint32 4))
+	    // CHECK: branch block2
+
+        // CHECK: block4: # end_for
+        // CHECK: ty:bytes %abi_encoded.temp.10 = (alloc bytes len %array_bytes_size_0.temp.8)
+		// CHECK: ty:uint32 %temp.11 = uint32 0
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.10 offset:%temp.11 value:(builtin ArrayLength (%temp.7))
+	    // CHECK: ty:uint32 %temp.11 = uint32 4
+	    // CHECK: ty:uint32 %for_i_0.temp.12 = uint32 0
+	    // CHECK: branch block5
+
+        // CHECK: block5: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.12 < (builtin ArrayLength (%temp.7))), block7, block8
+
+        // CHECK: block6: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.12 = (%for_i_0.temp.12 + uint32 1)
+	    // CHECK: branch block5
+
+        // CHECK: block7: # body
+	    // CHECK: ty:uint32 %temp.13 = (builtin ArrayLength ((load (subscript string[] %temp.7[%for_i_0.temp.12]))))
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.10 offset:%temp.11 value:%temp.13
+	    // CHECK: memcpy src: (load (subscript string[] %temp.7[%for_i_0.temp.12])), dest: (advance ptr: %abi_encoded.temp.10, by: (%temp.11 + uint32 4)), bytes_len: %temp.13
+	    // CHECK: ty:uint32 %temp.11 = ((%temp.13 + uint32 4) + %temp.11)
+	    // CHECK: branch block6   
+
+        // CHECK: block8: # end_for
+	    // CHECK: ty:uint32 %temp.11 = (%temp.11 - uint32 0)
+	    // CHECK: ty:bytes %b = %abi_encoded.temp.10
+        return b;
+    }
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::complexStruct
+    function complexStruct() public view returns (bytes memory) {
+        NonConstantStruct memory cte = NonConstantStruct(1, non_cte_array);
+        bytes memory b = abi.encode(cte);
+
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.15 = uint32 0
+	    // CHECK: ty:uint32 %for_i_0.temp.16 = uint32 0
+	    // CHECK: branch block1
+
+        // CHECK: block1: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.16 < (builtin ArrayLength ((load (struct %cte field 1))))), block3, block4
+
+        // CHECK: block2: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.16 = (%for_i_0.temp.16 + uint32 1)
+	    // CHECK: branch block1
+
+        // CHECK: block3: # body
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.15 = (%array_bytes_size_0.temp.15 + ((builtin ArrayLength ((load (subscript string[] (load (struct %cte field 1))[%for_i_0.temp.16])))) + uint32 4))
+	    // CHECK: branch block2
+
+        // CHECK: block4: # end_for
+	    // CHECK: ty:bytes %abi_encoded.temp.17 = (alloc bytes len (uint32 8 + %array_bytes_size_0.temp.15))
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.17 offset:uint32 0 value:(load (struct %cte field 0))
+		// CHECK: ty:uint32 %temp.18 = uint32 8
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.17 offset:%temp.18 value:(builtin ArrayLength ((load (struct %cte field 1))))
+	    // CHECK: ty:uint32 %temp.18 = uint32 12
+	    // CHECK: ty:uint32 %for_i_0.temp.19 = uint32 0
+	    // CHECK: branch block5
+
+        // CHECK: block5: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.19 < (builtin ArrayLength ((load (struct %cte field 1))))), block7, block8
+
+        // CHECK: block6: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.19 = (%for_i_0.temp.19 + uint32 1)
+	    // CHECK: branch block5
+
+        // CHECK: block7: # body
+	    // CHECK: ty:uint32 %temp.20 = (builtin ArrayLength ((load (subscript string[] (load (struct %cte field 1))[%for_i_0.temp.19]))))
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.17 offset:%temp.18 value:%temp.20
+	    // CHECK: memcpy src: (load (subscript string[] (load (struct %cte field 1))[%for_i_0.temp.19])), dest: (advance ptr: %abi_encoded.temp.17, by: (%temp.18 + uint32 4)), bytes_len: %temp.20
+	    // CHECK: ty:uint32 %temp.18 = ((%temp.20 + uint32 4) + %temp.18)
+	    // CHECK: branch block6
+
+        // CHECK: block8: # end_for
+	    // CHECK: ty:uint32 %temp.18 = (%temp.18 - uint32 8)
+	    // CHECK: ty:bytes %b = %abi_encoded.temp.17
+
+        return b;
+    }
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::complexArray
+    function complexArray() public view returns (bytes memory) {
+        bytes memory b = abi.encode(complex_array);
+
+	    // CHECK: %temp.21 = load storage slot(uint32 20) ty:struct EncodingTest.NonConstantStruct[]
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.22 = uint32 0
+	    // CHECK: ty:uint32 %for_i_0.temp.23 = uint32 0
+	    // CHECK: branch block1
+
+        // CHECK: block1: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.23 < (builtin ArrayLength (%temp.21))), block3, block4
+
+        // CHECK: block2: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.23 = (%for_i_0.temp.23 + uint32 1)
+	    // CHECK: branch block1
+
+        // CHECK: block3: # body
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.24 = uint32 0
+	    // CHECK: ty:uint32 %for_i_0.temp.25 = uint32 0
+	    // CHECK: branch block5
+
+        // CHECK: block4: # end_for
+	    // CHECK: ty:bytes %abi_encoded.temp.26 = (alloc bytes len %array_bytes_size_0.temp.22)
+		// CHECK: ty:uint32 %temp.27 = uint32 0
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:%temp.27 value:(builtin ArrayLength (%temp.21))
+	    // CHECK: ty:uint32 %temp.27 = uint32 4
+	    // CHECK: ty:uint32 %for_i_0.temp.28 = uint32 0	
+        // CHECK: branch block9
+
+        // CHECK: block5: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.25 < (builtin ArrayLength ((load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.23]) field 1))))), block7, block8
+
+        // CHECK: block6: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.25 = (%for_i_0.temp.25 + uint32 1)
+	    // CHECK: branch block5
+
+        // CHECK: block7: # body
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.24 = (%array_bytes_size_0.temp.24 + ((builtin ArrayLength ((load (subscript string[] (load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.23]) field 1))[%for_i_0.temp.25])))) + uint32 4))
+	    // CHECK: branch block6
+
+        // CHECK: block8: # end_for
+	    // CHECK: ty:uint32 %array_bytes_size_0.temp.22 = (%array_bytes_size_0.temp.22 + (uint32 8 + %array_bytes_size_0.temp.24))
+	    // CHECK: branch block2
+
+        // CHECK: block9: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.28 < (builtin ArrayLength (%temp.21))), block11, block12
+
+        // CHECK: block10: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.28 = (%for_i_0.temp.28 + uint32 1)
+	    // CHECK: branch block9
+
+        // CHECK: block11: # body
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:%temp.27 value:(load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.28]) field 0))
+		// CHECK: ty:uint32 %temp.29 = (%temp.27 + uint32 8)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:%temp.29 value:(builtin ArrayLength ((load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.28]) field 1))))
+	    // CHECK: ty:uint32 %temp.29 = (%temp.29 + uint32 4)
+	    // CHECK: ty:uint32 %for_i_0.temp.30 = uint32 0
+	    // CHECK: branch block13
+
+        // CHECK: block12: # end_for
+	    // CHECK: ty:uint32 %temp.27 = (%temp.27 - uint32 0)
+	    // CHECK: ty:bytes %b = %abi_encoded.temp.26
+
+        // CHECK: block13: # cond
+	    // CHECK: branchcond (unsigned less %for_i_0.temp.30 < (builtin ArrayLength ((load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.28]) field 1))))), block15, block16
+
+        // CHECK: block14: # next
+	    // CHECK: ty:uint32 %for_i_0.temp.30 = (%for_i_0.temp.30 + uint32 1)
+	    // CHECK: branch block13
+
+        // CHECK: block15: # body
+	    // CHECK: ty:uint32 %temp.31 = (builtin ArrayLength ((load (subscript string[] (load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.28]) field 1))[%for_i_0.temp.30]))))
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:%temp.29 value:%temp.31
+	    // CHECK: memcpy src: (load (subscript string[] (load (struct (subscript struct EncodingTest.NonConstantStruct[] %temp.21[%for_i_0.temp.28]) field 1))[%for_i_0.temp.30])), dest: (advance ptr: %abi_encoded.temp.26, by: (%temp.29 + uint32 4)), bytes_len: %temp.31
+	    // CHECK: ty:uint32 %temp.29 = ((%temp.31 + uint32 4) + %temp.29)
+	    // CHECK: branch block14
+
+        // CHECK: block16: # end_for
+	    // CHECK: ty:uint32 %temp.29 = (%temp.29 - (%temp.27 + uint32 8))
+	    // CHECK: ty:uint32 %temp.27 = ((uint32 8 + %temp.29) + %temp.27)
+	    // CHECK: branch block10
+
+        return b;
+    }
+}

+ 250 - 0
tests/codegen_testcases/solidity/borsh_encoding_simple_types.sol

@@ -0,0 +1,250 @@
+// RUN: --target solana --emit cfg --no-strength-reduce
+
+contract EncodingTest {
+
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::encodePrimitive_1__bool
+    function encodePrimitive_1(bool a) public view returns (bytes memory) {
+        address myAddr = address(this);
+        uint8 b = 1;
+        uint16 c = 2;
+        uint32 d = 3;
+        uint64 e = 4;
+        uint128 f = 5;
+        uint256 g = 6;
+        int8 h = 7;
+        int16 i = 8;
+        int32 j = 9;
+        int64 k = 10;
+        int128 l = 11;
+        uint104 l2 = 23;
+        int256 m = 12;
+
+        // CHECK: ty:bytes %abi_encoded.temp.55 = (alloc bytes len (((((((((((((((uint32 32 + uint32 32) + uint32 1) + uint32 1) + uint32 2) + uint32 4) + uint32 8) + uint32 16) + uint32 32) + uint32 1) + uint32 2) + uint32 4) + uint32 8) + uint32 16) + uint32 13) + uint32 32))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:uint32 0 value:%myAddr
+        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + uint32 32)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%1.cse_temp value:(builtin GetAddress ())
+        // CHECK: ty:uint32 %2.cse_temp = (%1.cse_temp + uint32 32)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%2.cse_temp value:%a
+        // CHECK: ty:uint32 %3.cse_temp = (%2.cse_temp + uint32 1)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%3.cse_temp value:%b
+        // CHECK:ty:uint32 %4.cse_temp = (%3.cse_temp + uint32 1)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%4.cse_temp value:%c
+	    // CHECK: ty:uint32 %5.cse_temp = (%4.cse_temp + uint32 2)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%5.cse_temp value:%d
+	    // CHECK: ty:uint32 %6.cse_temp = (%5.cse_temp + uint32 4)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%6.cse_temp value:%e
+	    // CHECK: ty:uint32 %7.cse_temp = (%6.cse_temp + uint32 8)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%7.cse_temp value:%f
+	    // CHECK: ty:uint32 %8.cse_temp = (%7.cse_temp + uint32 16)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%8.cse_temp value:%g
+	    // CHECK: ty:uint32 %9.cse_temp = (%8.cse_temp + uint32 32)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%9.cse_temp value:%h
+	    // CHECK: ty:uint32 %10.cse_temp = (%9.cse_temp + uint32 1)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%10.cse_temp value:%i
+	    // CHECK: ty:uint32 %11.cse_temp = (%10.cse_temp + uint32 2)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%11.cse_temp value:%j
+	    // CHECK: ty:uint32 %12.cse_temp = (%11.cse_temp + uint32 4)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%12.cse_temp value:%k
+	    // CHECK: ty:uint32 %13.cse_temp = (%12.cse_temp + uint32 8)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%13.cse_temp value:%l
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:%14.cse_temp value:%l2
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.55 offset:(%14.cse_temp + uint32 13) value:%m
+	    // CHECK: ty:bytes %n = %abi_encoded.temp.55
+
+        bytes memory n = abi.encode(myAddr, this, a, b, 
+        c, d, e, f, g, h, i, j, k, l, l2, m);
+        return n;
+    }
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::encodeFixedByes
+    function encodeFixedByes() public pure returns (bytes memory) {
+        bytes1 a = "a";
+        bytes2 b = "ab";
+        bytes3 c = "abc";
+        bytes4 d = "abcd";
+
+        bytes16 p = "abcdefghijklmnop";
+        bytes17 q = "abcdefghijklmnopq";
+        bytes18 r = "abcdefghijklmnopqr";
+        bytes19 s = "abcdefghijklmnopqrs";
+        bytes20 t = "abcdefghijklmnopqrst";
+
+        bytes28 e = "abcdefghijklmnopqrstuvwxyz";
+        bytes29 f = "qwertyuiopasdfghjklllzxcvbnm";
+        bytes30 g = ".,mnbvcxzlkjhgfdsapoiuytrewq";  
+        bytes31 h = "qazxsedcvfrtgbnhyujmkiopl,.";
+        bytes32 i = "coffe_is_tastier_than_tea";
+ 
+        bytes memory k = abi.encode(a, b, c, d, p, q, r, s, t, e, f, g, h, i);
+        // CHECK: ty:bytes %abi_encoded.temp.70 = (alloc bytes len (((((((((((((uint32 1 + uint32 2) + uint32 3) + uint32 4) + uint32 16) + uint32 17) + uint32 18) + uint32 19) + uint32 20) + uint32 28) + uint32 29) + uint32 30) + uint32 31) + uint32 32))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:uint32 0 value:%a
+	    // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + uint32 1)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%1.cse_temp value:%b
+	    // CHECK: ty:uint32 %2.cse_temp = (%1.cse_temp + uint32 2)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%2.cse_temp value:%c
+	    // CHECK: ty:uint32 %3.cse_temp = (%2.cse_temp + uint32 3)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%3.cse_temp value:%d
+	    // CHECK: ty:uint32 %4.cse_temp = (%3.cse_temp + uint32 4)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%4.cse_temp value:%p
+	    // CHECK: ty:uint32 %5.cse_temp = (%4.cse_temp + uint32 16)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%5.cse_temp value:%q
+	    // CHECK: ty:uint32 %6.cse_temp = (%5.cse_temp + uint32 17)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%6.cse_temp value:%r
+	    // CHECK: ty:uint32 %7.cse_temp = (%6.cse_temp + uint32 18)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%7.cse_temp value:%s
+	    // CHECK: ty:uint32 %8.cse_temp = (%7.cse_temp + uint32 19)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%8.cse_temp value:%t
+	    // CHECK: ty:uint32 %9.cse_temp = (%8.cse_temp + uint32 20)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%9.cse_temp value:%e
+	    // CHECK: ty:uint32 %10.cse_temp = (%9.cse_temp + uint32 28)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%10.cse_temp value:%f
+	    // CHECK: ty:uint32 %11.cse_temp = (%10.cse_temp + uint32 29)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%11.cse_temp value:%g
+	    // CHECK: ty:uint32 %12.cse_temp = (%11.cse_temp + uint32 30)
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:%12.cse_temp value:%h
+	    // CHECK: writebuffer buffer:%abi_encoded.temp.70 offset:(%12.cse_temp + uint32 31) value:%i
+	    // CHECK: ty:bytes %k = %abi_encoded.temp.70
+
+
+        return k;
+    }
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::encodeStringsAndBytes
+    function encodeStringsAndBytes() public pure returns (bytes memory) {
+        string memory a = "coffe_is_tastier_than_tea";
+        bytes memory b = "who_said_tea_is_better?";
+        bytes memory c = abi.encode(a, b);
+        // CHECK: ty:bytes %abi_encoded.temp.83 = (alloc bytes len (((builtin ArrayLength (%a)) + uint32 4) + ((builtin ArrayLength (%b)) + uint32 4)))
+        // CHECK: ty:uint32 %temp.84 = (builtin ArrayLength (%a))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.83 offset:uint32 0 value:%temp.84
+        // CHECK: memcpy src: %a, dest: (advance ptr: %abi_encoded.temp.83, by: (uint32 0 + uint32 4)), bytes_len: %temp.84
+        // CHECK: ty:uint32 %temp.85 = (builtin ArrayLength (%b))
+        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + (%temp.84 + uint32 4))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.83 offset:%1.cse_temp value:%temp.85
+        // CHECK: memcpy src: %b, dest: (advance ptr: %abi_encoded.temp.83, by: (%1.cse_temp + uint32 4)), bytes_len: %temp.85
+        // CHECK: ty:bytes %c = %abi_encoded.temp.83
+        return c;
+    }
+
+    enum WeekDays {
+        sunday, monday, tuesday, wednesday, thursday, friday, saturday
+    }
+    
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::encodeEnum
+    function encodeEnum() public pure returns (bytes memory) {
+        WeekDays[3] memory vec = [WeekDays.sunday, WeekDays.tuesday, WeekDays.friday];
+        WeekDays elem = WeekDays.saturday;
+        bytes memory b = abi.encode(WeekDays.sunday, elem, vec[2]);
+        // CHECK: ty:bytes %abi_encoded.temp.88 = (alloc bytes len ((uint32 1 + uint32 1) + uint32 1))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.88 offset:uint32 0 value:enum EncodingTest.WeekDays 0
+        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + uint32 1)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.88 offset:%1.cse_temp value:%elem
+        // CHECK: writebuffer buffer:%abi_encoded.temp.88 offset:(%1.cse_temp + uint32 1) value:(load (subscript enum EncodingTest.WeekDays[3] %vec[%index.temp.87]))
+        // CHECK: ty:bytes %b = %abi_encoded.temp.88
+        return b;
+    }
+
+    struct noPadStruct {
+        uint32 a;
+        uint32 b;
+    }
+
+    struct PaddedStruct {
+        uint128 a;
+        uint8 b;
+        bytes32 c;
+    }
+
+    noPadStruct[] test_vec_1;
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::encodeStruct
+    function encodeStruct() public view returns (bytes memory) {
+        PaddedStruct memory ss = PaddedStruct(1, 3, "there_is_padding_here");
+        bytes memory b = abi.encode(test_vec_1[2], ss);
+        // CHECK: %temp.91 = load storage slot((subscript struct EncodingTest.noPadStruct[] storage uint32 16[uint32 2])) ty:struct EncodingTest.noPadStruct
+        // CHECK: ty:bytes %abi_encoded.temp.92 = (alloc bytes len (uint32 8 + uint32 49))
+        // CHECK: memcpy src: %temp.91, dest: (advance ptr: %abi_encoded.temp.92, by: uint32 0), bytes_len: uint32 8
+        // CHECK: ty:uint32 %1.cse_temp = (uint32 0 + uint32 8)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.92 offset:%1.cse_temp value:(load (struct %ss field 0))
+        // CHECK: ty:uint32 %2.cse_temp = (%1.cse_temp + uint32 16)
+        // CHECK: writebuffer buffer:%abi_encoded.temp.92 offset:%2.cse_temp value:(load (struct %ss field 1))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.92 offset:(%2.cse_temp + uint32 1) value:(load (struct %ss field 2))
+        // CHECK: ty:bytes %b = %abi_encoded.temp.92
+        return b;
+    }
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::primitiveStruct
+    function primitiveStruct() public view returns (bytes memory) {
+        uint32[4] memory mem_vec = [uint32(1), 2, 3, 4];
+        noPadStruct[2] memory str_vec = [noPadStruct(1,2), noPadStruct(3, 4)];
+        bytes memory b1 = abi.encode(test_vec_1, mem_vec, str_vec);
+        // CHECK: %temp.95 = load storage slot(uint32 16) ty:struct EncodingTest.noPadStruct[]
+	    // CHECK: ty:uint32 %temp.96 = ((builtin ArrayLength (%temp.95)) * uint32 8)
+	    // CHECK: ty:uint32 %temp.96 = (%temp.96 + uint32 4)
+	    // CHECK: ty:uint32 %temp.97 = uint32 16
+	    // CHECK: ty:uint32 %temp.98 = uint32 16
+	    // CHECK: ty:bytes %abi_encoded.temp.99 = (alloc bytes len ((%temp.96 + %temp.97) + %temp.98))
+        // CHECK: ty:uint32 %temp.100 = uint32 0
+        // CHECK: writebuffer buffer:%abi_encoded.temp.99 offset:%temp.100 value:(builtin ArrayLength (%temp.95))
+
+        // CHECK: ty:uint32 %temp.100 = uint32 4
+        // CHECK: ty:uint32 %for_i_0.temp.101 = uint32 0
+        // CHECK: branch block1
+
+        // CHECK: block1: # cond
+        // CHECK: branchcond (unsigned less %for_i_0.temp.101 < (builtin ArrayLength (%temp.95))), block3, block4
+
+        // CHECK: block2: # next
+        // CHECK: ty:uint32 %for_i_0.temp.101 = (%for_i_0.temp.101 + uint32 1)
+        // CHECK: branch block1
+
+        // CHECK: block3: # body
+        // CHECK: memcpy src: (subscript struct EncodingTest.noPadStruct[] %temp.95[%for_i_0.temp.101]), dest: (advance ptr: %abi_encoded.temp.99, by: %temp.100), bytes_len: uint32 8
+        // CHECK: ty:uint32 %temp.100 = (uint32 8 + %temp.100)
+        // CHECK: branch block2
+        
+        // CHECK: block4: # end_for
+        // CHECK: ty:uint32 %temp.100 = (%temp.100 - uint32 0)
+        // CHECK: memcpy src: %mem_vec, dest: (advance ptr: %abi_encoded.temp.99, by: (uint32 0 + %temp.100)), bytes_len: uint32 16
+        // CHECK: ty:uint32 %2.cse_temp = ((uint32 0 + %temp.100) + uint32 16)
+        // CHECK: ty:uint32 %temp.102 = %2.cse_temp
+        // CHECK: ty:uint32 %for_i_0.temp.103 = uint32 0
+        // CHECK: branch block5
+
+        // CHECK: block5: # cond
+        // CHECK: branchcond (unsigned less %for_i_0.temp.103 < uint32 2), block7, block8
+
+        // CHECK: block6: # next
+        // CHECK: ty:uint32 %for_i_0.temp.103 = (%for_i_0.temp.103 + uint32 1)
+        // CHECK: branch block5
+
+        // CHECK: block7: # body
+        // CHECK: memcpy src: (subscript struct EncodingTest.noPadStruct[2] %str_vec[%for_i_0.temp.103]), dest: (advance ptr: %abi_encoded.temp.99, by: %temp.102), bytes_len: uint32 8
+        // CHECK: ty:uint32 %temp.102 = (uint32 8 + %temp.102)
+        // CHECK: branch block6
+
+        // CHECK: block8: # end_for
+        // CHECK: ty:uint32 %temp.102 = (%temp.102 - %2.cse_temp)
+        // CHECK: ty:bytes %b1 = %abi_encoded.temp.99
+        // CHECK: return %b1
+        return b1;
+    }
+
+    function doThis(int64 a, int64 b) public pure returns (int64) {
+        return a+b;
+    }
+
+    // BEGIN-CHECK: EncodingTest::EncodingTest::function::externalFunction
+    function externalFunction() public view returns (bytes memory) {
+        function (int64, int64) external returns (int64) fPtr = this.doThis;
+        uint64 pr = 9234;
+
+        // CHECK: ty:bytes %abi_encoded.temp.106 = (alloc bytes len (uint32 36 + uint32 8))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.106 offset:uint32 0 value:(builtin FunctionSelector (%fPtr))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.106 offset:(uint32 0 + uint32 4) value:(builtin ExternalFunctionAddress (%fPtr))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.106 offset:(uint32 0 + uint32 36) value:%pr
+
+        bytes memory b = abi.encode(fPtr, pr);
+        return b;
+    }
+}

+ 23 - 12
tests/codegen_testcases/solidity/common_subexpression_elimination.sol

@@ -95,13 +95,19 @@ contract c1 {
         x = a+b-54;
         int d = x*(a+b);
         int p = x+d;
-        // CHECK: ty:int256 %2.cse_temp = (%x + %d)
-        // CHECK:branchcond (signed more %2.cse_temp > int256 0), block2, block3
+        // CHECK: ty:int256 %2.cse_temp = ((arg #0) + (arg #1))
+        // CHECK: ty:int256 %1.cse_temp = (%2.cse_temp - int256 54)
+        // CHECK: ty:int256 %x = %1.cse_temp
+	    // CHECK: ty:int256 %d = (%1.cse_temp * %2.cse_temp)
+	    // CHECK: ty:int256 %p = (%1.cse_temp + %d)
+
+        // CHECK: ty:int256 %3.cse_temp = (%x + %d)
+        // CHECK: branchcond (signed more %3.cse_temp > int256 0), block2, block3
         while (x+d > 0) {
             // CHECK: ty:int256 %t = ((arg #0) - (arg #1))
             int t = a-b;
             bool e1 = t > 3;
-            // CHECK: ty:int256 %x = %2.cse_temp
+            // CHECK: ty:int256 %x = %3.cse_temp
 			x = x+d;
         }
 
@@ -118,7 +124,7 @@ contract c1 {
        	for(int i=0; i<10; i++) {
             // CHECK: ty:int256 %t = ((arg #0) - (arg #1))
 			int t = a-b;
-            // CHECK: ty:int256 %i = (%temp.186 + int256 1)
+            // CHECK: ty:int256 %i = (%temp.187 + int256 1)
 			bool e1 = t > 3;
 		}
 
@@ -201,9 +207,11 @@ contract c1 {
         int r2= int(p2+9) -9;
         // CHECK: ty:int256 %r2 = (%2.cse_temp - int256 9)
 
+        // CHECK: ty:int256 %3.cse_temp = -%r1
+        // CHECK: ty:int256 %ret = %3.cse_temp
         ret = -r1;
 
-        // CHECK: ty:int256 %ret = (%ret + %r2)
+        // CHECK: ty:int256 %ret = (%3.cse_temp + %r2)
         ret = -r1 + r2;
     }
 
@@ -439,15 +447,18 @@ contract c1 {
     function test15(uint a, uint b) public pure returns (uint) {
         uint c = a << b;
         bool b1 = c > 0;
-        // CHECK: ty:bool %1.cse_temp = !%b1
-        // CHECK: branchcond %1.cse_temp, block1, block2
+        // CHECK: ty:uint256 %1.cse_temp = ((arg #0) << (arg #1))
+	    // CHECK: ty:uint256 %c = %1.cse_temp
+	    // CHECK: ty:bool %b1 = (unsigned more %1.cse_temp > uint256 0)
+	    // CHECK: ty:bool %2.cse_temp = !%b1
+	    // CHECK: branchcond %2.cse_temp, block1, block2
         if (!b1) {
-            // CHECK: return (%c + uint256 1)
+            // CHECK: return (%1.cse_temp + uint256 1)
             return (a << b) + 1;
         }
 
-        // CHECK: ty:uint256 %2.cse_temp = ((arg #0) & (arg #1))
-        // CHECK: branchcond %1.cse_temp, block4, block3
+        // CHECK: ty:uint256 %3.cse_temp = ((arg #0) & (arg #1))
+        // CHECK: branchcond %2.cse_temp, block4, block3
         if(!b1 || c > 0) {
             // CHECK: = %b1
             // CHECK: return ((arg #0) << ((arg #1) + uint256 1))
@@ -459,12 +470,12 @@ contract c1 {
             c++;
         }
 
-        // CHECK: branchcond (%2.cse_temp == uint256 0), block13, block14
+        // CHECK: branchcond (%3.cse_temp == uint256 0), block13, block14
         if (a & b == 0) {
             return c--;
         }
 
-        // CHECK: branchcond (unsigned more %2.cse_temp > uint256 1), block15, block16
+        // CHECK: branchcond (unsigned more %3.cse_temp > uint256 1), block15, block16
         if (a & b > 1) {
             return a;
         }

+ 818 - 0
tests/solana_tests/abi_encode.rs

@@ -0,0 +1,818 @@
+use crate::build_solidity;
+use borsh::BorshDeserialize;
+use ethabi::Token;
+
+#[test]
+fn integers_bool_enum() {
+    #[derive(BorshDeserialize, PartialEq, Debug)]
+    enum WeekDay {
+        Sunday,
+        Monday,
+        Tuesday,
+        Wednesday,
+        Thursday,
+        Friday,
+        Saturday,
+    }
+
+    #[derive(BorshDeserialize, Debug)]
+    struct Res1 {
+        a: u8,
+        b: u64,
+        c: u128,
+        d: i16,
+        e: i32,
+        day: WeekDay,
+        h: bool,
+    }
+
+    #[derive(BorshDeserialize, Debug)]
+    struct Res2 {
+        sunday: WeekDay,
+        elem: WeekDay,
+        vec_2: WeekDay,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+contract Testing {
+    enum weekday{
+        sunday, monday, tuesday, wednesday, thursday, friday, saturday
+    }
+
+    function getThis() public pure returns (bytes memory) {
+        uint8 a = 45;
+        uint64 b = 9965956609890;
+        uint128 c = 88;
+
+        int16 d = -29;
+        int32 e = -88;
+
+        weekday f = weekday.wednesday;
+        bool h = false;
+        bytes memory g = abi.encode(a, b, c, d, e, f, h);
+        return g;
+    }
+
+    function encodeEnum() public pure returns (bytes memory) {
+        weekday[3] memory vec = [weekday.sunday, weekday.tuesday, weekday.friday];
+        weekday elem = weekday.saturday;
+        bytes memory b = abi.encode(weekday.sunday, elem, vec[2]);
+        return b;
+    }
+}
+
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("getThis", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res1::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.a, 45);
+    assert_eq!(decoded.b, 9965956609890);
+    assert_eq!(decoded.c, 88);
+    assert_eq!(decoded.d, -29);
+    assert_eq!(decoded.e, -88);
+    assert_eq!(decoded.day, WeekDay::Wednesday);
+    assert!(!decoded.h);
+
+    let returns = vm.function("encodeEnum", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res2::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.sunday, WeekDay::Sunday);
+    assert_eq!(decoded.elem, WeekDay::Saturday);
+    assert_eq!(decoded.vec_2, WeekDay::Friday);
+}
+
+#[test]
+fn encode_address() {
+    #[derive(BorshDeserialize, Debug)]
+    struct Response {
+        address: [u8; 32],
+        this: [u8; 32],
+    }
+
+    let mut vm = build_solidity(
+        r#"
+contract Testing {
+
+    function getThis() public view returns (bytes memory) {
+        bytes memory b = abi.encode(address(this), this);
+        return b;
+    }
+}
+        "#,
+    );
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("getThis", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Response::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.address, vm.programs[0].data);
+    assert_eq!(decoded.this, vm.programs[0].data);
+}
+
+#[test]
+fn string_and_bytes() {
+    #[derive(BorshDeserialize, Debug)]
+    struct MyStruct {
+        a: String,
+        b: Vec<u8>,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+contract Testing {
+
+    function getThis() public pure returns (bytes memory) {
+        string memory a = "coffe";
+        bytes memory b = "tea";
+        bytes memory c = abi.encode(a, b);
+        return c;
+    }
+}
+      "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("getThis", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = MyStruct::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.a, "coffe");
+    assert_eq!(decoded.b, b"tea");
+}
+
+#[test]
+fn primitive_structs() {
+    #[derive(Debug, BorshDeserialize)]
+    struct NoPadStruct {
+        a: u32,
+        b: u32,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct PaddedStruct {
+        a: u128,
+        b: u8,
+        c: [u8; 32],
+    }
+
+    let mut vm = build_solidity(
+        r#"
+        contract Testing {
+
+    struct noPadStruct {
+        uint32 a;
+        uint32 b;
+    }
+
+    struct PaddedStruct {
+        uint128 a;
+        uint8 b;
+        bytes32 c;
+    }
+
+    function getThis() public pure returns (bytes memory) {
+        noPadStruct memory a = noPadStruct(1238, 87123);
+        bytes memory b = abi.encode(a);
+        return b;
+    }
+
+    function getThat() public pure returns (bytes memory) {
+        PaddedStruct memory a = PaddedStruct(12998, 240, "tea_is_good");
+        bytes memory b = abi.encode(a);
+        return b;
+    }
+}
+        "#,
+    );
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("getThis", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = NoPadStruct::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.a, 1238);
+    assert_eq!(decoded.b, 87123);
+
+    let returns = vm.function("getThat", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = PaddedStruct::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.a, 12998);
+    assert_eq!(decoded.b, 240);
+    let mut b: [u8; 11] = b"tea_is_good".to_owned();
+    b.reverse();
+    assert_eq!(&decoded.c[21..32], b);
+}
+
+#[test]
+fn argument_string() {
+    #[derive(Debug, BorshDeserialize)]
+    struct Response {
+        rr: String,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+contract Testing {
+
+    function testStruct(string memory rr) public pure returns (bytes memory) {
+        bytes memory b1 = abi.encode(rr);
+        return b1;
+    }
+}
+      "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function(
+        "testStruct",
+        &[Token::String("nihao".to_string())],
+        &[],
+        None,
+    );
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Response::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.rr, "nihao");
+}
+
+#[test]
+fn test_string_array() {
+    #[derive(Debug, BorshDeserialize)]
+    struct Response {
+        a: Vec<String>,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+        contract Testing {
+            string[] string_vec;
+            function encode() public view returns (bytes memory) {
+                string[] memory mem_vec = string_vec;
+                bytes memory b = abi.encode(mem_vec);
+                return b;
+            }
+
+            function insertStrings() public {
+                string_vec.push("tea");
+                string_vec.push("coffee");
+            }
+        }
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("encode", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Response::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.a.len(), 0);
+
+    let _ = vm.function("insertStrings", &[], &[], None);
+    let returns = vm.function("encode", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Response::try_from_slice(&encoded).unwrap();
+    assert_eq!(decoded.a.len(), 2);
+    assert_eq!(decoded.a[0], "tea");
+    assert_eq!(decoded.a[1], "coffee");
+}
+
+#[test]
+fn struct_within_struct() {
+    #[derive(Debug, BorshDeserialize)]
+    struct NoPadStruct {
+        a: u32,
+        b: u32,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct PaddedStruct {
+        a: u128,
+        b: u8,
+        c: [u8; 32],
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct NonConstantStruct {
+        a: u64,
+        b: Vec<String>,
+        no_pad: NoPadStruct,
+        pad: PaddedStruct,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+contract Testing {
+
+  struct noPadStruct {
+        uint32 a;
+        uint32 b;
+    }
+
+    struct PaddedStruct {
+        uint128 a;
+        uint8 b;
+        bytes32 c;
+    }
+
+    struct NonConstantStruct {
+        uint64 a;
+        string[] b;
+        noPadStruct noPad;
+        PaddedStruct pad;
+    }
+
+    string[] string_vec;
+    NonConstantStruct to_encode;
+    function testStruct() public returns (bytes memory) {
+        noPadStruct memory noPad = noPadStruct(89123, 12354);
+        PaddedStruct memory padded = PaddedStruct(988834, 129, "tea_is_good");
+        string_vec.push("tea");
+        string_vec.push("coffee");
+
+        to_encode = NonConstantStruct(890234, string_vec, noPad, padded);
+
+        bytes memory b1 = abi.encode(to_encode);
+        return b1;
+    }
+}
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("testStruct", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = NonConstantStruct::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.a, 890234);
+    assert_eq!(decoded.b.len(), 2);
+    assert_eq!(decoded.b[0], "tea");
+    assert_eq!(decoded.b[1], "coffee");
+    assert_eq!(decoded.no_pad.a, 89123);
+    assert_eq!(decoded.no_pad.b, 12354);
+    assert_eq!(decoded.pad.a, 988834);
+    assert_eq!(decoded.pad.b, 129);
+    let mut b: [u8; 11] = b"tea_is_good".to_owned();
+    b.reverse();
+    assert_eq!(&decoded.pad.c[21..32], b);
+}
+
+#[test]
+fn struct_in_array() {
+    #[derive(Debug, BorshDeserialize, PartialEq, Copy, Default, Clone)]
+    struct NoPadStruct {
+        a: u32,
+        b: u32,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct PaddedStruct {
+        a: u128,
+        b: u8,
+        c: [u8; 32],
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res1 {
+        item_1: NoPadStruct,
+        item_2: PaddedStruct,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res2 {
+        item_1: Vec<NoPadStruct>,
+        item_2: [i32; 4],
+        item_3: [NoPadStruct; 2],
+    }
+
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+
+        struct noPadStruct {
+            uint32 a;
+            uint32 b;
+        }
+
+        struct PaddedStruct {
+            uint128 a;
+            uint8 b;
+            bytes32 c;
+        }
+
+        noPadStruct[] test_vec_1;
+
+        function addData() public  {
+            noPadStruct memory mm = noPadStruct(1623, 43279);
+            test_vec_1.push(mm);
+            mm.a = 41234;
+            mm.b = 98375;
+            test_vec_1.push(mm);
+            mm.a = 945;
+            mm.b = 7453;
+            test_vec_1.push(mm);
+        }
+
+
+        function encodeStruct() public view returns (bytes memory) {
+            PaddedStruct memory ss = PaddedStruct(1, 3, "there_is_padding_here");
+            bytes memory b = abi.encode(test_vec_1[2], ss);
+            return b;
+        }
+
+        function primitiveStruct() public view returns (bytes memory) {
+            int32[4] memory mem_vec = [int32(1), -298, 3, -434];
+            noPadStruct[2] memory str_vec = [noPadStruct(1,2), noPadStruct(3, 4)];
+            bytes memory b1 = abi.encode(test_vec_1, mem_vec, str_vec);
+            return b1;
+        }
+    }
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let _ = vm.function("addData", &[], &[], None);
+    let returns = vm.function("encodeStruct", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res1::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item_1.a, 945);
+    assert_eq!(decoded.item_1.b, 7453);
+    assert_eq!(decoded.item_2.a, 1);
+    assert_eq!(decoded.item_2.b, 3);
+    let mut b: [u8; 21] = b"there_is_padding_here".to_owned();
+    b.reverse();
+    assert_eq!(&decoded.item_2.c[11..32], b);
+
+    let returns = vm.function("primitiveStruct", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res2::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item_1.len(), 3);
+    assert_eq!(decoded.item_1[0], NoPadStruct { a: 1623, b: 43279 });
+    assert_eq!(decoded.item_1[1], NoPadStruct { a: 41234, b: 98375 });
+    assert_eq!(decoded.item_1[2], NoPadStruct { a: 945, b: 7453 });
+    assert_eq!(decoded.item_2, [1, -298, 3, -434]);
+    assert_eq!(decoded.item_3[0], NoPadStruct { a: 1, b: 2 });
+    assert_eq!(decoded.item_3[1], NoPadStruct { a: 3, b: 4 });
+}
+
+#[test]
+fn arrays() {
+    #[derive(Debug, BorshDeserialize)]
+    struct Res1 {
+        vec_1: Vec<i16>,
+    }
+
+    #[derive(Debug, BorshDeserialize, Default, Clone)]
+    struct NonConstantStruct {
+        a: u64,
+        b: Vec<String>,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res2 {
+        complex_array: Vec<NonConstantStruct>,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res3 {
+        multi_dim: [[i8; 2]; 3],
+    }
+
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+        int16[] vec_1;
+        function addData() public {
+            vec_1.push(-90);
+            vec_1.push(5523);
+            vec_1.push(-89);
+        }
+
+        struct NonConstantStruct {
+            uint64 a;
+            string[] b;
+        }
+
+        function encodeComplex() public returns (bytes memory) {
+            string[] vec_2 = new string[](2);
+            vec_2[0] = "tea";
+            vec_2[1] = "coffee";
+            NonConstantStruct[] arr = new NonConstantStruct[](2);
+            arr[0] = NonConstantStruct(897, vec_2);
+
+            string[] vec_3 = new string[](2);
+            vec_3[0] = "cortado";
+            vec_3[1] = "cappuccino";
+            arr[1] = NonConstantStruct(74123, vec_3);
+            return abi.encode(arr);
+        }
+
+        function encodeArray() public view returns (bytes memory) {
+            bytes memory b = abi.encode(vec_1);
+            return b;
+        }
+
+        function multiDimArrays() public pure returns (bytes memory) {
+            int8[2][3] memory vec = [[int8(1), 2], [int8(4), 5], [int8(6), 7]];
+            bytes memory b = abi.encode(vec);
+            return b;
+        }
+    }
+      "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let _ = vm.function("addData", &[], &[], None);
+    let returns = vm.function("encodeArray", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res1::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.vec_1.len(), 3);
+    assert_eq!(decoded.vec_1[0], -90);
+    assert_eq!(decoded.vec_1[1], 5523);
+    assert_eq!(decoded.vec_1[2], -89);
+
+    let returns = vm.function("encodeComplex", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res2::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.complex_array.len(), 2);
+    assert_eq!(decoded.complex_array[0].a, 897);
+    assert_eq!(
+        decoded.complex_array[0].b,
+        vec!["tea".to_string(), "coffee".to_string()]
+    );
+    assert_eq!(decoded.complex_array[1].a, 74123);
+    assert_eq!(
+        decoded.complex_array[1].b,
+        vec!["cortado".to_string(), "cappuccino".to_string()]
+    );
+
+    let returns = vm.function("multiDimArrays", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res3::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.multi_dim[0], [1, 2]);
+    assert_eq!(decoded.multi_dim[1], [4, 5]);
+    assert_eq!(decoded.multi_dim[2], [6, 7]);
+}
+
+#[test]
+fn multi_dimensional_array() {
+    #[derive(Debug, BorshDeserialize, Default, Copy, Clone, PartialEq)]
+    struct PaddedStruct {
+        a: u128,
+        b: u8,
+        c: [u8; 32],
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res1 {
+        item_1: Vec<[[PaddedStruct; 2]; 3]>,
+        item_2: u16,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res2 {
+        item: Vec<[[u16; 4]; 2]>,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res3 {
+        item: Vec<u16>,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+contract Testing {
+
+    struct PaddedStruct {
+        uint128 a;
+        uint8 b;
+        bytes32 c;
+    }
+
+    function getThis() public pure returns (bytes memory) {
+        PaddedStruct memory a = PaddedStruct(56, 1, "oi");
+        PaddedStruct memory b = PaddedStruct(78, 6, "bc");
+        PaddedStruct memory c = PaddedStruct(89, 4, "sn");
+        PaddedStruct memory d = PaddedStruct(42, 56, "cn");
+        PaddedStruct memory e = PaddedStruct(23, 78, "fr");
+        PaddedStruct memory f = PaddedStruct(445, 46, "br");
+
+        PaddedStruct[2][3] memory vec = [[a, b], [c, d], [e, f]];
+
+        PaddedStruct[2][3][] memory arr2 = new PaddedStruct[2][3][](1);
+        arr2[0] = vec;
+
+        uint16 g = 5;
+        bytes memory b1 = abi.encode(arr2, g);
+        return b1;
+    }
+
+    function multiDim() public pure returns (bytes memory) {
+        uint16[4][2] memory vec = [[uint16(1), 2, 3, 4], [uint16(5), 6, 7, 8]];
+
+        uint16[4][2][] memory simple_arr = new uint16[4][2][](1);
+        simple_arr[0] = vec;
+
+        bytes memory b = abi.encode(simple_arr);
+        return b;
+    }
+
+    function uniqueDim() public pure returns (bytes memory) {
+        uint16[] memory vec = new uint16[](5);
+        vec[0] = 9;
+        vec[1] = 3;
+        vec[2] = 4;
+        vec[3] = 90;
+        vec[4] = 834;
+        bytes memory b = abi.encode(vec);
+        return b;
+    }
+}
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("getThis", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res1::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item_1.len(), 1);
+    let mut res1_c: Vec<u8> = Vec::new();
+    res1_c.resize(32, 0);
+
+    assert_eq!(
+        decoded.item_1[0][0][0],
+        PaddedStruct {
+            a: 56,
+            b: 1,
+            c: create_response(&mut res1_c, b"oi")
+        }
+    );
+    assert_eq!(
+        decoded.item_1[0][0][1],
+        PaddedStruct {
+            a: 78,
+            b: 6,
+            c: create_response(&mut res1_c, b"bc")
+        }
+    );
+    assert_eq!(
+        decoded.item_1[0][1][0],
+        PaddedStruct {
+            a: 89,
+            b: 4,
+            c: create_response(&mut res1_c, b"sn")
+        }
+    );
+    assert_eq!(
+        decoded.item_1[0][1][1],
+        PaddedStruct {
+            a: 42,
+            b: 56,
+            c: create_response(&mut res1_c, b"cn")
+        }
+    );
+    assert_eq!(
+        decoded.item_1[0][2][0],
+        PaddedStruct {
+            a: 23,
+            b: 78,
+            c: create_response(&mut res1_c, b"fr")
+        }
+    );
+    assert_eq!(
+        decoded.item_1[0][2][1],
+        PaddedStruct {
+            a: 445,
+            b: 46,
+            c: create_response(&mut res1_c, b"br")
+        }
+    );
+    assert_eq!(decoded.item_2, 5);
+
+    let returns = vm.function("multiDim", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res2::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item.len(), 1);
+    assert_eq!(decoded.item[0][0], [1, 2, 3, 4]);
+    assert_eq!(decoded.item[0][1], [5, 6, 7, 8]);
+
+    let returns = vm.function("uniqueDim", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res3::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item.len(), 5);
+    assert_eq!(decoded.item, vec![9, 3, 4, 90, 834]);
+}
+
+fn create_response(vec: &mut [u8], string: &[u8; 2]) -> [u8; 32] {
+    vec[30] = string[1];
+    vec[31] = string[0];
+    <[u8; 32]>::try_from(vec.to_owned()).unwrap()
+}
+
+#[test]
+fn null_pointer() {
+    #[derive(Debug, BorshDeserialize)]
+    struct S {
+        f1: i64,
+        f2: String,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res1 {
+        item: Vec<S>,
+    }
+
+    #[derive(Debug, BorshDeserialize)]
+    struct Res2 {
+        item: Vec<String>,
+    }
+
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+
+        struct S {
+            int64 f1;
+            string f2;
+        }
+
+        function test1() public pure returns (bytes memory) {
+            S[] memory s = new S[](5);
+            return abi.encode(s);
+        }
+
+        function test2() public pure returns (bytes memory) {
+            string[] memory x = new string[](5);
+            return abi.encode(x);
+        }
+    }
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("test1", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res1::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item.len(), 5);
+    for i in 0..5 {
+        assert_eq!(decoded.item[i].f1, 0);
+        assert!(decoded.item[i].f2.is_empty())
+    }
+
+    let returns = vm.function("test2", &[], &[], None);
+    let encoded = returns[0].clone().into_bytes().unwrap();
+    let decoded = Res2::try_from_slice(&encoded).unwrap();
+
+    assert_eq!(decoded.item.len(), 5);
+
+    for i in 0..5 {
+        assert!(decoded.item[i].is_empty());
+    }
+}
+
+#[test]
+fn external_function() {
+    #[derive(Debug, BorshDeserialize)]
+    struct Res {
+        item_1: [u8; 4],
+        item_2: [u8; 32],
+    }
+
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+        function doThis(int64 a, int64 b) public pure returns (int64) {
+            return a+b;
+        }
+
+        function doThat() public view returns (bytes4, address, bytes memory) {
+            function (int64, int64) external returns (int64) fPtr = this.doThis;
+
+            bytes memory b = abi.encode(fPtr);
+            return (fPtr.selector, fPtr.address, b);
+        }
+    }
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+
+    let returns = vm.function("doThat", &[], &[], None);
+    let encoded = returns[2].clone().into_bytes().unwrap();
+    let decoded = Res::try_from_slice(&encoded).unwrap();
+
+    let mut selector = returns[0].clone().into_fixed_bytes().unwrap();
+    selector.reverse();
+    let address = returns[1].clone().into_fixed_bytes().unwrap();
+
+    assert_eq!(decoded.item_1, &selector[..]);
+    assert_eq!(decoded.item_2, &address[..]);
+}

+ 1 - 0
tests/solana_tests/mod.rs

@@ -1,4 +1,5 @@
 mod abi;
+mod abi_encode;
 mod accessor;
 mod account_info;
 mod arrays;