From 5798ffd1b0ad7fa4eb2fc6e5a1672f278950bd49 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 27 May 2025 13:21:53 +0000 Subject: [PATCH 1/7] fix: allow of returning of tuples from contract funcs --- .../aztec-nr/aztec/src/macros/utils.nr | 2 + noir-projects/noir-contracts/Nargo.toml | 1 + .../test/returning_tuple_contract/Nargo.toml | 8 ++++ .../test/returning_tuple_contract/src/main.nr | 43 +++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 noir-projects/noir-contracts/contracts/test/returning_tuple_contract/Nargo.toml create mode 100644 noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr diff --git a/noir-projects/aztec-nr/aztec/src/macros/utils.nr b/noir-projects/aztec-nr/aztec/src/macros/utils.nr index 9ea763d34ae6..384f0cc0647e 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/utils.nr @@ -111,6 +111,8 @@ pub(crate) comptime fn add_to_hasher(hasher_name: Quoted, name: Quoted, typ: Typ $hasher_name.add_multiple($serialized_name[i]); } } + } else if typ.as_tuple().is_some() { + // TODO: What do? } else if typ.as_str().is_some() { quote { $hasher_name.add_multiple($name.as_bytes().map(| byte: u8 | byte as Field)); diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index 9e6c33825e5a..5ff484e328d5 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -45,6 +45,7 @@ members = [ "contracts/test/note_getter_contract", "contracts/test/parent_contract", "contracts/test/pending_note_hashes_contract", + "contracts/test/returning_tuple_contract", "contracts/test/spam_contract", "contracts/test/state_vars_contract", "contracts/test/stateful_test_contract", diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/Nargo.toml new file mode 100644 index 000000000000..f66addd5cec8 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "returning_tuple_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../../aztec-nr/aztec" } diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr new file mode 100644 index 000000000000..5435ddc5444a --- /dev/null +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr @@ -0,0 +1,43 @@ +use aztec::macros::aztec; + +// Test that Aztec.nr handles returning tuples correctly. +#[aztec] +pub contract ReturningTuple { + use aztec::macros::functions::{private, view}; + + #[private] + #[view] + fn fn_that_returns_1() -> pub (Field) { + (1) + } + + #[private] + #[view] + fn fn_that_returns_2() -> pub (Field, Field) { + (1, 2) + } + + #[private] + #[view] + fn fn_that_returns_3() -> pub (Field, Field, Field) { + (1, 2, 3) + } + + #[private] + #[view] + fn fn_that_returns_4() -> pub (Field, Field, Field, Field) { + (1, 2, 3, 4) + } + + #[private] + #[view] + fn fn_that_returns_5() -> pub (Field, Field, Field, Field, Field) { + (1, 2, 3, 4, 5) + } + + #[private] + #[view] + fn fn_that_returns_6() -> pub (Field, Field, Field, Field, Field, Field) { + (1, 2, 3, 4, 5, 6) + } +} From ffda0d6652884afeac02fa3b9634cf7c54ac4e08 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 28 May 2025 10:31:56 +0000 Subject: [PATCH 2/7] works --- .../aztec-nr/aztec/src/macros/utils.nr | 8 ++++- .../test/returning_tuple_contract/src/main.nr | 30 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/utils.nr b/noir-projects/aztec-nr/aztec/src/macros/utils.nr index 384f0cc0647e..94b287733e07 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/utils.nr @@ -112,7 +112,13 @@ pub(crate) comptime fn add_to_hasher(hasher_name: Quoted, name: Quoted, typ: Typ } } } else if typ.as_tuple().is_some() { - // TODO: What do? + let tuple_len = typ.as_tuple().unwrap().len(); + let mut tuple_quotes: [Quoted] = []; + for i in 0..tuple_len { + let element_quote = quote { $hasher_name.add_multiple(dep::aztec::protocol_types::traits::Serialize::serialize($name.$i)); }; + tuple_quotes = tuple_quotes.push_back(element_quote); + } + tuple_quotes.join(quote {}) } else if typ.as_str().is_some() { quote { $hasher_name.add_multiple($name.as_bytes().map(| byte: u8 | byte as Field)); diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr index 5435ddc5444a..ef9f9f5d4cd4 100644 --- a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr @@ -1,43 +1,47 @@ use aztec::macros::aztec; -// Test that Aztec.nr handles returning tuples correctly. +// Tests that Aztec.nr handles returning tuples correctly. #[aztec] pub contract ReturningTuple { - use aztec::macros::functions::{private, view}; + use aztec::{ + macros::functions::{private, view}, + prelude::{AztecAddress, Point}, + protocol_types::traits::{Deserialize, FromField}, + }; #[private] #[view] - fn fn_that_returns_1() -> pub (Field) { - (1) + fn fn_that_returns_1() -> pub (bool) { + (true) } #[private] #[view] - fn fn_that_returns_2() -> pub (Field, Field) { + fn fn_that_returns_2() -> pub (Field, u32) { (1, 2) } #[private] #[view] - fn fn_that_returns_3() -> pub (Field, Field, Field) { - (1, 2, 3) + fn fn_that_returns_3() -> pub (Field, bool, str<4>) { + (1, true, "test") } #[private] #[view] - fn fn_that_returns_4() -> pub (Field, Field, Field, Field) { - (1, 2, 3, 4) + fn fn_that_returns_4() -> pub (Field, u64, bool, str<3>) { + (1, 2, false, "abc") } #[private] #[view] - fn fn_that_returns_5() -> pub (Field, Field, Field, Field, Field) { - (1, 2, 3, 4, 5) + fn fn_that_returns_5() -> pub (Field, u32, bool, str<2>, i64) { + (1, 2, true, "hi", -5) } #[private] #[view] - fn fn_that_returns_6() -> pub (Field, Field, Field, Field, Field, Field) { - (1, 2, 3, 4, 5, 6) + fn fn_that_returns_6() -> pub (Field, u128, bool, str<3>, AztecAddress, Point) { + (1, 2, false, "xyz", AztecAddress::from_field(1), Point::deserialize([1, 2, 3])) } } From dbd4d174852e42c82cb1a3d515132b07bc04ed0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Thu, 29 May 2025 10:29:22 +0200 Subject: [PATCH 3/7] Update noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- .../contracts/test/returning_tuple_contract/src/main.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr index ef9f9f5d4cd4..31762483206b 100644 --- a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr @@ -11,8 +11,8 @@ pub contract ReturningTuple { #[private] #[view] - fn fn_that_returns_1() -> pub (bool) { - (true) + fn fn_that_returns_1() -> pub (bool,) { + (true,) } #[private] From 9e95265270b0bd7d70d177d6af2d57e6a78ca4ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Thu, 29 May 2025 10:29:37 +0200 Subject: [PATCH 4/7] Update noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- .../contracts/test/returning_tuple_contract/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr index 31762483206b..d449d4e46539 100644 --- a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr @@ -17,7 +17,7 @@ pub contract ReturningTuple { #[private] #[view] - fn fn_that_returns_2() -> pub (Field, u32) { + fn fn_that_returns_2() -> (Field, u32) { (1, 2) } From 70c629e3b55cd848be31e0694708c8e865553d09 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 29 May 2025 08:36:35 +0000 Subject: [PATCH 5/7] removing redundant pub keyword --- .../test/returning_tuple_contract/src/main.nr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr index d449d4e46539..2612a23b0614 100644 --- a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr @@ -11,7 +11,7 @@ pub contract ReturningTuple { #[private] #[view] - fn fn_that_returns_1() -> pub (bool,) { + fn fn_that_returns_1() -> (bool,) { (true,) } @@ -23,25 +23,25 @@ pub contract ReturningTuple { #[private] #[view] - fn fn_that_returns_3() -> pub (Field, bool, str<4>) { + fn fn_that_returns_3() -> (Field, bool, str<4>) { (1, true, "test") } #[private] #[view] - fn fn_that_returns_4() -> pub (Field, u64, bool, str<3>) { + fn fn_that_returns_4() -> (Field, u64, bool, str<3>) { (1, 2, false, "abc") } #[private] #[view] - fn fn_that_returns_5() -> pub (Field, u32, bool, str<2>, i64) { + fn fn_that_returns_5() -> (Field, u32, bool, str<2>, i64) { (1, 2, true, "hi", -5) } #[private] #[view] - fn fn_that_returns_6() -> pub (Field, u128, bool, str<3>, AztecAddress, Point) { + fn fn_that_returns_6() -> (Field, u128, bool, str<3>, AztecAddress, Point) { (1, 2, false, "xyz", AztecAddress::from_field(1), Point::deserialize([1, 2, 3])) } } From acbffc3fe9d8dd2503f346157e07d9ee58289d15 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 29 May 2025 09:16:26 +0000 Subject: [PATCH 6/7] testing tuple value gets correctly decoded --- yarn-project/stdlib/src/abi/decoder.test.ts | 89 +++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/yarn-project/stdlib/src/abi/decoder.test.ts b/yarn-project/stdlib/src/abi/decoder.test.ts index ea19a4276cc1..d5ccbb7cf341 100644 --- a/yarn-project/stdlib/src/abi/decoder.test.ts +++ b/yarn-project/stdlib/src/abi/decoder.test.ts @@ -1,5 +1,6 @@ import { Fr } from '@aztec/foundation/fields'; +import { AztecAddress } from '../aztec-address/index.js'; import type { ABIParameterVisibility, FunctionArtifact } from './abi.js'; import { decodeFromAbi, decodeFunctionSignature, decodeFunctionSignatureWithParameterNames } from './decoder.js'; @@ -182,4 +183,92 @@ describe('decoder', () => { ); expect(decoded).toBe(2n ** 63n - 1n); }); + + it('decodes a tuple', () => { + // ABI copied from noir-projects/noir-contracts/target/returning_tuple_contract-ReturningTuple.json + const decoded = decodeFromAbi( + [ + { + kind: 'tuple', + fields: [ + { + kind: 'field', + }, + { + kind: 'integer', + sign: 'unsigned', + width: 128, + }, + { + kind: 'boolean', + }, + { + kind: 'string', + length: 3, + }, + { + kind: 'struct', + path: 'aztec::protocol_types::address::aztec_address::AztecAddress', + fields: [ + { + name: 'inner', + type: { + kind: 'field', + }, + }, + ], + }, + { + kind: 'struct', + path: 'std::embedded_curve_ops::EmbeddedCurvePoint', + fields: [ + { + name: 'x', + type: { + kind: 'field', + }, + }, + { + name: 'y', + type: { + kind: 'field', + }, + }, + { + name: 'is_infinite', + type: { + kind: 'boolean', + }, + }, + ], + }, + ], + }, + ], + [ + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')), // field + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000002', 'hex')), // u128 + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')), // bool + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000078', 'hex')), // "x" + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000079', 'hex')), // "y" + Fr.fromBuffer(Buffer.from('000000000000000000000000000000000000000000000000000000000000007a', 'hex')), // "z" + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')), // address + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')), // point.x + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000002', 'hex')), // point.y + Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')), // point.is_infinite + ], + ); + + expect(decoded).toEqual([ + 1n, + 2n, + false, + // TODO(#14600): correctly decode the string as string instead of 3 bigints + // 'xyz', + [120n, 121n, 122n], + AztecAddress.fromBigInt(1n), + // eslint-disable-next-line camelcase + { x: 1n, y: 2n, is_infinite: false }, + ]); + }); }); From acb4858a61ce52cef7bef5cca05a3fef9c2aae02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Thu, 29 May 2025 21:55:46 +0200 Subject: [PATCH 7/7] fix: correctly decoding string return value (#14603) This pull request addresses an issue where the ABI decoder incorrectly returns string values as numeric byte arrays (e.g., [120n, 121n, 122n]) instead of the expected string ("xyz"). The fix modifies the `AbiDecoder` class to properly parse and convert byte-array-based string representations into their corresponding string literals. Fixes #14600 --- yarn-project/stdlib/src/abi/decoder.test.ts | 4 +--- yarn-project/stdlib/src/abi/decoder.ts | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/yarn-project/stdlib/src/abi/decoder.test.ts b/yarn-project/stdlib/src/abi/decoder.test.ts index d5ccbb7cf341..7e048e0fb84e 100644 --- a/yarn-project/stdlib/src/abi/decoder.test.ts +++ b/yarn-project/stdlib/src/abi/decoder.test.ts @@ -263,9 +263,7 @@ describe('decoder', () => { 1n, 2n, false, - // TODO(#14600): correctly decode the string as string instead of 3 bigints - // 'xyz', - [120n, 121n, 122n], + 'xyz', AztecAddress.fromBigInt(1n), // eslint-disable-next-line camelcase { x: 1n, y: 2n, is_infinite: false }, diff --git a/yarn-project/stdlib/src/abi/decoder.ts b/yarn-project/stdlib/src/abi/decoder.ts index da5c39023a51..ff858cf287a7 100644 --- a/yarn-project/stdlib/src/abi/decoder.ts +++ b/yarn-project/stdlib/src/abi/decoder.ts @@ -7,7 +7,7 @@ import { isAztecAddressStruct, parseSignedInt } from './utils.js'; /** * The type of our decoded ABI. */ -export type AbiDecoded = bigint | boolean | AztecAddress | AbiDecoded[] | { [key: string]: AbiDecoded }; +export type AbiDecoded = bigint | boolean | string | AztecAddress | AbiDecoded[] | { [key: string]: AbiDecoded }; /** * Decodes values using a provided ABI. @@ -58,11 +58,12 @@ class AbiDecoder { return struct; } case 'string': { - const array = []; + let str = ''; for (let i = 0; i < abiType.length; i += 1) { - array.push(this.getNextField().toBigInt()); + const charCode = Number(this.getNextField().toBigInt()); + str += String.fromCharCode(charCode); } - return array; + return str; } case 'tuple': { const array = [];