mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 06:01:11 +00:00
refactor(apps/amm): move the amm_client crate into modules/amm/ffi as amm_ffi
After the amm_module refactor, this crate is linked only by the module (the
UI delegates to modules().amm_module and links nothing), so its home under
apps/amm/ and the name "client" were both misnomers: it's the AMM business
logic the module wraps, reached across an FFI boundary — the same relationship
logos_execution_zone has with wallet_ffi. Co-locate it with the module that
owns it and name it for that role.
The FFI surface is unchanged — the exported functions are already amm_* (not
amm_client_*) — so only the crate, directory, generated header, dylib, and
package names move. The module's call sites are untouched; it just includes the
renamed header.
- apps/amm/client → modules/amm/ffi (git-tracked rename; history preserved)
- crate amm_client → amm_ffi: package name, include/amm_ffi.h, libamm_ffi.dylib,
AMM_FFI_H guard, build.rs output path, tests/public_api.rs import
- workspace member path + Cargo.lock
- flake.nix: pname, -p, header-copy path, dylib install_name, packages.amm_ffi,
ammModuleOutputs externalLibInputs, DYLD wrapper
- modules/amm: metadata external_libraries, CMakeLists EXTERNAL_LIBS, impl
#include + comments, README, flake note
- apps/amm/flake.nix: drop the now-dead amm_client external-lib input (the UI
links no external lib of its own)
Resulting layout:
modules/amm/
src/ # C++ module (amm_module_impl.{h,cpp})
ffi/ # Rust crate amm_ffi (Cargo.toml, src/, include/amm_ffi.h)
This commit is contained in:
Generated
+15
-15
@@ -77,7 +77,21 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amm_client"
|
||||
name = "amm_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"borsh",
|
||||
"lee_core",
|
||||
"risc0-zkvm",
|
||||
"ruint",
|
||||
"serde",
|
||||
"spel-framework-macros",
|
||||
"token_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amm_ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
@@ -97,20 +111,6 @@ dependencies = [
|
||||
"twap_oracle_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amm_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"borsh",
|
||||
"lee_core",
|
||||
"risc0-zkvm",
|
||||
"ruint",
|
||||
"serde",
|
||||
"spel-framework-macros",
|
||||
"token_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amm_program"
|
||||
version = "0.1.0"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"apps/amm/client",
|
||||
"modules/amm/ffi",
|
||||
"programs/token/core",
|
||||
"programs/token",
|
||||
"programs/token/methods",
|
||||
|
||||
@@ -26,7 +26,7 @@ add_subdirectory("${LOGOS_WALLET_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/share
|
||||
# generated *SimpleSource/*ViewPluginBase). Mirrors the LEZ wallet UI module.
|
||||
# The AMM business logic lives in the amm_module core module (declared as a
|
||||
# dependency in metadata.json, reached via modules().amm_module in the backend),
|
||||
# so the UI links no amm_client library of its own.
|
||||
# so the UI links no amm_ffi library of its own.
|
||||
logos_module(
|
||||
NAME amm_ui
|
||||
REP_FILE src/AmmUiBackend.rep
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ nix run .#amm-ui
|
||||
This builds and runs the application in development mode. The Logos bridge is unavailable in standalone mode, but the UI layout and mock data are fully functional.
|
||||
|
||||
Build just the AMM core module with `nix build .#amm-module`, or its underlying
|
||||
client crate with `nix build .#amm_client`. (Each UI is exposed under its own
|
||||
logic crate with `nix build .#amm_ffi`. (Each UI is exposed under its own
|
||||
name, so future apps are `nix run .#<name>` — there is no bare `nix run .`
|
||||
default.)
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ syntax, and the complete module build.
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
cargo +1.94.0 test -p amm_client
|
||||
cargo +1.94.0 clippy -p amm_client --all-targets -- -D warnings
|
||||
cargo +1.94.0 test -p amm_ffi
|
||||
cargo +1.94.0 clippy -p amm_ffi --all-targets -- -D warnings
|
||||
logos_qml=$(nix build github:logos-co/logos-design-system/6176f0d7a5dfeb64a7f0f98e7ca2bf71a4804772 --no-link --print-out-paths)
|
||||
amm_qml=$(nix build ./apps/amm#packages.x86_64-linux.default --no-link --print-out-paths)
|
||||
qt_qml=$(nix-store --query --requisites "$amm_qml" | rg -m1 -- '-qtdeclarative-[0-9]')
|
||||
|
||||
+8
-19
@@ -26,21 +26,15 @@
|
||||
inputs.logos-execution-zone.url =
|
||||
"github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05";
|
||||
};
|
||||
|
||||
amm_client.url = "path:../..";
|
||||
};
|
||||
|
||||
# NOTE: this flake is no longer built standalone. The amm_client crate
|
||||
# (the Rust C FFI library the AmmUiBackend C++ code links against) lives in
|
||||
# the repo-root flake, and referencing it from here would require either a
|
||||
# hardcoded absolute `git+file://` path or a `path:../..` input — the latter
|
||||
# fails flake evaluation because the app directory is copied into the Nix
|
||||
# store as its own flake root, so `../..` can't escape it there. Instead,
|
||||
# the repo-root flake.nix builds this module directly (src = ./apps/amm)
|
||||
# and resolves amm_client via `self`. The repo-root flake exposes the UI
|
||||
# as a named attribute (there is no bare `default`): run it from the repo root
|
||||
# with `nix run .#amm-ui`, and build just the FFI crate with
|
||||
# `nix build .#amm_client`.
|
||||
# NOTE: this flake is no longer built standalone; the repo-root flake.nix
|
||||
# builds the UI directly (src = ./apps/amm). The UI links no external lib of
|
||||
# its own — the AMM logic lives in the amm_ffi crate, which the amm_module
|
||||
# core module links; the UI reaches it via modules().amm_module (declared in
|
||||
# metadata.json `dependencies`). The repo-root flake exposes the UI as a named
|
||||
# attribute (there is no bare `default`): run it with `nix run .#amm-ui`, and
|
||||
# build the AMM logic crate with `nix build .#amm_ffi`.
|
||||
outputs = inputs@{ logos-module-builder, shared_wallet, ... }:
|
||||
logos-module-builder.lib.mkLogosQmlModule {
|
||||
src = ./.;
|
||||
@@ -49,12 +43,7 @@
|
||||
preConfigure = ''
|
||||
cmakeFlagsArray+=("-DLOGOS_WALLET_SOURCE_DIR=${shared_wallet}")
|
||||
'';
|
||||
externalLibInputs = {
|
||||
amm_client = {
|
||||
input = inputs.amm_client;
|
||||
packages.default = "amm_client";
|
||||
};
|
||||
};
|
||||
externalLibInputs = { };
|
||||
postInstall = ''
|
||||
# The builder installs the view under lib/qml after this hook. Its
|
||||
# import descriptor points back to this compiled shared QML module.
|
||||
|
||||
@@ -74,7 +74,7 @@ private:
|
||||
|
||||
LogosAPI* m_logosAPI;
|
||||
// Handle for the amm_module core module (resolvePool / swapExactInput /
|
||||
// tokenList / new-position). The module wraps the amm_client brain and
|
||||
// tokenList / new-position). The module wraps the amm_ffi brain and
|
||||
// reaches the shared wallet through its own logos_execution_zone dependency;
|
||||
// this backend keeps a thin LogosModules over the same LogosAPI as the
|
||||
// wallet provider so both resolve that one shared wallet instance.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
};
|
||||
|
||||
# The AMM QML UI module (apps/amm) is built from this same flake so it can
|
||||
# reference the amm_client crate package via `self` — no filesystem
|
||||
# reference the amm_ffi crate package via `self` — no filesystem
|
||||
# path or git-remote reference to this repo is needed (see apps/amm/flake.nix
|
||||
# history: a `git+file://` URL pointing at a local checkout is
|
||||
# machine-specific and not portable).
|
||||
@@ -54,19 +54,19 @@
|
||||
craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain;
|
||||
|
||||
# Whole workspace: crane needs Cargo.lock + all path deps (amm_core,
|
||||
# twap_oracle_core, token_core, ...) to resolve `-p amm_client`.
|
||||
# twap_oracle_core, token_core, ...) to resolve `-p amm_ffi`.
|
||||
src = ./.;
|
||||
|
||||
commonArgs = {
|
||||
inherit src;
|
||||
strictDeps = true;
|
||||
pname = "amm_client";
|
||||
pname = "amm_ffi";
|
||||
version = "0.1.0";
|
||||
# CRITICAL: scope to ONLY this crate. The workspace also contains
|
||||
# `amm/methods` etc. whose build.rs compiles the risc0 guest (which
|
||||
# WOULD invoke Metal on darwin). `-p amm_client` never builds
|
||||
# WOULD invoke Metal on darwin). `-p amm_ffi` never builds
|
||||
# those crates or their build scripts.
|
||||
cargoExtraArgs = "-p amm_client";
|
||||
cargoExtraArgs = "-p amm_ffi";
|
||||
doCheck = false;
|
||||
# NOTE: cbindgen is used here as a Cargo *build-dependency*
|
||||
# (invoked from build.rs via its Rust library API), not as the
|
||||
@@ -76,17 +76,17 @@
|
||||
|
||||
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
|
||||
|
||||
# The single AMM host FFI crate (apps/amm/client): swap + new-position
|
||||
# The single AMM host FFI crate (modules/amm/ffi): swap + new-position
|
||||
# operations behind one JSON C ABI. Its header is cbindgen-generated
|
||||
# into include/amm_client.h at build time.
|
||||
ammClient = craneLib.buildPackage (
|
||||
# into include/amm_ffi.h at build time.
|
||||
ammFfi = craneLib.buildPackage (
|
||||
commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
postInstall =
|
||||
''
|
||||
mkdir -p $out/include
|
||||
cp apps/amm/client/include/amm_client.h $out/include/
|
||||
cp modules/amm/ffi/include/amm_ffi.h $out/include/
|
||||
''
|
||||
+ pkgs.lib.optionalString pkgs.stdenv.isDarwin ''
|
||||
# Set the dylib's install-name to its ABSOLUTE store path (NOT
|
||||
@@ -95,20 +95,20 @@
|
||||
# @rpath id fails to dlopen at launch. An absolute /nix/store id
|
||||
# is recorded in the plugin's LC_LOAD_DYLIB, kept in the closure
|
||||
# by Nix, and resolved directly at runtime — no rpath needed.
|
||||
if [ -f $out/lib/libamm_client.dylib ]; then
|
||||
install_name_tool -id "$out/lib/libamm_client.dylib" $out/lib/libamm_client.dylib
|
||||
if [ -f $out/lib/libamm_ffi.dylib ]; then
|
||||
install_name_tool -id "$out/lib/libamm_ffi.dylib" $out/lib/libamm_ffi.dylib
|
||||
fi
|
||||
'';
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
packages.default = ammClient;
|
||||
packages.amm_client = ammClient;
|
||||
packages.default = ammFfi;
|
||||
packages.amm_ffi = ammFfi;
|
||||
}
|
||||
);
|
||||
|
||||
# The AMM QML UI module (apps/amm). It links no amm_client library of its
|
||||
# The AMM QML UI module (apps/amm). It links no amm_ffi library of its
|
||||
# own — the AMM logic lives in the amm_module core module, which the UI
|
||||
# depends on (declared in apps/amm/metadata.json, reached via
|
||||
# modules().amm_module in the backend) alongside the logos_execution_zone
|
||||
@@ -123,7 +123,7 @@
|
||||
# direct dep too, so both the UI and amm_module resolve the one shared
|
||||
# wallet instance.
|
||||
flakeInputs = inputs // { amm_module = ammModuleOutputs; };
|
||||
# The UI links no external lib of its own — the AMM brain (amm_client) is
|
||||
# The UI links no external lib of its own — the AMM brain (amm_ffi) is
|
||||
# linked by amm_module, which the UI reaches via modules().amm_module.
|
||||
externalLibInputs = { };
|
||||
# The AMM UI links the shared C++ wallet access lib and bundles the
|
||||
@@ -159,7 +159,7 @@
|
||||
appPkgs = appOutputs.packages or { };
|
||||
|
||||
# AMM core module (modules/amm): the AMM business logic as a headless
|
||||
# `core` Logos module. It links the amm_client crate (the transport-
|
||||
# `core` Logos module. It links the amm_ffi crate (the transport-
|
||||
# independent AMM brain, resolved via `self`) and depends on the
|
||||
# logos_execution_zone wallet module (declared in modules/amm/metadata.json,
|
||||
# reached via modules().logos_execution_zone in the impl). Exposed as the
|
||||
@@ -169,25 +169,25 @@
|
||||
configFile = ./modules/amm/metadata.json;
|
||||
flakeInputs = inputs;
|
||||
externalLibInputs = {
|
||||
amm_client = { input = self; packages.default = "amm_client"; };
|
||||
amm_ffi = { input = self; packages.default = "amm_ffi"; };
|
||||
};
|
||||
};
|
||||
ammModulePkgs = ammModuleOutputs.packages or { };
|
||||
|
||||
# Wrap the app launcher to export DYLD_FALLBACK_LIBRARY_PATH pointing at the
|
||||
# amm_client lib. The logos module builder links the plugin against
|
||||
# @rpath/libamm_client.dylib but does NOT stage that dylib into the
|
||||
# amm_ffi lib. The logos module builder links the plugin against
|
||||
# @rpath/libamm_ffi.dylib but does NOT stage that dylib into the
|
||||
# plugin-dir it loads at runtime, so dlopen fails with "Failed to load UI
|
||||
# plugin". Adding the crate's store lib dir to DYLD's fallback search path
|
||||
# lets the loader find it (the store path stays in the closure).
|
||||
wrapWithDyld = system: app:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; };
|
||||
ammClient = crateOutputs.packages.${system}.amm_client;
|
||||
ammFfi = crateOutputs.packages.${system}.amm_ffi;
|
||||
in
|
||||
app // {
|
||||
program = "${pkgs.writeShellScript "run-amm-ui" ''
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="${ammClient}/lib''${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="${ammFfi}/lib''${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}"
|
||||
exec ${app.program} "$@"
|
||||
''}";
|
||||
};
|
||||
|
||||
@@ -16,9 +16,9 @@ string(JSON MODULE_NAME GET ${METADATA_JSON} name)
|
||||
|
||||
# Universal core module: we write only the impl class; the Qt plugin glue is
|
||||
# generated from src/amm_module_impl.h (because metadata.json sets
|
||||
# "interface": "universal"). EXTERNAL_LIBS links amm_client — the pure-Rust,
|
||||
# "interface": "universal"). EXTERNAL_LIBS links amm_ffi — the pure-Rust,
|
||||
# transport-independent AMM brain (PDA/decode/quote/plan/encode) exposed as a
|
||||
# JSON FFI (amm_client.h), the same library the UI links. The chain I/O
|
||||
# JSON FFI (amm_ffi.h), the same library the UI links. The chain I/O
|
||||
# dependency (logos_execution_zone) is declared in metadata.json and reached via
|
||||
# modules().logos_execution_zone in the impl, so it is NOT listed here.
|
||||
logos_module(
|
||||
@@ -27,5 +27,5 @@ logos_module(
|
||||
src/amm_module_impl.h
|
||||
src/amm_module_impl.cpp
|
||||
EXTERNAL_LIBS
|
||||
amm_client
|
||||
amm_ffi
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ for the module framework.
|
||||
## What it does
|
||||
|
||||
The impl class `AmmModuleImpl` (`src/amm_module_impl.{h,cpp}`) is a **transport
|
||||
adapter**: the AMM domain math lives in the Rust `amm_client` crate (a
|
||||
adapter**: the AMM domain math lives in the Rust `amm_ffi` crate (a
|
||||
transport-independent JSON FFI), and this module sequences those pure ops with
|
||||
chain I/O delegated to the `logos_execution_zone` wallet module. Its public
|
||||
methods (the module API is generated from the header) are:
|
||||
@@ -41,7 +41,7 @@ methods (the module API is generated from the header) are:
|
||||
## How it fits together
|
||||
|
||||
```
|
||||
QML ──modules().amm_module──┐ ┌── amm_client (Rust cdylib, JSON FFI):
|
||||
QML ──modules().amm_module──┐ ┌── amm_ffi (Rust cdylib, JSON FFI):
|
||||
CLI ──logoscore call────────┤ │ PDA derivation, account decode,
|
||||
▼ │ quote/plan math, instruction encoding
|
||||
amm_module ───┤ — transport-independent (external_libraries)
|
||||
@@ -51,7 +51,7 @@ CLI ──logoscore call────────┤ │ PDA derivation
|
||||
via modules().logos_execution_zone.*
|
||||
```
|
||||
|
||||
The `amm_client` crate is deliberately I/O-free — each op takes the account data
|
||||
The `amm_ffi` crate is deliberately I/O-free — each op takes the account data
|
||||
it needs as JSON input and returns a JSON result. This module is the transport
|
||||
adapter the crate is designed to require: it fetches accounts through the wallet
|
||||
module (`get_account_public`, `list_accounts`), hands them to the pure Rust op,
|
||||
@@ -86,12 +86,12 @@ The UI passes `QString` (→ `QVariant` → string branch) and is unaffected.
|
||||
|
||||
## Build
|
||||
|
||||
Built from the **repo-root** flake (which provides the `amm_client` library it
|
||||
Built from the **repo-root** flake (which provides the `amm_ffi` library it
|
||||
links):
|
||||
|
||||
```bash
|
||||
nix build .#amm-module
|
||||
# output: result/lib/amm_module_plugin.dylib (+ libamm_client.dylib)
|
||||
# output: result/lib/amm_module_plugin.dylib (+ libamm_ffi.dylib)
|
||||
```
|
||||
|
||||
## Runtime configuration
|
||||
@@ -123,7 +123,7 @@ Have all of the following in place before staging the modules dir:
|
||||
`lm` introspects a built plugin without running it — handy to confirm the API
|
||||
(`lm result/lib/amm_module_plugin.dylib` shows methods, signatures, deps).
|
||||
|
||||
3. **This module, built** (produces `amm_module_plugin.dylib` + `libamm_client.dylib`):
|
||||
3. **This module, built** (produces `amm_module_plugin.dylib` + `libamm_ffi.dylib`):
|
||||
|
||||
```bash
|
||||
nix build .#amm-module # from the repo root; output under result/lib/
|
||||
@@ -168,7 +168,7 @@ verify the manifest hashes at load time.
|
||||
modules/
|
||||
amm_module/
|
||||
amm_module_plugin.dylib
|
||||
libamm_client.dylib
|
||||
libamm_ffi.dylib
|
||||
variant # one line: darwin-arm64-dev
|
||||
manifest.json
|
||||
logos_execution_zone/
|
||||
@@ -268,6 +268,6 @@ the fork pinned as the `logos_execution_zone` input. See
|
||||
- **`swapExactOutput` is not exposed yet.** The on-chain program supports it
|
||||
(`amm_core::Instruction::SwapExactOutput`, identical account layout to
|
||||
`SwapExactInput`), but the client path was only ever built for exact-input:
|
||||
`amm_client` has no exact-output op and neither the UI nor this module has a
|
||||
`amm_ffi` has no exact-output op and neither the UI nor this module has a
|
||||
`swapExactOutput` method. Adding it is a near-copy of the exact-input path — an
|
||||
`amm_swap_exact_output_*` op in the crate plus a `swapExactOutput` method here.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "amm_client"
|
||||
name = "amm_ffi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -3,7 +3,7 @@ fn main() {
|
||||
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is set by cargo");
|
||||
cbindgen::generate(&crate_dir)
|
||||
.expect("cbindgen")
|
||||
.write_to_file(format!("{crate_dir}/include/amm_client.h"));
|
||||
.write_to_file(format!("{crate_dir}/include/amm_ffi.h"));
|
||||
println!("cargo:rerun-if-changed=src");
|
||||
println!("cargo:rerun-if-changed=cbindgen.toml");
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
language = "C"
|
||||
include_guard = "AMM_CLIENT_H"
|
||||
include_guard = "AMM_FFI_H"
|
||||
pragma_once = true
|
||||
cpp_compat = true
|
||||
autogen_warning = "/* Generated by cbindgen. Do not edit. */"
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef AMM_CLIENT_H
|
||||
#define AMM_CLIENT_H
|
||||
#ifndef AMM_FFI_H
|
||||
#define AMM_FFI_H
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -46,4 +46,4 @@ void amm_free(char *value);
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif /* AMM_CLIENT_H */
|
||||
#endif /* AMM_FFI_H */
|
||||
@@ -1,4 +1,4 @@
|
||||
use amm_client::{config_id, ConfigIdRequest};
|
||||
use amm_ffi::{config_id, ConfigIdRequest};
|
||||
|
||||
#[test]
|
||||
fn direct_rust_api_does_not_require_ffi() {
|
||||
@@ -11,13 +11,13 @@
|
||||
logos_execution_zone.url = "github:gravityblast/logos-execution-zone-module?ref=fix/generic-tx-instruction-bstr";
|
||||
};
|
||||
|
||||
# NOTE: like apps/amm, this flake is NOT built standalone. The amm_client
|
||||
# NOTE: like apps/amm, this flake is NOT built standalone. The amm_ffi
|
||||
# crate this module links (the Rust JSON-FFI brain) lives in the repo-root
|
||||
# flake, and referencing it from here would require a hardcoded `git+file://`
|
||||
# path or a `path:../..` input — the latter fails flake evaluation because this
|
||||
# dir is copied into the Nix store as its own flake root, so `../..` can't
|
||||
# escape it. Instead, the repo-root flake.nix builds this module directly
|
||||
# (src = ./modules/amm) and resolves amm_client via `self`. Build it from
|
||||
# (src = ./modules/amm) and resolves amm_ffi via `self`. Build it from
|
||||
# the repo root:
|
||||
# nix build .#amm-module
|
||||
outputs = inputs@{ logos-module-builder, ... }:
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"runtime": []
|
||||
},
|
||||
"external_libraries": [
|
||||
{ "name": "amm_client" }
|
||||
{ "name": "amm_ffi" }
|
||||
],
|
||||
"cmake": {
|
||||
"find_packages": [],
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "logos_sdk.h"
|
||||
|
||||
extern "C" {
|
||||
#include "amm_client.h"
|
||||
#include "amm_ffi.h"
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -193,20 +193,20 @@ std::vector<uint8_t> jsonWordsToLeBytes(const json& arr) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Result of an amm_client JSON op: the `{ ok, value }` envelope decoded.
|
||||
// Result of an amm_ffi JSON op: the `{ ok, value }` envelope decoded.
|
||||
struct FfiResult {
|
||||
bool ok = false;
|
||||
json value;
|
||||
};
|
||||
|
||||
// Serialize `request`, hand it to an amm_client op, and decode its
|
||||
// Serialize `request`, hand it to an amm_ffi op, and decode its
|
||||
// `{ ok, value, error }` envelope. Mirrors apps/amm/src/AmmClient.cpp. `value`
|
||||
// is only populated (and `ok` true) when the op reports success with an object.
|
||||
FfiResult call(char* (*op)(const char*), const json& request) {
|
||||
const std::string payload = request.dump();
|
||||
char* raw = op(payload.c_str());
|
||||
if (raw == nullptr) {
|
||||
AMM_TRACE("amm_client op returned null");
|
||||
AMM_TRACE("amm_ffi op returned null");
|
||||
return {};
|
||||
}
|
||||
const std::string response(raw);
|
||||
@@ -214,16 +214,16 @@ FfiResult call(char* (*op)(const char*), const json& request) {
|
||||
|
||||
const auto doc = json::parse(response, nullptr, /*allow_exceptions=*/false);
|
||||
if (!doc.is_object()) {
|
||||
AMM_TRACE("amm_client op returned invalid JSON");
|
||||
AMM_TRACE("amm_ffi op returned invalid JSON");
|
||||
return {};
|
||||
}
|
||||
if (!doc.value("ok", false)) {
|
||||
AMM_TRACE("amm_client op failure: " << doc.value("error", std::string()));
|
||||
AMM_TRACE("amm_ffi op failure: " << doc.value("error", std::string()));
|
||||
return {};
|
||||
}
|
||||
const auto it = doc.find("value");
|
||||
if (it == doc.end() || !it->is_object()) {
|
||||
AMM_TRACE("amm_client op value is not an object");
|
||||
AMM_TRACE("amm_ffi op value is not an object");
|
||||
return {};
|
||||
}
|
||||
return {true, *it};
|
||||
@@ -294,7 +294,7 @@ std::vector<uint8_t> AmmModuleImpl::loadAmmElf() {
|
||||
std::string AmmModuleImpl::ammProgramId() {
|
||||
const std::vector<uint8_t> elf = loadAmmElf();
|
||||
if (elf.empty()) return {};
|
||||
// Hand the deployed binary to the amm_client program_id op, which decodes it
|
||||
// Hand the deployed binary to the amm_ffi program_id op, which decodes it
|
||||
// and computes the Image ID — 64-char lowercase hex, little-endian per u32
|
||||
// word (matches `spel program-id` and the on-chain *_program_id fields).
|
||||
const FfiResult r = call(amm_program_id, json{{"elf", toHex(elf.data(), elf.size())}});
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//
|
||||
// Orchestration only: the AMM domain math (PDA derivation, on-chain account
|
||||
// decoding, quote/plan computation, and instruction encoding) lives in the Rust
|
||||
// `amm_client` crate and is reached through its JSON FFI (amm_client.h — one
|
||||
// `amm_ffi` crate and is reached through its JSON FFI (amm_ffi.h — one
|
||||
// `char* op(const char*)` per operation, request and response both JSON). This
|
||||
// module sequences those ops with chain I/O delegated to the
|
||||
// `logos_execution_zone` wallet module (reached via modules().logos_execution_zone).
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
// success, so a startup miss (bin not readable yet) retries.
|
||||
Network network();
|
||||
|
||||
// 64-char lowercase-hex AMM program id via the amm_client `program_id` op
|
||||
// 64-char lowercase-hex AMM program id via the amm_ffi `program_id` op
|
||||
// over the AMM_PROGRAM_BIN bytes (empty if unset/unreadable/bad).
|
||||
std::string ammProgramId();
|
||||
|
||||
@@ -120,13 +120,13 @@ private:
|
||||
std::string normalizeAccountId(const std::string& id);
|
||||
|
||||
// Derives the config account id (amm_config_id) and reads it, returning the
|
||||
// account-read shape the amm_client ops embed. Null json when the config_id
|
||||
// account-read shape the amm_ffi ops embed. Null json when the config_id
|
||||
// op itself fails (readPublicAccount always yields at least {id,status}).
|
||||
nlohmann::json readConfig(const Network& net);
|
||||
|
||||
// Reads a public account through the wallet module and returns the
|
||||
// { id, status, account:{ program_owner, balance, nonce, data } } shape the
|
||||
// amm_client ops expect (see the app-side accountReadJson). `account` is
|
||||
// amm_ffi ops expect (see the app-side accountReadJson). `account` is
|
||||
// omitted when the read has no data (uninitialized/nonexistent).
|
||||
nlohmann::json readPublicAccount(const std::string& account_id);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user