From 5da7874a9fcaa833b051ff57664e9505a359923a Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 14:43:18 -0400 Subject: [PATCH 1/7] feat(codegen): plaintext parser runtime for generated modules Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/__init__.py | 9 +++ sdk/python/aleo/codegen/runtime.py | 88 ++++++++++++++++++++++++ sdk/python/tests/test_codegen_runtime.py | 40 +++++++++++ 3 files changed, 137 insertions(+) create mode 100644 sdk/python/aleo/codegen/__init__.py create mode 100644 sdk/python/aleo/codegen/runtime.py create mode 100644 sdk/python/tests/test_codegen_runtime.py diff --git a/sdk/python/aleo/codegen/__init__.py b/sdk/python/aleo/codegen/__init__.py new file mode 100644 index 00000000..c5d33ee6 --- /dev/null +++ b/sdk/python/aleo/codegen/__init__.py @@ -0,0 +1,9 @@ +"""aleo.codegen — build-time ABI→Python emitter. + +Turns an ``aleo-abi`` JSON description of a program into a module of frozen +dataclasses with ``to_plaintext()`` encoders and ``from_plaintext()`` +decoders. Build-time only: nothing in the ``aleo`` runtime imports this +package, and generated modules import only :mod:`aleo.codegen.runtime`. + +Design: docs/superpowers/specs/2026-07-10-shield-swap-sdk-design.md. +""" diff --git a/sdk/python/aleo/codegen/runtime.py b/sdk/python/aleo/codegen/runtime.py new file mode 100644 index 00000000..01e4ba68 --- /dev/null +++ b/sdk/python/aleo/codegen/runtime.py @@ -0,0 +1,88 @@ +"""Runtime helpers imported by aleo.codegen-generated modules. + +Pure Python, no PyO3 — generated modules must import cheaply and work in any +environment where the ``aleo`` package is installed. + +``parse_plaintext`` parses an Aleo plaintext literal into Python values: +structs/records become dicts (record visibility suffixes ``.private`` / +``.public`` are stripped; ``_nonce`` is kept as a plain entry), suffixed +integers become ``int``, booleans become ``bool``, arrays become lists, and +field/group/scalar/address literals stay verbatim strings. + +The ``fmt_*`` helpers are the inverse direction: they format Python values as +Aleo literals, validating range and shape so a bad value fails at encode time +with a clear message instead of on-chain. +""" +from __future__ import annotations + +import re +from typing import Any + +_INT_RE = re.compile(r"^(-?\d+)(u8|u16|u32|u64|u128|i8|i16|i32|i64|i128)$") +_MODE_RE = re.compile(r"\.(private|public|constant)$") +_ATOM_RE = re.compile(r"[^,}\]]+") + + +def parse_plaintext(text: str) -> Any: + """Parse an Aleo plaintext literal into Python values.""" + value, rest = _parse_value(text.strip()) + if rest.strip(): + raise ValueError(f"Trailing content after plaintext value: {rest!r}") + return value + + +def _parse_value(s: str) -> tuple[Any, str]: + s = s.lstrip() + if s.startswith("{"): + return _parse_struct(s) + if s.startswith("["): + return _parse_array(s) + return _parse_atom(s) + + +def _parse_struct(s: str) -> tuple[dict[str, Any], str]: + s = s[1:].lstrip() # consume "{" + out: dict[str, Any] = {} + while not s.startswith("}"): + if not s: + raise ValueError("Unterminated struct in plaintext") + name, sep, s = s.partition(":") + if not sep: + raise ValueError(f"Expected 'name:' in struct, got {name!r}") + value, s = _parse_value(s) + out[name.strip()] = value + s = s.lstrip() + if s.startswith(","): + s = s[1:].lstrip() + return out, s[1:] + + +def _parse_array(s: str) -> tuple[list[Any], str]: + s = s[1:].lstrip() # consume "[" + out: list[Any] = [] + while not s.startswith("]"): + if not s: + raise ValueError("Unterminated array in plaintext") + value, s = _parse_value(s) + out.append(value) + s = s.lstrip() + if s.startswith(","): + s = s[1:].lstrip() + return out, s[1:] + + +def _parse_atom(s: str) -> tuple[Any, str]: + m = _ATOM_RE.match(s) + if m is None: + raise ValueError(f"Expected a plaintext atom, got {s[:20]!r}") + token = _MODE_RE.sub("", m.group(0).strip()) + rest = s[m.end():] + if token == "true": + return True, rest + if token == "false": + return False, rest + im = _INT_RE.match(token) + if im: + return int(im.group(1)), rest + # field/group/scalar literals, addresses, signatures — verbatim strings + return token, rest diff --git a/sdk/python/tests/test_codegen_runtime.py b/sdk/python/tests/test_codegen_runtime.py new file mode 100644 index 00000000..e7f90800 --- /dev/null +++ b/sdk/python/tests/test_codegen_runtime.py @@ -0,0 +1,40 @@ +"""Tests for aleo.codegen.runtime — plaintext parsing and literal formatting.""" +from aleo.codegen.runtime import parse_plaintext + + +def test_parse_scalar_literals(): + assert parse_plaintext("4055i32") == 4055 + assert parse_plaintext("-4055i32") == -4055 + assert parse_plaintext("183051202759u128") == 183051202759 + assert parse_plaintext("true") is True + assert parse_plaintext("false") is False + assert parse_plaintext("4719field") == "4719field" + assert parse_plaintext("2group") == "2group" + assert parse_plaintext("7scalar") == "7scalar" + assert parse_plaintext("aleo1qyqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5g5x67") \ + == "aleo1qyqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5g5x67" + + +def test_parse_struct(): + text = "{ tick: 4055i32, sqrt_price: 22526123159817891330747538u128, pool: 4719field }" + assert parse_plaintext(text) == { + "tick": 4055, + "sqrt_price": 22526123159817891330747538, + "pool": "4719field", + } + + +def test_parse_nested_struct_and_array(): + text = "{ inner: { a: 1u8, flag: true }, xs: [1u8, 2u8] }" + assert parse_plaintext(text) == {"inner": {"a": 1, "flag": True}, "xs": [1, 2]} + + +def test_parse_record_strips_modes_keeps_nonce(): + text = ("{ owner: aleo1abc.private, amount: 5000000u128.private, " + "token_id: 99field.private, _nonce: 123group.public }") + assert parse_plaintext(text) == { + "owner": "aleo1abc", + "amount": 5000000, + "token_id": "99field", + "_nonce": "123group", + } From 7c9a0960a0a0651c0723dd50b4d328c3b675691d Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 14:44:23 -0400 Subject: [PATCH 2/7] feat(codegen): literal formatters for generated encoders Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/runtime.py | 44 ++++++++++++++++++++++++ sdk/python/tests/test_codegen_runtime.py | 35 ++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/sdk/python/aleo/codegen/runtime.py b/sdk/python/aleo/codegen/runtime.py index 01e4ba68..e1ada231 100644 --- a/sdk/python/aleo/codegen/runtime.py +++ b/sdk/python/aleo/codegen/runtime.py @@ -86,3 +86,47 @@ def _parse_atom(s: str) -> tuple[Any, str]: return int(im.group(1)), rest # field/group/scalar literals, addresses, signatures — verbatim strings return token, rest + + +# ── Literal formatters (encode direction, used by generated to_plaintext) ──── + +_INT_BOUNDS = { + "u8": (0, 2**8 - 1), "u16": (0, 2**16 - 1), "u32": (0, 2**32 - 1), + "u64": (0, 2**64 - 1), "u128": (0, 2**128 - 1), + "i8": (-(2**7), 2**7 - 1), "i16": (-(2**15), 2**15 - 1), + "i32": (-(2**31), 2**31 - 1), "i64": (-(2**63), 2**63 - 1), + "i128": (-(2**127), 2**127 - 1), +} + + +def fmt_int(v: int, suffix: str) -> str: + """Format an int as a suffixed Aleo integer literal, validating range.""" + if isinstance(v, bool) or not isinstance(v, int): + raise ValueError(f"Expected int for {suffix}, got {type(v).__name__}") + lo, hi = _INT_BOUNDS[suffix] + if not lo <= v <= hi: + raise ValueError(f"{v} out of range for {suffix} [{lo}, {hi}]") + return f"{v}{suffix}" + + +def fmt_bool(v: bool) -> str: + """Format a bool as an Aleo boolean literal.""" + if not isinstance(v, bool): + raise ValueError(f"Expected bool, got {type(v).__name__}") + return "true" if v else "false" + + +def fmt_fieldlike(v: int | str, suffix: str) -> str: + """Format an int or pre-suffixed literal as a field/group/scalar literal.""" + if isinstance(v, int) and not isinstance(v, bool): + return f"{v}{suffix}" + if isinstance(v, str) and re.fullmatch(rf"\d+{suffix}", v): + return v + raise ValueError(f"Expected int or '{suffix}' literal, got {v!r}") + + +def fmt_address(v: str) -> str: + """Validate an aleo1… address literal (passes through unchanged).""" + if not (isinstance(v, str) and v.startswith("aleo1")): + raise ValueError(f"Expected an aleo1… address literal, got {v!r}") + return v diff --git a/sdk/python/tests/test_codegen_runtime.py b/sdk/python/tests/test_codegen_runtime.py index e7f90800..e27ce689 100644 --- a/sdk/python/tests/test_codegen_runtime.py +++ b/sdk/python/tests/test_codegen_runtime.py @@ -1,5 +1,13 @@ """Tests for aleo.codegen.runtime — plaintext parsing and literal formatting.""" -from aleo.codegen.runtime import parse_plaintext +import pytest + +from aleo.codegen.runtime import ( + fmt_address, + fmt_bool, + fmt_fieldlike, + fmt_int, + parse_plaintext, +) def test_parse_scalar_literals(): @@ -38,3 +46,28 @@ def test_parse_record_strips_modes_keeps_nonce(): "token_id": "99field", "_nonce": "123group", } + + +def test_fmt_int_ranges(): + assert fmt_int(5, "u128") == "5u128" + assert fmt_int(-1, "i32") == "-1i32" + with pytest.raises(ValueError): + fmt_int(-1, "u64") # negative unsigned + with pytest.raises(ValueError): + fmt_int(2**32, "u32") # overflow + with pytest.raises(ValueError): + fmt_int(2**31, "i32") # signed overflow + with pytest.raises(ValueError): + fmt_int(True, "u8") # bool is not an int here + + +def test_fmt_fieldlike_and_address(): + assert fmt_fieldlike(123, "field") == "123field" + assert fmt_fieldlike("123field", "field") == "123field" + with pytest.raises(ValueError): + fmt_fieldlike("123group", "field") # wrong suffix + assert fmt_bool(True) == "true" + assert fmt_bool(False) == "false" + assert fmt_address("aleo1abc") == "aleo1abc" + with pytest.raises(ValueError): + fmt_address("0xdeadbeef") From ca8847ed25c7900f6026e0395dc78d87e25fe06d Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 14:45:28 -0400 Subject: [PATCH 3/7] feat(codegen): ABI ty-tree to Python type mapping Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/_emit.py | 54 +++++++++++++++++++++++++++ sdk/python/tests/test_codegen_emit.py | 26 +++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 sdk/python/aleo/codegen/_emit.py create mode 100644 sdk/python/tests/test_codegen_emit.py diff --git a/sdk/python/aleo/codegen/_emit.py b/sdk/python/aleo/codegen/_emit.py new file mode 100644 index 00000000..57a96043 --- /dev/null +++ b/sdk/python/aleo/codegen/_emit.py @@ -0,0 +1,54 @@ +"""ABI JSON → Python source emitter. + +Build-time only; the emitted code imports :mod:`aleo.codegen.runtime` for +parsing and formatting. The ABI shape this consumes is the ``aleo-abi`` +output: struct = ``{path: [Name], fields: [{name, ty}]}``, record fields add +``mode``, mapping = ``{name, key: ty, value: ty}``, and ``ty`` is either +``{"Primitive": ...}`` or ``{"Struct": {"path": [...], "program": ...}}``. +""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Callable + + +@dataclass(frozen=True) +class PyType: + """How one ABI type appears in emitted Python. + + ``annotation`` is the type annotation; ``encode_expr``/``decode_expr`` + map a value expression to the encoding/decoding expression emitted into + ``to_plaintext``/``from_decoded`` bodies. + """ + + annotation: str + encode_expr: Callable[[str], str] + decode_expr: Callable[[str], str] + + +def resolve_ty(ty: Any) -> PyType: + """Map an ABI ``ty`` tree to its emitted-Python representation.""" + if isinstance(ty, dict) and "Primitive" in ty: + prim = ty["Primitive"] + if isinstance(prim, dict): + width = prim.get("UInt") or prim.get("Int") + if width is None: + raise ValueError(f"Unsupported primitive: {prim!r}") + suffix = width.lower() + return PyType("int", lambda e, s=suffix: f"fmt_int({e}, '{s}')", lambda e: e) + if prim == "Boolean": + return PyType("bool", lambda e: f"fmt_bool({e})", lambda e: e) + if prim == "Address": + return PyType("str", lambda e: f"fmt_address({e})", lambda e: e) + if prim in ("Field", "Group", "Scalar"): + suffix = prim.lower() + return PyType("str", lambda e, s=suffix: f"fmt_fieldlike({e}, '{s}')", lambda e: e) + raise ValueError(f"Unsupported primitive: {prim!r}") + if isinstance(ty, dict) and "Struct" in ty: + name = ty["Struct"]["path"][-1] + return PyType( + name, + lambda e: f"{e}.to_plaintext()", + lambda e, n=name: f"{n}.from_decoded({e})", + ) + raise ValueError(f"Unsupported ABI type: {ty!r}") diff --git a/sdk/python/tests/test_codegen_emit.py b/sdk/python/tests/test_codegen_emit.py new file mode 100644 index 00000000..d3ed6cdb --- /dev/null +++ b/sdk/python/tests/test_codegen_emit.py @@ -0,0 +1,26 @@ +"""Tests for aleo.codegen._emit — ABI type mapping and source emission.""" +from aleo.codegen._emit import resolve_ty + + +def test_resolve_uint(): + t = resolve_ty({"Primitive": {"UInt": "U128"}}) + assert t.annotation == "int" + assert t.encode_expr("self.amount") == "fmt_int(self.amount, 'u128')" + assert t.decode_expr("d['amount']") == "d['amount']" + + +def test_resolve_int_bool_field_address(): + assert resolve_ty({"Primitive": {"Int": "I32"}}).encode_expr("v") == "fmt_int(v, 'i32')" + assert resolve_ty({"Primitive": "Boolean"}).encode_expr("v") == "fmt_bool(v)" + assert resolve_ty({"Primitive": "Boolean"}).annotation == "bool" + f = resolve_ty({"Primitive": "Field"}) + assert f.annotation == "str" + assert f.encode_expr("v") == "fmt_fieldlike(v, 'field')" + assert resolve_ty({"Primitive": "Address"}).encode_expr("v") == "fmt_address(v)" + + +def test_resolve_nested_struct(): + t = resolve_ty({"Struct": {"path": ["Slot"], "program": "x.aleo"}}) + assert t.annotation == "Slot" + assert t.encode_expr("self.slot") == "self.slot.to_plaintext()" + assert t.decode_expr("d['slot']") == "Slot.from_decoded(d['slot'])" From abea87e6619047ed78265cf5e05ec3d6c000ec28 Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 14:46:32 -0400 Subject: [PATCH 4/7] feat(codegen): struct dataclass emitter with encode/decode Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/_emit.py | 39 +++++++++++++++++++++++++++ sdk/python/tests/test_codegen_emit.py | 30 ++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/sdk/python/aleo/codegen/_emit.py b/sdk/python/aleo/codegen/_emit.py index 57a96043..661ec734 100644 --- a/sdk/python/aleo/codegen/_emit.py +++ b/sdk/python/aleo/codegen/_emit.py @@ -52,3 +52,42 @@ def resolve_ty(ty: Any) -> PyType: lambda e, n=name: f"{n}.from_decoded({e})", ) raise ValueError(f"Unsupported ABI type: {ty!r}") + + +def emit_struct(struct: dict[str, Any]) -> str: + """Emit one struct as a frozen dataclass with encode/decode methods.""" + name = struct["path"][-1] + fields = struct["fields"] + lines: list[str] = ["@dataclass(frozen=True)", f"class {name}:"] + for f in fields: + lines.append(f" {f['name']}: {resolve_ty(f['ty']).annotation}") + + # to_plaintext — emitted as a parts list + join (readable generated code). + lines += ["", " def to_plaintext(self) -> str:", " parts = ["] + for f in fields: + enc = resolve_ty(f["ty"]).encode_expr("self." + f["name"]) + lines.append(f" \"{f['name']}: \" + {enc},") + lines += [ + " ]", + " return \"{ \" + \", \".join(parts) + \" }\"", + ] + + # from_decoded / from_plaintext. Subscript expressions are precomputed + # outside the f-string (no backslashes in f-string expressions on 3.10). + kwarg_parts: list[str] = [] + for f in fields: + subscript = "d['" + f["name"] + "']" + kwarg_parts.append(f"{f['name']}={resolve_ty(f['ty']).decode_expr(subscript)}") + kwargs = ", ".join(kwarg_parts) + lines += [ + "", + " @classmethod", + " def from_decoded(cls, d: dict) -> \"" + name + "\":", + f" return cls({kwargs})", + "", + " @classmethod", + " def from_plaintext(cls, text: str) -> \"" + name + "\":", + " return cls.from_decoded(parse_plaintext(text))", + "", + ] + return "\n".join(lines) + "\n" diff --git a/sdk/python/tests/test_codegen_emit.py b/sdk/python/tests/test_codegen_emit.py index d3ed6cdb..86764c02 100644 --- a/sdk/python/tests/test_codegen_emit.py +++ b/sdk/python/tests/test_codegen_emit.py @@ -1,5 +1,21 @@ """Tests for aleo.codegen._emit — ABI type mapping and source emission.""" -from aleo.codegen._emit import resolve_ty +from aleo.codegen._emit import emit_struct, resolve_ty + +SLOT_ABI = { + "path": ["MiniSlot"], + "fields": [ + {"name": "tick", "ty": {"Primitive": {"Int": "I32"}}}, + {"name": "sqrt_price", "ty": {"Primitive": {"UInt": "U128"}}}, + {"name": "pool", "ty": {"Primitive": "Field"}}, + {"name": "active", "ty": {"Primitive": "Boolean"}}, + ], +} + +PREAMBLE = ( + "from dataclasses import dataclass\n" + "from aleo.codegen.runtime import (parse_plaintext, fmt_int, fmt_bool," + " fmt_fieldlike, fmt_address)\n" +) def test_resolve_uint(): @@ -24,3 +40,15 @@ def test_resolve_nested_struct(): assert t.annotation == "Slot" assert t.encode_expr("self.slot") == "self.slot.to_plaintext()" assert t.decode_expr("d['slot']") == "Slot.from_decoded(d['slot'])" + + +def test_emit_struct_roundtrip(): + ns: dict = {} + exec(PREAMBLE + emit_struct(SLOT_ABI), ns) + MiniSlot = ns["MiniSlot"] + s = MiniSlot(tick=-4055, sqrt_price=22526123159817891330747538, + pool="4719field", active=True) + text = s.to_plaintext() + assert text == ("{ tick: -4055i32, sqrt_price: 22526123159817891330747538u128, " + "pool: 4719field, active: true }") + assert MiniSlot.from_plaintext(text) == s From 41e766d0cbf0c1a1d844f86e9165fe881fdeecdd Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 14:47:44 -0400 Subject: [PATCH 5/7] feat(codegen): record emitter, mapping decoder table, module assembly Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/_emit.py | 91 +++++++++++++++++++++++++++ sdk/python/tests/test_codegen_emit.py | 52 ++++++++++++++- 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/sdk/python/aleo/codegen/_emit.py b/sdk/python/aleo/codegen/_emit.py index 661ec734..7b8b5a21 100644 --- a/sdk/python/aleo/codegen/_emit.py +++ b/sdk/python/aleo/codegen/_emit.py @@ -91,3 +91,94 @@ def emit_struct(struct: dict[str, Any]) -> str: "", ] return "\n".join(lines) + "\n" + + +# ── Records, mappings, module assembly ─────────────────────────────────────── + +_HEADER = "# Generated by aleo.codegen — DO NOT EDIT.\n" +_IMPORTS = ( + "from dataclasses import dataclass\n" + "from typing import Any, Callable, Optional\n" + "from aleo.codegen.runtime import (parse_plaintext, fmt_int, fmt_bool," + " fmt_fieldlike, fmt_address)\n\n" +) + + +def _struct_deps(struct: dict[str, Any]) -> set[str]: + deps: set[str] = set() + for f in struct["fields"]: + ty = f["ty"] + if isinstance(ty, dict) and "Struct" in ty: + deps.add(ty["Struct"]["path"][-1]) + return deps + + +def _toposort(structs: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Order structs so nested struct classes are defined before use.""" + by_name = {s["path"][-1]: s for s in structs} + done: list[dict[str, Any]] = [] + seen: set[str] = set() + + def visit(name: str) -> None: + if name in seen or name not in by_name: + return + seen.add(name) + for dep in _struct_deps(by_name[name]): + visit(dep) + done.append(by_name[name]) + + for s in structs: + visit(s["path"][-1]) + return done + + +def emit_record(record: dict[str, Any]) -> str: + """Emit one record as a decode-only frozen dataclass. + + Records are produced by scanners and never hand-constructed, so no + ``to_plaintext`` is emitted. The scanner's ``_nonce`` rides along as an + optional extra field. + """ + name = record["path"][-1] + lines = ["@dataclass(frozen=True)", f"class {name}:"] + for f in record["fields"]: + lines.append(f" {f['name']}: {resolve_ty(f['ty']).annotation}") + lines.append(" _nonce: Optional[str] = None") + kwarg_parts: list[str] = [] + for f in record["fields"]: + subscript = "d['" + f["name"] + "']" + kwarg_parts.append(f"{f['name']}={resolve_ty(f['ty']).decode_expr(subscript)}") + kwargs = ", ".join(kwarg_parts) + lines += [ + "", + " @classmethod", + f" def from_decoded(cls, d: dict) -> \"{name}\":", + f" return cls({kwargs}, _nonce=d.get('_nonce'))", + "", + " @classmethod", + f" def from_plaintext(cls, text: str) -> \"{name}\":", + " return cls.from_decoded(parse_plaintext(text))", + "", + ] + return "\n".join(lines) + "\n" + + +def emit_module(abi: dict[str, Any]) -> str: + """Emit a complete generated module for one program's ABI.""" + parts = [_HEADER, _IMPORTS, f"PROGRAM_ID = \"{abi['program']}\"\n\n"] + for s in _toposort(abi.get("structs", [])): + parts.append(emit_struct(s)) + parts.append("\n") + for r in abi.get("records", []): + parts.append(emit_record(r)) + parts.append("\n") + dec_entries: list[str] = [] + for m in abi.get("mappings", []): + v = m["value"] + if isinstance(v, dict) and "Struct" in v: + dec_entries.append(f" \"{m['name']}\": {v['Struct']['path'][-1]}.from_plaintext,") + else: + dec_entries.append(f" \"{m['name']}\": parse_plaintext,") + parts.append("MAPPING_VALUE_DECODERS: dict[str, Callable[[str], Any]] = {\n" + + "\n".join(dec_entries) + "\n}\n") + return "".join(parts) diff --git a/sdk/python/tests/test_codegen_emit.py b/sdk/python/tests/test_codegen_emit.py index 86764c02..f01f49ca 100644 --- a/sdk/python/tests/test_codegen_emit.py +++ b/sdk/python/tests/test_codegen_emit.py @@ -1,5 +1,5 @@ """Tests for aleo.codegen._emit — ABI type mapping and source emission.""" -from aleo.codegen._emit import emit_struct, resolve_ty +from aleo.codegen._emit import emit_module, emit_struct, resolve_ty SLOT_ABI = { "path": ["MiniSlot"], @@ -52,3 +52,53 @@ def test_emit_struct_roundtrip(): assert text == ("{ tick: -4055i32, sqrt_price: 22526123159817891330747538u128, " "pool: 4719field, active: true }") assert MiniSlot.from_plaintext(text) == s + + +MINI_ABI = { + "program": "mini.aleo", + "structs": [ + {"path": ["Inner"], "fields": [{"name": "a", "ty": {"Primitive": {"UInt": "U8"}}}]}, + {"path": ["Outer"], "fields": [ + {"name": "inner", "ty": {"Struct": {"path": ["Inner"], "program": "mini.aleo"}}}, + {"name": "pool", "ty": {"Primitive": "Field"}}, + ]}, + ], + "records": [ + {"path": ["Token"], "fields": [ + {"name": "owner", "ty": {"Primitive": "Address"}, "mode": "Private"}, + {"name": "amount", "ty": {"Primitive": {"UInt": "U128"}}, "mode": "Private"}, + ]}, + ], + "mappings": [ + {"name": "outers", "key": {"Primitive": "Field"}, + "value": {"Struct": {"path": ["Outer"], "program": "mini.aleo"}}}, + {"name": "heights", "key": {"Primitive": "Field"}, + "value": {"Primitive": {"UInt": "U32"}}}, + ], + "storage_variables": [], "functions": [], "views": [], +} + + +def test_emit_module_end_to_end(): + src = emit_module(MINI_ABI) + assert src.startswith("# Generated by aleo.codegen — DO NOT EDIT.") + ns: dict = {} + exec(compile(src, "generated", "exec"), ns) + assert ns["PROGRAM_ID"] == "mini.aleo" + outer = ns["Outer"].from_plaintext("{ inner: { a: 7u8 }, pool: 5field }") + assert outer.inner.a == 7 and outer.pool == "5field" + assert outer.to_plaintext() == "{ inner: { a: 7u8 }, pool: 5field }" + tok = ns["Token"].from_plaintext( + "{ owner: aleo1abc.private, amount: 5u128.private, _nonce: 9group.public }") + assert tok.owner == "aleo1abc" and tok.amount == 5 and tok._nonce == "9group" + dec = ns["MAPPING_VALUE_DECODERS"] + assert dec["outers"]("{ inner: { a: 1u8 }, pool: 2field }").inner.a == 1 + assert dec["heights"]("42u32") == 42 + + +def test_emit_module_sorts_nested_structs_first(): + # Outer references Inner; reversing declaration order must still work. + flipped = dict(MINI_ABI, structs=list(reversed(MINI_ABI["structs"]))) + ns: dict = {} + exec(compile(emit_module(flipped), "generated", "exec"), ns) + assert ns["Outer"].from_plaintext("{ inner: { a: 7u8 }, pool: 5field }").inner.a == 7 From c7bbbfa346f70abc59f5339f0049319aa34e141e Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 14:50:55 -0400 Subject: [PATCH 6/7] feat(codegen): CLI with --abi/--out and --config modes; real-ABI smoke test Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/__main__.py | 49 + sdk/python/aleo/codegen/_emit.py | 1 + sdk/python/aleo/codegen/runtime.py | 8 +- .../tests/fixtures/shield_swap_v3.abi.json | 2654 +++++++++++++++++ sdk/python/tests/test_codegen_cli.py | 48 + 5 files changed, 2756 insertions(+), 4 deletions(-) create mode 100644 sdk/python/aleo/codegen/__main__.py create mode 100644 sdk/python/tests/fixtures/shield_swap_v3.abi.json create mode 100644 sdk/python/tests/test_codegen_cli.py diff --git a/sdk/python/aleo/codegen/__main__.py b/sdk/python/aleo/codegen/__main__.py new file mode 100644 index 00000000..274f6cb0 --- /dev/null +++ b/sdk/python/aleo/codegen/__main__.py @@ -0,0 +1,49 @@ +"""CLI: python -m aleo.codegen --abi abi.json --out generated.py [--config cfg.json] + +Config mode drives multiple programs from one JSON file +(``{"programs": [{"abi": "...", "out": "..."}]}``); paths inside a config +resolve relative to the config file's own location. +""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +from ._emit import emit_module + + +def _generate(abi_path: Path, out_path: Path) -> None: + abi = json.loads(abi_path.read_text()) + out_path.parent.mkdir(parents=True, exist_ok=True) + out_path.write_text(emit_module(abi)) + print(f"generated {len(abi.get('structs', []))} structs, " + f"{len(abi.get('records', []))} records, " + f"{len(abi.get('mappings', []))} mapping decoders -> {out_path}") + + +def main(argv: list[str] | None = None) -> int: + p = argparse.ArgumentParser(prog="aleo.codegen") + p.add_argument("--abi", type=Path, help="path to ABI JSON") + p.add_argument("--out", type=Path, help="output .py path") + p.add_argument("--config", type=Path, help="config JSON with a programs list") + args = p.parse_args(argv) + try: + if args.config: + cfg = json.loads(args.config.read_text()) + base = args.config.parent + for entry in cfg["programs"]: + _generate((base / entry["abi"]).resolve(), (base / entry["out"]).resolve()) + elif args.abi and args.out: + _generate(args.abi, args.out) + else: + p.error("provide --abi and --out, or --config") + except (OSError, json.JSONDecodeError, ValueError, KeyError) as exc: + print(f"aleo.codegen: {exc}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/sdk/python/aleo/codegen/_emit.py b/sdk/python/aleo/codegen/_emit.py index 7b8b5a21..18864491 100644 --- a/sdk/python/aleo/codegen/_emit.py +++ b/sdk/python/aleo/codegen/_emit.py @@ -1,3 +1,4 @@ +# pyright: reportUnknownMemberType=false, reportUnknownVariableType=false, reportUnknownArgumentType=false, reportUnknownLambdaType=false """ABI JSON → Python source emitter. Build-time only; the emitted code imports :mod:`aleo.codegen.runtime` for diff --git a/sdk/python/aleo/codegen/runtime.py b/sdk/python/aleo/codegen/runtime.py index e1ada231..489213ae 100644 --- a/sdk/python/aleo/codegen/runtime.py +++ b/sdk/python/aleo/codegen/runtime.py @@ -99,7 +99,7 @@ def _parse_atom(s: str) -> tuple[Any, str]: } -def fmt_int(v: int, suffix: str) -> str: +def fmt_int(v: object, suffix: str) -> str: """Format an int as a suffixed Aleo integer literal, validating range.""" if isinstance(v, bool) or not isinstance(v, int): raise ValueError(f"Expected int for {suffix}, got {type(v).__name__}") @@ -109,14 +109,14 @@ def fmt_int(v: int, suffix: str) -> str: return f"{v}{suffix}" -def fmt_bool(v: bool) -> str: +def fmt_bool(v: object) -> str: """Format a bool as an Aleo boolean literal.""" if not isinstance(v, bool): raise ValueError(f"Expected bool, got {type(v).__name__}") return "true" if v else "false" -def fmt_fieldlike(v: int | str, suffix: str) -> str: +def fmt_fieldlike(v: object, suffix: str) -> str: """Format an int or pre-suffixed literal as a field/group/scalar literal.""" if isinstance(v, int) and not isinstance(v, bool): return f"{v}{suffix}" @@ -125,7 +125,7 @@ def fmt_fieldlike(v: int | str, suffix: str) -> str: raise ValueError(f"Expected int or '{suffix}' literal, got {v!r}") -def fmt_address(v: str) -> str: +def fmt_address(v: object) -> str: """Validate an aleo1… address literal (passes through unchanged).""" if not (isinstance(v, str) and v.startswith("aleo1")): raise ValueError(f"Expected an aleo1… address literal, got {v!r}") diff --git a/sdk/python/tests/fixtures/shield_swap_v3.abi.json b/sdk/python/tests/fixtures/shield_swap_v3.abi.json new file mode 100644 index 00000000..bc3e9581 --- /dev/null +++ b/sdk/python/tests/fixtures/shield_swap_v3.abi.json @@ -0,0 +1,2654 @@ +{ + "program": "shield_swap_v3.aleo", + "structs": [ + { + "path": [ + "SwapRequest" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "zero_for_one", + "ty": { + "Primitive": "Boolean" + } + }, + { + "name": "amount_in", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount_out_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "sqrt_price_limit", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "recipient", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "nonce", + "ty": { + "Primitive": { + "UInt": "U64" + } + } + }, + { + "name": "deadline", + "ty": { + "Primitive": { + "UInt": "U32" + } + } + } + ] + }, + { + "path": [ + "SwapHop" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "zero_for_one", + "ty": { + "Primitive": "Boolean" + } + }, + { + "name": "sqrt_price_limit", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + }, + { + "path": [ + "SwapMultiHopRequest" + ], + "fields": [ + { + "name": "token_in", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token_out", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_in", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount_out_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "recipient", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "hop0", + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "hop1", + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "hop2", + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "hop_count", + "ty": { + "Primitive": { + "UInt": "U8" + } + } + }, + { + "name": "nonce", + "ty": { + "Primitive": { + "UInt": "U64" + } + } + }, + { + "name": "deadline", + "ty": { + "Primitive": { + "UInt": "U32" + } + } + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + } + } + ] + }, + { + "path": [ + "MintPositionRequest" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "tick_lower", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_upper", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "amount0_desired", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount1_desired", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount0_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount1_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tick_lower_hint", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_upper_hint", + "ty": { + "Primitive": { + "Int": "I32" + } + } + } + ] + }, + { + "path": [ + "PoolState" + ], + "fields": [ + { + "name": "token0", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token1", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "fee", + "ty": { + "Primitive": { + "UInt": "U16" + } + } + }, + { + "name": "enabled", + "ty": { + "Primitive": "Boolean" + } + }, + { + "name": "scale0", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "scale1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + }, + { + "path": [ + "Slot" + ], + "fields": [ + { + "name": "tick", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_spacing", + "ty": { + "Primitive": { + "UInt": "U32" + } + } + }, + { + "name": "sqrt_price", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_protocol", + "ty": { + "Primitive": { + "UInt": "U8" + } + } + }, + { + "name": "liquidity", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_global0_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_global1_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_residual0_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_residual1_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "max_liquidity_per_tick", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "protocol_fees0", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "protocol_fees1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "next_init_below", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "next_init_above", + "ty": { + "Primitive": { + "Int": "I32" + } + } + } + ] + }, + { + "path": [ + "Tick" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "liquidity_net", + "ty": { + "Primitive": { + "Int": "I128" + } + } + }, + { + "name": "liquidity_gross", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tick", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "fee_growth_outside0_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_outside1_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "prev", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "next", + "ty": { + "Primitive": { + "Int": "I32" + } + } + } + ] + }, + { + "path": [ + "Position" + ], + "fields": [ + { + "name": "token_id", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "tick_lower", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_upper", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "liquidity", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_inside0_last_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_inside1_last_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tokens_owed0", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tokens_owed1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + }, + { + "path": [ + "PairKey" + ], + "fields": [ + { + "name": "token0", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token1", + "ty": { + "Primitive": "Field" + } + } + ] + }, + { + "path": [ + "SwapOutput" + ], + "fields": [ + { + "name": "recipient", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "token_in", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token_out", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_out", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount_remaining", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "token_in_1", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_remaining_1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "token_in_2", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_remaining_2", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + } + ], + "records": [ + { + "path": [ + "PositionNFT" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "token_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token0_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token1_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "pool", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "tick_lower", + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Private" + }, + { + "name": "tick_upper", + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Private" + } + ] + }, + { + "path": [ + "SwapComplianceRecord" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "swap_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token_in", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token_out", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "request", + "ty": { + "Struct": { + "path": [ + "SwapRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Private" + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "blinded_address", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + ] + }, + { + "path": [ + "MultiHopSwapComplianceRecord" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "swap_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "request", + "ty": { + "Struct": { + "path": [ + "SwapMultiHopRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Private" + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "blinded_address", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + ] + }, + { + "path": [ + "MintComplianceRecord" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "token_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token0_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token1_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "request", + "ty": { + "Struct": { + "path": [ + "MintPositionRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Private" + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "recipient", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + ] + } + ], + "mappings": [ + { + "name": "pools", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "PoolState" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "slots", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "Slot" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "ticks", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "Tick" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "initialized_pools", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "tick_spacings", + "key": { + "Primitive": { + "UInt": "U32" + } + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "fee_tiers", + "key": { + "Primitive": { + "UInt": "U16" + } + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "fee_to_tick_spacing", + "key": { + "Primitive": { + "UInt": "U16" + } + }, + "value": { + "Primitive": { + "UInt": "U32" + } + } + }, + { + "name": "positions", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "Position" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "swap_outputs", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "SwapOutput" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "admin", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Address" + } + }, + { + "name": "pending_admin", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Address" + } + }, + { + "name": "used_blinded_addresses", + "key": { + "Primitive": "Address" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "token_decimals", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U8" + } + } + }, + { + "name": "pool_creation_is_open", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "global_paused", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "token_allowed", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "token_paused", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "pair_paused", + "key": { + "Struct": { + "path": [ + "PairKey" + ], + "program": "shield_swap_v3.aleo" + } + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "frozen_position", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U32" + } + } + } + ], + "storage_variables": [], + "functions": [ + { + "name": "transfer_admin", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "accept_admin", + "inputs": [], + "outputs": [ + "Final" + ] + }, + { + "name": "add_tick_spacing", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "add_fee_tier", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U16" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "bind_fee_to_tick_spacing", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U16" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_token_decimals", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_pool_enabled", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_pool_creation_is_open", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_global_paused", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "allow_token", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_token_paused", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_pair_paused", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "freeze_position", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "unfreeze_position", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_fee_protocol", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "collect_protocol", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "create_pool", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U16" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + "Final" + ] + }, + { + "name": "mint", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + "DynamicRecord", + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "MintPositionRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + "DynamicRecord", + "DynamicRecord", + { + "Record": { + "path": [ + "MintComplianceRecord" + ], + "program": "shield_swap_v3.aleo" + } + }, + "Final" + ] + }, + { + "name": "decrease_liquidity", + "inputs": [ + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + "Final" + ] + }, + { + "name": "increase_liquidity", + "inputs": [ + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + "DynamicRecord", + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + "DynamicRecord", + "DynamicRecord", + "Final" + ] + }, + { + "name": "collect", + "inputs": [ + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + } + ], + "outputs": [ + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + "DynamicRecord", + "DynamicRecord", + "Final" + ] + }, + { + "name": "burn", + "inputs": [ + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + "Final" + ] + }, + { + "name": "swap", + "inputs": [ + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + "DynamicRecord", + { + "Record": { + "path": [ + "SwapComplianceRecord" + ], + "program": "shield_swap_v3.aleo" + } + }, + "Final" + ] + }, + { + "name": "claim_swap_output", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "DynamicRecord", + "DynamicRecord", + "Final" + ] + }, + { + "name": "swap_multi_hop", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + "DynamicRecord", + { + "Record": { + "path": [ + "MultiHopSwapComplianceRecord" + ], + "program": "shield_swap_v3.aleo" + } + }, + "Final" + ] + }, + { + "name": "claim_multi_hop_output", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "DynamicRecord", + "DynamicRecord", + "DynamicRecord", + "DynamicRecord", + "Final" + ] + } + ], + "views": [ + { + "name": "view_sqrt_price_at_tick", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ] + }, + { + "name": "view_amounts_for_liquidity", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ] + }, + { + "name": "view_liquidity_for_amounts", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ] + }, + { + "name": "view_compute_swap_step", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/sdk/python/tests/test_codegen_cli.py b/sdk/python/tests/test_codegen_cli.py new file mode 100644 index 00000000..70afbb18 --- /dev/null +++ b/sdk/python/tests/test_codegen_cli.py @@ -0,0 +1,48 @@ +"""CLI tests for python -m aleo.codegen, including a real-ABI smoke test.""" +import json +import subprocess +import sys +from pathlib import Path + +FIXTURE = Path(__file__).parent / "fixtures" / "shield_swap_v3.abi.json" + + +def test_cli_abi_out(tmp_path): + out = tmp_path / "gen.py" + r = subprocess.run( + [sys.executable, "-m", "aleo.codegen", "--abi", str(FIXTURE), "--out", str(out)], + capture_output=True, text=True, + ) + assert r.returncode == 0, r.stderr + ns: dict = {} + exec(compile(out.read_text(), str(out), "exec"), ns) + assert ns["PROGRAM_ID"] == "shield_swap_v3.aleo" + # Every struct/record named in the ABI must exist as a class. + abi = json.loads(FIXTURE.read_text()) + for s in abi["structs"] + abi["records"]: + assert s["path"][-1] in ns, f"missing class {s['path'][-1]}" + # Slot decodes a realistic mapping value. + slot = ns["Slot"].from_plaintext( + "{ tick: 4055i32, tick_spacing: 60i32, sqrt_price: 22526123159817891330747538u128, " + "fee_protocol: 0u8, liquidity: 183051202759u128, fee_growth_global0_x_64: 0u128, " + "fee_growth_global1_x_64: 0u128, fee_residual0_x_64: 0u128, fee_residual1_x_64: 0u128, " + "max_liquidity_per_tick: 1000u128, protocol_fees0: 0u128, protocol_fees1: 0u128, " + "next_init_below: 3960i32, next_init_above: 4080i32 }") + assert slot.tick == 4055 and slot.sqrt_price == 22526123159817891330747538 + + +def test_cli_config_mode(tmp_path): + out = tmp_path / "gen2.py" + cfg = tmp_path / "cfg.json" + cfg.write_text(json.dumps({"programs": [{"abi": str(FIXTURE), "out": str(out)}]})) + r = subprocess.run([sys.executable, "-m", "aleo.codegen", "--config", str(cfg)], + capture_output=True, text=True) + assert r.returncode == 0, r.stderr + assert out.exists() + + +def test_cli_missing_abi_errors(tmp_path): + r = subprocess.run([sys.executable, "-m", "aleo.codegen", "--abi", "nope.json", + "--out", str(tmp_path / "x.py")], capture_output=True, text=True) + assert r.returncode != 0 + assert "nope.json" in r.stderr From 3c7de7c6078b3f3ca1b306b0d38d4644847fce16 Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Mon, 13 Jul 2026 15:03:37 -0400 Subject: [PATCH 7/7] fix(codegen): harden emitter and parser per review - validate ABI identifiers before interpolating into emitted source (keywords, non-identifiers, reserved _nonce -> generation-time ValueError) - reject cross-program/undefined struct references and duplicate struct names at emit time instead of emitting NameError-at-import modules - strict plaintext parsing: missing separators and malformed member names now raise instead of silently gluing tokens into strings - clear TypeError/ValueError for None/'null'/empty mapping values - accept signed field/group/scalar literals in fmt_fieldlike (-1field) - emit the full ABI dict constant (mapping key types stay recoverable) - fixture provenance (_source) per AGENTS.md; drop git-ignored spec path from the package docstring Co-Authored-By: Claude Fable 5 --- sdk/python/aleo/codegen/__init__.py | 2 +- sdk/python/aleo/codegen/_emit.py | 118 +- sdk/python/aleo/codegen/runtime.py | 46 +- .../tests/fixtures/shield_swap_v3.abi.json | 5101 +++++++++-------- sdk/python/tests/test_codegen_emit.py | 36 + sdk/python/tests/test_codegen_runtime.py | 15 + 6 files changed, 2733 insertions(+), 2585 deletions(-) diff --git a/sdk/python/aleo/codegen/__init__.py b/sdk/python/aleo/codegen/__init__.py index c5d33ee6..77aaaf2b 100644 --- a/sdk/python/aleo/codegen/__init__.py +++ b/sdk/python/aleo/codegen/__init__.py @@ -5,5 +5,5 @@ decoders. Build-time only: nothing in the ``aleo`` runtime imports this package, and generated modules import only :mod:`aleo.codegen.runtime`. -Design: docs/superpowers/specs/2026-07-10-shield-swap-sdk-design.md. +Usage: ``python -m aleo.codegen --abi abi.json --out generated.py``. """ diff --git a/sdk/python/aleo/codegen/_emit.py b/sdk/python/aleo/codegen/_emit.py index 18864491..e08d797d 100644 --- a/sdk/python/aleo/codegen/_emit.py +++ b/sdk/python/aleo/codegen/_emit.py @@ -9,9 +9,31 @@ """ from __future__ import annotations -from dataclasses import dataclass +import keyword +import re +from dataclasses import dataclass, field from typing import Any, Callable +_PROGRAM_ID_RE = re.compile(r"[a-zA-Z0-9_.]+") + + +def _ident(name: Any, context: str) -> str: + """Validate an ABI name before interpolating it into emitted source. + + ABI JSON is external input; a name that is not a plain Python identifier + (or that shadows a keyword or the synthetic ``_nonce`` record field) must + fail at generation time with a pointer to the offender, never become a + SyntaxError — or executable code — in the generated module. + """ + if ( + not isinstance(name, str) + or not name.isidentifier() + or keyword.iskeyword(name) + or name == "_nonce" + ): + raise ValueError(f"Invalid identifier in ABI {context}: {name!r}") + return name + @dataclass(frozen=True) class PyType: @@ -24,7 +46,8 @@ class PyType: annotation: str encode_expr: Callable[[str], str] - decode_expr: Callable[[str], str] + # Only nested structs decode; primitives pass through as parsed. + decode_expr: Callable[[str], str] = field(default=lambda e: e) def resolve_ty(ty: Any) -> PyType: @@ -36,17 +59,17 @@ def resolve_ty(ty: Any) -> PyType: if width is None: raise ValueError(f"Unsupported primitive: {prim!r}") suffix = width.lower() - return PyType("int", lambda e, s=suffix: f"fmt_int({e}, '{s}')", lambda e: e) + return PyType("int", lambda e, s=suffix: f"fmt_int({e}, '{s}')") if prim == "Boolean": - return PyType("bool", lambda e: f"fmt_bool({e})", lambda e: e) + return PyType("bool", lambda e: f"fmt_bool({e})") if prim == "Address": - return PyType("str", lambda e: f"fmt_address({e})", lambda e: e) + return PyType("str", lambda e: f"fmt_address({e})") if prim in ("Field", "Group", "Scalar"): suffix = prim.lower() - return PyType("str", lambda e, s=suffix: f"fmt_fieldlike({e}, '{s}')", lambda e: e) + return PyType("str", lambda e, s=suffix: f"fmt_fieldlike({e}, '{s}')") raise ValueError(f"Unsupported primitive: {prim!r}") if isinstance(ty, dict) and "Struct" in ty: - name = ty["Struct"]["path"][-1] + name = _ident(ty["Struct"]["path"][-1], "struct reference") return PyType( name, lambda e: f"{e}.to_plaintext()", @@ -57,17 +80,18 @@ def resolve_ty(ty: Any) -> PyType: def emit_struct(struct: dict[str, Any]) -> str: """Emit one struct as a frozen dataclass with encode/decode methods.""" - name = struct["path"][-1] - fields = struct["fields"] + name = _ident(struct["path"][-1], "struct name") + fields = [(_ident(f["name"], f"field of {name}"), resolve_ty(f["ty"])) + for f in struct["fields"]] lines: list[str] = ["@dataclass(frozen=True)", f"class {name}:"] - for f in fields: - lines.append(f" {f['name']}: {resolve_ty(f['ty']).annotation}") + for fname, pt in fields: + lines.append(f" {fname}: {pt.annotation}") # to_plaintext — emitted as a parts list + join (readable generated code). lines += ["", " def to_plaintext(self) -> str:", " parts = ["] - for f in fields: - enc = resolve_ty(f["ty"]).encode_expr("self." + f["name"]) - lines.append(f" \"{f['name']}: \" + {enc},") + for fname, pt in fields: + enc = pt.encode_expr("self." + fname) + lines.append(f" \"{fname}: \" + {enc},") lines += [ " ]", " return \"{ \" + \", \".join(parts) + \" }\"", @@ -76,9 +100,9 @@ def emit_struct(struct: dict[str, Any]) -> str: # from_decoded / from_plaintext. Subscript expressions are precomputed # outside the f-string (no backslashes in f-string expressions on 3.10). kwarg_parts: list[str] = [] - for f in fields: - subscript = "d['" + f["name"] + "']" - kwarg_parts.append(f"{f['name']}={resolve_ty(f['ty']).decode_expr(subscript)}") + for fname, pt in fields: + subscript = "d['" + fname + "']" + kwarg_parts.append(f"{fname}={pt.decode_expr(subscript)}") kwargs = ", ".join(kwarg_parts) lines += [ "", @@ -140,15 +164,17 @@ def emit_record(record: dict[str, Any]) -> str: ``to_plaintext`` is emitted. The scanner's ``_nonce`` rides along as an optional extra field. """ - name = record["path"][-1] + name = _ident(record["path"][-1], "record name") + fields = [(_ident(f["name"], f"field of {name}"), resolve_ty(f["ty"])) + for f in record["fields"]] lines = ["@dataclass(frozen=True)", f"class {name}:"] - for f in record["fields"]: - lines.append(f" {f['name']}: {resolve_ty(f['ty']).annotation}") + for fname, pt in fields: + lines.append(f" {fname}: {pt.annotation}") lines.append(" _nonce: Optional[str] = None") kwarg_parts: list[str] = [] - for f in record["fields"]: - subscript = "d['" + f["name"] + "']" - kwarg_parts.append(f"{f['name']}={resolve_ty(f['ty']).decode_expr(subscript)}") + for fname, pt in fields: + subscript = "d['" + fname + "']" + kwarg_parts.append(f"{fname}={pt.decode_expr(subscript)}") kwargs = ", ".join(kwarg_parts) lines += [ "", @@ -164,9 +190,49 @@ def emit_record(record: dict[str, Any]) -> str: return "\n".join(lines) + "\n" +def _check_struct_refs(abi: dict[str, Any]) -> None: + """Every struct reference must resolve to a struct defined in THIS ABI. + + A cross-program or missing reference would otherwise emit a call to a + class that is never generated (NameError at import/decode time), and two + structs sharing a terminal name would silently collapse to one class. + """ + program = abi["program"] + structs = abi.get("structs", []) + names = [s["path"][-1] for s in structs] + dupes = {n for n in names if names.count(n) > 1} + if dupes: + raise ValueError(f"Duplicate struct names in ABI: {sorted(dupes)}") + local = set(names) + + def check(ty: Any, context: str) -> None: + if isinstance(ty, dict) and "Struct" in ty: + ref = ty["Struct"] + name, prog = ref["path"][-1], ref.get("program", program) + if name not in local or prog != program: + raise ValueError( + f"Unresolvable struct reference {name!r} (program {prog!r}) " + f"in {context}: cross-program and undefined structs are not " + "supported — the generated class would not exist." + ) + + for s in structs: + for f in s["fields"]: + check(f["ty"], f"struct {s['path'][-1]}") + for r in abi.get("records", []): + for f in r["fields"]: + check(f["ty"], f"record {r['path'][-1]}") + for m in abi.get("mappings", []): + check(m["value"], f"mapping {m['name']}") + + def emit_module(abi: dict[str, Any]) -> str: """Emit a complete generated module for one program's ABI.""" - parts = [_HEADER, _IMPORTS, f"PROGRAM_ID = \"{abi['program']}\"\n\n"] + program = abi["program"] + if not isinstance(program, str) or not _PROGRAM_ID_RE.fullmatch(program): + raise ValueError(f"Invalid program id in ABI: {program!r}") + _check_struct_refs(abi) + parts = [_HEADER, _IMPORTS, f"PROGRAM_ID = \"{program}\"\n\n"] for s in _toposort(abi.get("structs", [])): parts.append(emit_struct(s)) parts.append("\n") @@ -182,4 +248,8 @@ def emit_module(abi: dict[str, Any]) -> str: dec_entries.append(f" \"{m['name']}\": parse_plaintext,") parts.append("MAPPING_VALUE_DECODERS: dict[str, Callable[[str], Any]] = {\n" + "\n".join(dec_entries) + "\n}\n") + # The full ABI rides along (like the TS bindings' PROGRAM_ABI) so callers + # can recover what the classes drop — e.g. mapping KEY types for + # formatting read keys — without re-reading the pinned JSON at runtime. + parts.append(f"\nABI: dict = {abi!r}\n") return "".join(parts) diff --git a/sdk/python/aleo/codegen/runtime.py b/sdk/python/aleo/codegen/runtime.py index 489213ae..34688ed0 100644 --- a/sdk/python/aleo/codegen/runtime.py +++ b/sdk/python/aleo/codegen/runtime.py @@ -1,7 +1,10 @@ """Runtime helpers imported by aleo.codegen-generated modules. -Pure Python, no PyO3 — generated modules must import cheaply and work in any -environment where the ``aleo`` package is installed. +Stdlib-only by design: generated modules depend on nothing beyond this module, +and this module's own imports are trivially cheap. (Importing it still pulls +in the ``aleo`` package ``__init__``, which loads the compiled extension — the +PyO3 ``Plaintext`` type is not used here because it does not expose values as +plain Python dicts.) ``parse_plaintext`` parses an Aleo plaintext literal into Python values: structs/records become dicts (record visibility suffixes ``.private`` / @@ -20,12 +23,27 @@ _INT_RE = re.compile(r"^(-?\d+)(u8|u16|u32|u64|u128|i8|i16|i32|i64|i128)$") _MODE_RE = re.compile(r"\.(private|public|constant)$") -_ATOM_RE = re.compile(r"[^,}\]]+") - - -def parse_plaintext(text: str) -> Any: - """Parse an Aleo plaintext literal into Python values.""" - value, rest = _parse_value(text.strip()) +# Atoms are single tokens: no whitespace, separators, or ':' — malformed +# plaintext must fail loudly, not be silently glued into one string value. +_ATOM_RE = re.compile(r"[^,{}\[\]\s:]+") + + +def parse_plaintext(text: object) -> Any: + """Parse an Aleo plaintext literal into Python values. + + Raises ``TypeError`` for non-strings (an absent mapping value arrives as + ``None`` from the node — handle absence before decoding) and + ``ValueError`` for empty/``null`` bodies and malformed plaintext. + """ + if not isinstance(text, str): + raise TypeError( + f"Expected a plaintext str, got {type(text).__name__} — absent " + "mapping values arrive as None; handle absence before decoding." + ) + stripped = text.strip() + if stripped in ("", "null"): + raise ValueError("Plaintext is empty or 'null' — the mapping entry is absent.") + value, rest = _parse_value(stripped) if rest.strip(): raise ValueError(f"Trailing content after plaintext value: {rest!r}") return value @@ -47,13 +65,18 @@ def _parse_struct(s: str) -> tuple[dict[str, Any], str]: if not s: raise ValueError("Unterminated struct in plaintext") name, sep, s = s.partition(":") + key = name.strip() if not sep: raise ValueError(f"Expected 'name:' in struct, got {name!r}") + if not key.isidentifier(): + raise ValueError(f"Invalid struct member name {key!r} in plaintext") value, s = _parse_value(s) - out[name.strip()] = value + out[key] = value s = s.lstrip() if s.startswith(","): s = s[1:].lstrip() + elif not s.startswith("}"): + raise ValueError(f"Expected ',' or '}}' in struct, got {s[:20]!r}") return out, s[1:] @@ -68,6 +91,8 @@ def _parse_array(s: str) -> tuple[list[Any], str]: s = s.lstrip() if s.startswith(","): s = s[1:].lstrip() + elif not s.startswith("]"): + raise ValueError(f"Expected ',' or ']' in array, got {s[:20]!r}") return out, s[1:] @@ -120,7 +145,8 @@ def fmt_fieldlike(v: object, suffix: str) -> str: """Format an int or pre-suffixed literal as a field/group/scalar literal.""" if isinstance(v, int) and not isinstance(v, bool): return f"{v}{suffix}" - if isinstance(v, str) and re.fullmatch(rf"\d+{suffix}", v): + # Signed literals are valid for mod-p types ("-1field" == p-1). + if isinstance(v, str) and re.fullmatch(rf"-?\d+{suffix}", v): return v raise ValueError(f"Expected int or '{suffix}' literal, got {v!r}") diff --git a/sdk/python/tests/fixtures/shield_swap_v3.abi.json b/sdk/python/tests/fixtures/shield_swap_v3.abi.json index bc3e9581..5a743502 100644 --- a/sdk/python/tests/fixtures/shield_swap_v3.abi.json +++ b/sdk/python/tests/fixtures/shield_swap_v3.abi.json @@ -1,2654 +1,2655 @@ { - "program": "shield_swap_v3.aleo", - "structs": [ + "_source": "aleo-viem@625fa05 packages/shield-swap/codegen/abi/shield_swap_v3.json (aleo-abi output for shield_swap_v3.aleo, testnet)", + "program": "shield_swap_v3.aleo", + "structs": [ + { + "path": [ + "SwapRequest" + ], + "fields": [ { - "path": [ + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "zero_for_one", + "ty": { + "Primitive": "Boolean" + } + }, + { + "name": "amount_in", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount_out_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "sqrt_price_limit", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "recipient", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "nonce", + "ty": { + "Primitive": { + "UInt": "U64" + } + } + }, + { + "name": "deadline", + "ty": { + "Primitive": { + "UInt": "U32" + } + } + } + ] + }, + { + "path": [ + "SwapHop" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "zero_for_one", + "ty": { + "Primitive": "Boolean" + } + }, + { + "name": "sqrt_price_limit", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + }, + { + "path": [ + "SwapMultiHopRequest" + ], + "fields": [ + { + "name": "token_in", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token_out", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_in", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount_out_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "recipient", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "hop0", + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "hop1", + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "hop2", + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "hop_count", + "ty": { + "Primitive": { + "UInt": "U8" + } + } + }, + { + "name": "nonce", + "ty": { + "Primitive": { + "UInt": "U64" + } + } + }, + { + "name": "deadline", + "ty": { + "Primitive": { + "UInt": "U32" + } + } + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + } + } + ] + }, + { + "path": [ + "MintPositionRequest" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "tick_lower", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_upper", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "amount0_desired", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount1_desired", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount0_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount1_min", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tick_lower_hint", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_upper_hint", + "ty": { + "Primitive": { + "Int": "I32" + } + } + } + ] + }, + { + "path": [ + "PoolState" + ], + "fields": [ + { + "name": "token0", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token1", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "fee", + "ty": { + "Primitive": { + "UInt": "U16" + } + } + }, + { + "name": "enabled", + "ty": { + "Primitive": "Boolean" + } + }, + { + "name": "scale0", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "scale1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + }, + { + "path": [ + "Slot" + ], + "fields": [ + { + "name": "tick", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_spacing", + "ty": { + "Primitive": { + "UInt": "U32" + } + } + }, + { + "name": "sqrt_price", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_protocol", + "ty": { + "Primitive": { + "UInt": "U8" + } + } + }, + { + "name": "liquidity", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_global0_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_global1_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_residual0_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_residual1_x_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "max_liquidity_per_tick", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "protocol_fees0", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "protocol_fees1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "next_init_below", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "next_init_above", + "ty": { + "Primitive": { + "Int": "I32" + } + } + } + ] + }, + { + "path": [ + "Tick" + ], + "fields": [ + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "liquidity_net", + "ty": { + "Primitive": { + "Int": "I128" + } + } + }, + { + "name": "liquidity_gross", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tick", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "fee_growth_outside0_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_outside1_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "prev", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "next", + "ty": { + "Primitive": { + "Int": "I32" + } + } + } + ] + }, + { + "path": [ + "Position" + ], + "fields": [ + { + "name": "token_id", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "pool", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "tick_lower", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "tick_upper", + "ty": { + "Primitive": { + "Int": "I32" + } + } + }, + { + "name": "liquidity", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_inside0_last_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "fee_growth_inside1_last_64", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tokens_owed0", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "tokens_owed1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + }, + { + "path": [ + "PairKey" + ], + "fields": [ + { + "name": "token0", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token1", + "ty": { + "Primitive": "Field" + } + } + ] + }, + { + "path": [ + "SwapOutput" + ], + "fields": [ + { + "name": "recipient", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + } + }, + { + "name": "token_in", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "token_out", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_out", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "amount_remaining", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "token_in_1", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_remaining_1", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + }, + { + "name": "token_in_2", + "ty": { + "Primitive": "Field" + } + }, + { + "name": "amount_remaining_2", + "ty": { + "Primitive": { + "UInt": "U128" + } + } + } + ] + } + ], + "records": [ + { + "path": [ + "PositionNFT" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "token_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token0_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token1_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "pool", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "tick_lower", + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Private" + }, + { + "name": "tick_upper", + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Private" + } + ] + }, + { + "path": [ + "SwapComplianceRecord" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "swap_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token_in", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token_out", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "request", + "ty": { + "Struct": { + "path": [ "SwapRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Private" + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "blinded_address", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + ] + }, + { + "path": [ + "MultiHopSwapComplianceRecord" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "swap_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "request", + "ty": { + "Struct": { + "path": [ + "SwapMultiHopRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Private" + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "blinded_address", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + ] + }, + { + "path": [ + "MintComplianceRecord" + ], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "token_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token0_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "token1_id", + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + }, + { + "name": "request", + "ty": { + "Struct": { + "path": [ + "MintPositionRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Private" + }, + { + "name": "caller", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + }, + { + "name": "recipient", + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + ] + } + ], + "mappings": [ + { + "name": "pools", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "PoolState" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "slots", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "Slot" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "ticks", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "Tick" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "initialized_pools", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "tick_spacings", + "key": { + "Primitive": { + "UInt": "U32" + } + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "fee_tiers", + "key": { + "Primitive": { + "UInt": "U16" + } + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "fee_to_tick_spacing", + "key": { + "Primitive": { + "UInt": "U16" + } + }, + "value": { + "Primitive": { + "UInt": "U32" + } + } + }, + { + "name": "positions", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "Position" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "swap_outputs", + "key": { + "Primitive": "Field" + }, + "value": { + "Struct": { + "path": [ + "SwapOutput" + ], + "program": "shield_swap_v3.aleo" + } + } + }, + { + "name": "admin", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Address" + } + }, + { + "name": "pending_admin", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Address" + } + }, + { + "name": "used_blinded_addresses", + "key": { + "Primitive": "Address" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "token_decimals", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U8" + } + } + }, + { + "name": "pool_creation_is_open", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "global_paused", + "key": { + "Primitive": "Boolean" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "token_allowed", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "token_paused", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "pair_paused", + "key": { + "Struct": { + "path": [ + "PairKey" + ], + "program": "shield_swap_v3.aleo" + } + }, + "value": { + "Primitive": "Boolean" + } + }, + { + "name": "frozen_position", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U32" + } + } + } + ], + "storage_variables": [], + "functions": [ + { + "name": "transfer_admin", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "accept_admin", + "inputs": [], + "outputs": [ + "Final" + ] + }, + { + "name": "add_tick_spacing", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "add_fee_tier", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U16" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "bind_fee_to_tick_spacing", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U16" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_token_decimals", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_pool_enabled", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_pool_creation_is_open", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_global_paused", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "allow_token", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_token_paused", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_pair_paused", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "freeze_position", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "unfreeze_position", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "set_fee_protocol", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "collect_protocol", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + } + ], + "outputs": [ + "Final" + ] + }, + { + "name": "create_pool", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U16" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + "Final" + ] + }, + { + "name": "mint", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + "DynamicRecord", + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "MintPositionRequest" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Record": { + "path": [ + "PositionNFT" ], - "fields": [ - { - "name": "pool", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "zero_for_one", - "ty": { - "Primitive": "Boolean" - } - }, - { - "name": "amount_in", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "amount_out_min", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "sqrt_price_limit", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "recipient", - "ty": { - "Primitive": "Address" - } - }, - { - "name": "nonce", - "ty": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "deadline", - "ty": { - "Primitive": { - "UInt": "U32" - } - } - } - ] + "program": "shield_swap_v3.aleo" + } + }, + "DynamicRecord", + "DynamicRecord", + { + "Record": { + "path": [ + "MintComplianceRecord" + ], + "program": "shield_swap_v3.aleo" + } + }, + "Final" + ] + }, + { + "name": "decrease_liquidity", + "inputs": [ + { + "Record": { + "path": [ + "PositionNFT" + ], + "program": "shield_swap_v3.aleo" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "path": [ - "SwapHop" - ], - "fields": [ - { - "name": "pool", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "zero_for_one", - "ty": { - "Primitive": "Boolean" - } - }, - { - "name": "sqrt_price_limit", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - } - ] + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } }, { + "Record": { "path": [ - "SwapMultiHopRequest" + "PositionNFT" ], - "fields": [ - { - "name": "token_in", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "token_out", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "amount_in", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "amount_out_min", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "recipient", - "ty": { - "Primitive": "Address" - } - }, - { - "name": "hop0", - "ty": { - "Struct": { - "path": [ - "SwapHop" - ], - "program": "shield_swap_v3.aleo" - } - } - }, - { - "name": "hop1", - "ty": { - "Struct": { - "path": [ - "SwapHop" - ], - "program": "shield_swap_v3.aleo" - } - } - }, - { - "name": "hop2", - "ty": { - "Struct": { - "path": [ - "SwapHop" - ], - "program": "shield_swap_v3.aleo" - } - } - }, - { - "name": "hop_count", - "ty": { - "Primitive": { - "UInt": "U8" - } - } - }, - { - "name": "nonce", - "ty": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "deadline", - "ty": { - "Primitive": { - "UInt": "U32" - } - } - }, - { - "name": "caller", - "ty": { - "Primitive": "Address" - } - } - ] + "program": "shield_swap_v3.aleo" + } }, + "Final" + ] + }, + { + "name": "increase_liquidity", + "inputs": [ { + "Record": { "path": [ - "MintPositionRequest" + "PositionNFT" ], - "fields": [ - { - "name": "pool", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "tick_lower", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "tick_upper", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "amount0_desired", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "amount1_desired", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "amount0_min", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "amount1_min", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "tick_lower_hint", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "tick_upper_hint", - "ty": { - "Primitive": { - "Int": "I32" - } - } - } - ] + "program": "shield_swap_v3.aleo" + } }, + "DynamicRecord", + "DynamicRecord", { - "path": [ - "PoolState" - ], - "fields": [ - { - "name": "token0", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "token1", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "fee", - "ty": { - "Primitive": { - "UInt": "U16" - } - } - }, - { - "name": "enabled", - "ty": { - "Primitive": "Boolean" - } - }, - { - "name": "scale0", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "scale1", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - } - ] + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "path": [ - "Slot" - ], - "fields": [ - { - "name": "tick", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "tick_spacing", - "ty": { - "Primitive": { - "UInt": "U32" - } - } - }, - { - "name": "sqrt_price", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_protocol", - "ty": { - "Primitive": { - "UInt": "U8" - } - } - }, - { - "name": "liquidity", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_growth_global0_x_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_growth_global1_x_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_residual0_x_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_residual1_x_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "max_liquidity_per_tick", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "protocol_fees0", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "protocol_fees1", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "next_init_below", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "next_init_above", - "ty": { - "Primitive": { - "Int": "I32" - } - } - } - ] + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "path": [ - "Tick" - ], - "fields": [ - { - "name": "pool", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "liquidity_net", - "ty": { - "Primitive": { - "Int": "I128" - } - } - }, - { - "name": "liquidity_gross", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "tick", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "fee_growth_outside0_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_growth_outside1_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "prev", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "next", - "ty": { - "Primitive": { - "Int": "I32" - } - } - } - ] + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "path": [ - "Position" - ], - "fields": [ - { - "name": "token_id", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "pool", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "tick_lower", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "tick_upper", - "ty": { - "Primitive": { - "Int": "I32" - } - } - }, - { - "name": "liquidity", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_growth_inside0_last_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "fee_growth_inside1_last_64", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "tokens_owed0", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "tokens_owed1", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - } - ] + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } }, { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Record": { "path": [ - "PairKey" + "PositionNFT" ], - "fields": [ - { - "name": "token0", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "token1", - "ty": { - "Primitive": "Field" - } - } - ] + "program": "shield_swap_v3.aleo" + } }, + "DynamicRecord", + "DynamicRecord", + "Final" + ] + }, + { + "name": "collect", + "inputs": [ { + "Record": { "path": [ - "SwapOutput" + "PositionNFT" ], - "fields": [ - { - "name": "recipient", - "ty": { - "Primitive": "Address" - } - }, - { - "name": "caller", - "ty": { - "Primitive": "Address" - } - }, - { - "name": "token_in", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "token_out", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "amount_out", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "amount_remaining", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "token_in_1", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "amount_remaining_1", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - }, - { - "name": "token_in_2", - "ty": { - "Primitive": "Field" - } - }, - { - "name": "amount_remaining_2", - "ty": { - "Primitive": { - "UInt": "U128" - } - } - } - ] + "program": "shield_swap_v3.aleo" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Private" + } } - ], - "records": [ + ], + "outputs": [ { + "Record": { "path": [ - "PositionNFT" + "PositionNFT" ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "token_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "token0_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "token1_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "pool", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "tick_lower", - "ty": { - "Primitive": { - "Int": "I32" - } - }, - "mode": "Private" - }, - { - "name": "tick_upper", - "ty": { - "Primitive": { - "Int": "I32" - } - }, - "mode": "Private" - } - ] + "program": "shield_swap_v3.aleo" + } }, + "DynamicRecord", + "DynamicRecord", + "Final" + ] + }, + { + "name": "burn", + "inputs": [ { + "Record": { "path": [ - "SwapComplianceRecord" + "PositionNFT" ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "swap_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "token_in", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "token_out", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "request", - "ty": { - "Struct": { - "path": [ - "SwapRequest" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Private" - }, - { - "name": "caller", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "blinded_address", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - } - ] + "program": "shield_swap_v3.aleo" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + "Final" + ] + }, + { + "name": "swap", + "inputs": [ + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + "DynamicRecord", + { + "Record": { "path": [ - "MultiHopSwapComplianceRecord" + "SwapComplianceRecord" ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "swap_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "request", - "ty": { - "Struct": { - "path": [ - "SwapMultiHopRequest" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Private" - }, - { - "name": "caller", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "blinded_address", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - } - ] + "program": "shield_swap_v3.aleo" + } + }, + "Final" + ] + }, + { + "name": "claim_swap_output", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + "DynamicRecord", + "DynamicRecord", + "Final" + ] + }, + { + "name": "swap_multi_hop", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Private" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Address" + }, + "mode": "Public" + } + }, + "DynamicRecord", + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } }, { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Struct": { + "path": [ + "SwapHop" + ], + "program": "shield_swap_v3.aleo" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": "Field" + }, + "mode": "Public" + } + }, + "DynamicRecord", + { + "Record": { "path": [ - "MintComplianceRecord" + "MultiHopSwapComplianceRecord" ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "token_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "token0_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "token1_id", - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - }, - { - "name": "request", - "ty": { - "Struct": { - "path": [ - "MintPositionRequest" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Private" - }, - { - "name": "caller", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - }, - { - "name": "recipient", - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - } - ] - } - ], - "mappings": [ - { - "name": "pools", - "key": { - "Primitive": "Field" - }, - "value": { - "Struct": { - "path": [ - "PoolState" - ], - "program": "shield_swap_v3.aleo" - } - } + "program": "shield_swap_v3.aleo" + } }, + "Final" + ] + }, + { + "name": "claim_multi_hop_output", + "inputs": [ { - "name": "slots", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": "Field" }, - "value": { - "Struct": { - "path": [ - "Slot" - ], - "program": "shield_swap_v3.aleo" - } - } + "mode": "Private" + } }, { - "name": "ticks", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": "Address" }, - "value": { - "Struct": { - "path": [ - "Tick" - ], - "program": "shield_swap_v3.aleo" - } - } + "mode": "Public" + } }, { - "name": "initialized_pools", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": "Field" }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } }, { - "name": "tick_spacings", - "key": { - "Primitive": { - "UInt": "U32" - } + "Plaintext": { + "ty": { + "Primitive": "Field" }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } }, { - "name": "fee_tiers", - "key": { - "Primitive": { - "UInt": "U16" - } + "Plaintext": { + "ty": { + "Primitive": "Field" }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } }, { - "name": "fee_to_tick_spacing", - "key": { - "Primitive": { - "UInt": "U16" - } + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": { - "UInt": "U32" - } - } + "mode": "Public" + } }, { - "name": "positions", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Struct": { - "path": [ - "Position" - ], - "program": "shield_swap_v3.aleo" - } - } + "mode": "Public" + } }, { - "name": "swap_outputs", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": "Field" }, - "value": { - "Struct": { - "path": [ - "SwapOutput" - ], - "program": "shield_swap_v3.aleo" - } - } + "mode": "Public" + } }, { - "name": "admin", - "key": { - "Primitive": "Boolean" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": "Address" - } + "mode": "Public" + } }, { - "name": "pending_admin", - "key": { - "Primitive": "Boolean" + "Plaintext": { + "ty": { + "Primitive": "Field" }, - "value": { - "Primitive": "Address" - } + "mode": "Public" + } }, { - "name": "used_blinded_addresses", - "key": { - "Primitive": "Address" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } + } + ], + "outputs": [ + "DynamicRecord", + "DynamicRecord", + "DynamicRecord", + "DynamicRecord", + "Final" + ] + } + ], + "views": [ + { + "name": "view_sqrt_price_at_tick", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "Int": "I32" + } + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + } + ] + }, + { + "name": "view_amounts_for_liquidity", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "name": "token_decimals", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": { - "UInt": "U8" - } - } + "mode": "Public" + } }, { - "name": "pool_creation_is_open", - "key": { - "Primitive": "Boolean" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } }, { - "name": "global_paused", - "key": { - "Primitive": "Boolean" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } }, { - "name": "token_allowed", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": "Boolean" }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "name": "token_paused", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } + } + ] + }, + { + "name": "view_liquidity_for_amounts", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } }, { - "name": "pair_paused", - "key": { - "Struct": { - "path": [ - "PairKey" - ], - "program": "shield_swap_v3.aleo" - } + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": "Boolean" - } + "mode": "Public" + } }, { - "name": "frozen_position", - "key": { - "Primitive": "Field" + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } }, - "value": { - "Primitive": { - "UInt": "U32" - } - } + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } } - ], - "storage_variables": [], - "functions": [ - { - "name": "transfer_admin", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "accept_admin", - "inputs": [], - "outputs": [ - "Final" - ] - }, - { - "name": "add_tick_spacing", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U32" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "add_fee_tier", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U16" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "bind_fee_to_tick_spacing", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U16" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U32" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_token_decimals", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_pool_enabled", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_pool_creation_is_open", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_global_paused", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "allow_token", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_token_paused", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_pair_paused", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "freeze_position", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "unfreeze_position", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "set_fee_protocol", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "collect_protocol", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - } - ], - "outputs": [ - "Final" - ] - }, - { - "name": "create_pool", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U16" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U32" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "Int": "I32" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - }, - "Final" - ] - }, - { - "name": "mint", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - } - }, - "DynamicRecord", - "DynamicRecord", - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - } - }, - { - "Plaintext": { - "ty": { - "Struct": { - "path": [ - "MintPositionRequest" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - "DynamicRecord", - "DynamicRecord", - { - "Record": { - "path": [ - "MintComplianceRecord" - ], - "program": "shield_swap_v3.aleo" - } - }, - "Final" - ] - }, - { - "name": "decrease_liquidity", - "inputs": [ - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - "Final" - ] - }, - { - "name": "increase_liquidity", - "inputs": [ - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - "DynamicRecord", - "DynamicRecord", - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "Int": "I32" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "Int": "I32" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - "DynamicRecord", - "DynamicRecord", - "Final" - ] - }, - { - "name": "collect", - "inputs": [ - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Private" - } - } - ], - "outputs": [ - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - }, - "DynamicRecord", - "DynamicRecord", - "Final" - ] - }, - { - "name": "burn", - "inputs": [ - { - "Record": { - "path": [ - "PositionNFT" - ], - "program": "shield_swap_v3.aleo" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - }, - "Final" - ] - }, - { - "name": "swap", - "inputs": [ - "DynamicRecord", - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U32" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - "DynamicRecord", - { - "Record": { - "path": [ - "SwapComplianceRecord" - ], - "program": "shield_swap_v3.aleo" - } - }, - "Final" - ] - }, - { - "name": "claim_swap_output", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "DynamicRecord", - "DynamicRecord", - "Final" - ] - }, - { - "name": "swap_multi_hop", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - }, - "DynamicRecord", - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Struct": { - "path": [ - "SwapHop" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Struct": { - "path": [ - "SwapHop" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Struct": { - "path": [ - "SwapHop" - ], - "program": "shield_swap_v3.aleo" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U32" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - "DynamicRecord", - { - "Record": { - "path": [ - "MultiHopSwapComplianceRecord" - ], - "program": "shield_swap_v3.aleo" - } - }, - "Final" - ] - }, - { - "name": "claim_multi_hop_output", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Private" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Address" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Field" - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - "DynamicRecord", - "DynamicRecord", - "DynamicRecord", - "DynamicRecord", - "Final" - ] + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } } - ], - "views": [ - { - "name": "view_sqrt_price_at_tick", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "Int": "I32" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ] - }, - { - "name": "view_amounts_for_liquidity", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ] - }, - { - "name": "view_liquidity_for_amounts", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ] - }, - { - "name": "view_compute_swap_step", - "inputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U32" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": "Boolean" - }, - "mode": "Public" - } - } - ], - "outputs": [ - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - }, - { - "Plaintext": { - "ty": { - "Primitive": { - "UInt": "U128" - } - }, - "mode": "Public" - } - } - ] + ] + }, + { + "name": "view_compute_swap_step", + "inputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U32" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": "Boolean" + }, + "mode": "Public" + } + } + ], + "outputs": [ + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } + }, + { + "Plaintext": { + "ty": { + "Primitive": { + "UInt": "U128" + } + }, + "mode": "Public" + } } - ] + ] + } + ] } \ No newline at end of file diff --git a/sdk/python/tests/test_codegen_emit.py b/sdk/python/tests/test_codegen_emit.py index f01f49ca..3bc3b736 100644 --- a/sdk/python/tests/test_codegen_emit.py +++ b/sdk/python/tests/test_codegen_emit.py @@ -102,3 +102,39 @@ def test_emit_module_sorts_nested_structs_first(): ns: dict = {} exec(compile(emit_module(flipped), "generated", "exec"), ns) assert ns["Outer"].from_plaintext("{ inner: { a: 7u8 }, pool: 5field }").inner.a == 7 + + +def test_emit_module_carries_abi_constant(): + ns: dict = {} + exec(compile(emit_module(MINI_ABI), "generated", "exec"), ns) + assert ns["ABI"]["program"] == "mini.aleo" + assert ns["ABI"]["mappings"][1]["key"] == {"Primitive": "Field"} + + +def test_emit_rejects_bad_identifiers(): + import pytest + bad_field = {"path": ["S"], "fields": [{"name": "from", "ty": {"Primitive": "Field"}}]} + with pytest.raises(ValueError, match="from"): + emit_struct(bad_field) + injected = {"path": ["S"], "fields": [ + {"name": "a: int = __import__('os') #", "ty": {"Primitive": "Field"}}]} + with pytest.raises(ValueError): + emit_struct(injected) + with pytest.raises(ValueError, match="_nonce"): + emit_module(dict(MINI_ABI, records=[{"path": ["R"], "fields": [ + {"name": "_nonce", "ty": {"Primitive": "Field"}, "mode": "Private"}]}])) + + +def test_emit_module_rejects_unresolvable_struct_refs(): + import pytest + # Cross-program reference: the class would never be generated. + foreign = dict(MINI_ABI, structs=MINI_ABI["structs"] + [ + {"path": ["Uses"], "fields": [ + {"name": "x", "ty": {"Struct": {"path": ["Ext"], "program": "other.aleo"}}}]}]) + with pytest.raises(ValueError, match="Ext"): + emit_module(foreign) + # Duplicate terminal names: last-one-wins would silently drop a struct. + duped = dict(MINI_ABI, structs=MINI_ABI["structs"] + [ + {"path": ["Inner"], "fields": [{"name": "b", "ty": {"Primitive": "Boolean"}}]}]) + with pytest.raises(ValueError, match="Inner"): + emit_module(duped) diff --git a/sdk/python/tests/test_codegen_runtime.py b/sdk/python/tests/test_codegen_runtime.py index e27ce689..16811b94 100644 --- a/sdk/python/tests/test_codegen_runtime.py +++ b/sdk/python/tests/test_codegen_runtime.py @@ -61,9 +61,24 @@ def test_fmt_int_ranges(): fmt_int(True, "u8") # bool is not an int here +def test_parse_rejects_malformed_and_absent(): + with pytest.raises(ValueError): + parse_plaintext("{ a: 1u8 2u8 }") # missing comma between members + with pytest.raises(ValueError): + parse_plaintext("[1u8 2u8]") # missing comma between elements + with pytest.raises(ValueError): + parse_plaintext("null") # absent mapping entry + with pytest.raises(ValueError): + parse_plaintext("") + with pytest.raises(TypeError): + parse_plaintext(None) # absent value from the node + + def test_fmt_fieldlike_and_address(): assert fmt_fieldlike(123, "field") == "123field" assert fmt_fieldlike("123field", "field") == "123field" + assert fmt_fieldlike("-1field", "field") == "-1field" # mod-p negative + assert fmt_fieldlike(-1, "field") == "-1field" with pytest.raises(ValueError): fmt_fieldlike("123group", "field") # wrong suffix assert fmt_bool(True) == "true"