diff --git a/src/codegen/encoding/soroban_encoding.rs b/src/codegen/encoding/soroban_encoding.rs index e3a210692..7309e0dc2 100644 --- a/src/codegen/encoding/soroban_encoding.rs +++ b/src/codegen/encoding/soroban_encoding.rs @@ -142,7 +142,7 @@ pub fn soroban_decode_arg( }, Type::Uint(64) => decode_u64(wrapper_cfg, vartab, arg), - Type::Address(_) | Type::String => arg.clone(), + Type::Address(_) | Type::String | Type::DynamicBytes => arg.clone(), Type::Enum(enum_no) => { let decoded = soroban_decode_arg(arg, wrapper_cfg, vartab, ns, Some(Type::Uint(32))); @@ -721,6 +721,11 @@ pub fn soroban_encode_arg( res: obj, expr: encode_vector(item.clone(), cfg, vartab), }, + Type::DynamicBytes => Instr::Set { + loc: Loc::Codegen, + res: obj, + expr: item.clone(), + }, _ => todo!("Type not yet supported in soroban encoder: {:?}", item.ty()), }; diff --git a/src/emit/binary.rs b/src/emit/binary.rs index fa28f823d..84b59cd95 100644 --- a/src/emit/binary.rs +++ b/src/emit/binary.rs @@ -888,11 +888,8 @@ impl<'a> Binary<'a> { fn var_ty_uses_pointer_storage(&self, ty: &Type) -> bool { match ty.deref_memory() { - Type::Struct(_) - | Type::Array(..) - | Type::DynamicBytes - | Type::ExternalFunction { .. } => true, - Type::String => self.ns.target != Target::Soroban, + Type::Struct(_) | Type::Array(..) | Type::ExternalFunction { .. } => true, + Type::String | Type::DynamicBytes => self.ns.target != Target::Soroban, _ => false, } } diff --git a/src/emit/soroban/mod.rs b/src/emit/soroban/mod.rs index 542a2626c..c8d0b3881 100644 --- a/src/emit/soroban/mod.rs +++ b/src/emit/soroban/mod.rs @@ -338,7 +338,9 @@ impl SorobanTarget { ast::Type::Uint(256) => ScSpecTypeDef::U256, ast::Type::Bool => ScSpecTypeDef::Bool, ast::Type::Address(_) => ScSpecTypeDef::Address, - ast::Type::Bytes(_) => ScSpecTypeDef::Bytes, + ast::Type::Bytes(_) | ast::Type::DynamicBytes => { + ScSpecTypeDef::Bytes + } ast::Type::String => ScSpecTypeDef::String, ast::Type::Array(ty, _) => { let element = Self::vec_spec_type(ty.as_ref()); @@ -377,7 +379,7 @@ impl SorobanTarget { ast::Type::Int(_) => ScSpecTypeDef::I32, ast::Type::Bool => ScSpecTypeDef::Bool, ast::Type::Address(_) => ScSpecTypeDef::Address, - ast::Type::Bytes(_) => ScSpecTypeDef::Bytes, + ast::Type::Bytes(_) | ast::Type::DynamicBytes => ScSpecTypeDef::Bytes, ast::Type::String => ScSpecTypeDef::String, ast::Type::Void => ScSpecTypeDef::Void, ast::Type::Struct(_) => ScSpecTypeDef::Void, // TODO: Map struct types. diff --git a/tests/soroban_testcases/dynamic_bytes.rs b/tests/soroban_testcases/dynamic_bytes.rs new file mode 100644 index 000000000..070eeae75 --- /dev/null +++ b/tests/soroban_testcases/dynamic_bytes.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 + +use crate::build_solidity; +use soroban_sdk::{Bytes, FromVal, IntoVal}; + +#[test] +fn set_and_get_bytes() { + let src = build_solidity( + r#"contract BytesWriteStub { + bytes public data; + + function set_data(bytes memory d) public { + data = d; + } + }"#, + |_| {}, + ); + + let addr = src.contracts.last().unwrap(); + + // empty bytes round-trip + let empty = Bytes::from_slice(&src.env, b""); + src.invoke_contract(addr, "set_data", vec![empty.clone().into_val(&src.env)]); + let res = src.invoke_contract(addr, "data", vec![]); + assert_eq!(Bytes::from_val(&src.env, &res), empty); + + // non-empty bytes round-trip + let payload = Bytes::from_slice(&src.env, b"hello"); + src.invoke_contract(addr, "set_data", vec![payload.clone().into_val(&src.env)]); + let res = src.invoke_contract(addr, "data", vec![]); + assert_eq!(Bytes::from_val(&src.env, &res), payload); +} diff --git a/tests/soroban_testcases/mod.rs b/tests/soroban_testcases/mod.rs index cdd404e6f..755b67441 100644 --- a/tests/soroban_testcases/mod.rs +++ b/tests/soroban_testcases/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 mod alloc; mod array_args; +mod dynamic_bytes; mod atomic_swap; mod auth; mod constructor;