Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"modules/amm/ffi",
"modules/stablecoin/ffi",
"modules/token/ffi",
"programs/token/core",
"programs/token",
Expand Down
39 changes: 39 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,17 @@
sourceDir = "modules/token/ffi";
header = "token_ffi.h";
};

stablecoinFfi = mkHostFfi {
package = "stablecoin_ffi";
sourceDir = "modules/stablecoin/ffi";
header = "stablecoin_ffi.h";
};
in
{
packages.default = ammFfi;
packages.amm_ffi = ammFfi;
packages.stablecoin_ffi = stablecoinFfi;
packages.token_ffi = tokenFfi;
}
);
Expand Down Expand Up @@ -311,6 +318,33 @@
// (if attrs ? install then { token-module-install = attrs.install; } else { })
) tokenModulePkgs;

# Stablecoin core module (modules/stablecoin): singleton discovery,
# ProtocolParameters decoding, and InitializeProgram orchestration. Rust
# owns the exact codecs and plan; the module reuses logos_execution_zone
# for live reads and submission.
stablecoinModuleOutputs = logos-module-builder.lib.mkLogosModule {
src = ./modules/stablecoin;
configFile = ./modules/stablecoin/metadata.json;
flakeInputs = inputs;
externalLibInputs = {
stablecoin_ffi = { input = self; packages.default = "stablecoin_ffi"; };
};
tests = {
dir = ./modules/stablecoin/tests;
mockCLibs = [ "stablecoin_ffi" ];
};
};
stablecoinModulePkgs = stablecoinModuleOutputs.packages or { };

# Preserve module-specific names for install artifacts and tests; bare
# builder names collide when several core modules share one root flake.
stablecoinModuleAliases = builtins.mapAttrs (
system: attrs:
(if attrs ? lgx then { stablecoin-module-lgx = attrs.lgx; } else { })
// (if attrs ? install then { stablecoin-module-install = attrs.install; } else { })
// (if attrs ? unit-tests then { stablecoin-module-tests = attrs.unit-tests; } else { })
) stablecoinModulePkgs;

# Wrap the app launcher to export DYLD_FALLBACK_LIBRARY_PATH pointing at the
# amm_ffi lib. The logos module builder links the plugin against
# @rpath/libamm_ffi.dylib but does NOT stage that dylib into the
Expand Down Expand Up @@ -384,6 +418,8 @@
ammModAliasPkgs = ammModuleAliases.${system} or { };
tokenModSysPkgs = tokenModulePkgs.${system} or { };
tokenModAliasPkgs = tokenModuleAliases.${system} or { };
stablecoinModSysPkgs = stablecoinModulePkgs.${system} or { };
stablecoinModAliasPkgs = stablecoinModuleAliases.${system} or { };
in
(builtins.removeAttrs cratePkgs [ "default" ])
// (builtins.removeAttrs appSysPkgs [ "default" ])
Expand All @@ -398,6 +434,9 @@
// (builtins.removeAttrs tokenModSysPkgs [ "default" ])
// (if tokenModSysPkgs ? default then { token-module = tokenModSysPkgs.default; } else { })
// tokenModAliasPkgs
// (builtins.removeAttrs stablecoinModSysPkgs [ "default" ])
// (if stablecoinModSysPkgs ? default then { stablecoin-module = stablecoinModSysPkgs.default; } else { })
// stablecoinModAliasPkgs
) crateOutputs.packages;
in
(builtins.removeAttrs appOutputs [ "apps" "packages" ])
Expand Down
24 changes: 24 additions & 0 deletions modules/stablecoin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.14)
project(StablecoinModulePlugin LANGUAGES CXX)

if(DEFINED ENV{LOGOS_MODULE_BUILDER_ROOT})
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/LogosModule.cmake")
include(cmake/LogosModule.cmake)
else()
message(FATAL_ERROR "LogosModule.cmake not found. Set LOGOS_MODULE_BUILDER_ROOT.")
endif()

file(READ "${CMAKE_CURRENT_SOURCE_DIR}/metadata.json" METADATA_JSON)
string(JSON MODULE_NAME GET ${METADATA_JSON} name)

logos_module(
NAME ${MODULE_NAME}
SOURCES
src/stablecoin_module_impl.h
src/stablecoin_module_impl.cpp
src/stablecoin_module_support.h
src/stablecoin_module_support.cpp
EXTERNAL_LIBS
stablecoin_ffi
)
93 changes: 93 additions & 0 deletions modules/stablecoin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Stablecoin core module

`stablecoin_module` is a headless Logos `core` module for the LEZ Stablecoin
Program. It exposes deployment discovery, protocol-parameter reads, and
protocol initialization through the same universal API used by `logoscore` and
UI modules.

The Qt-free C++ adapter handles live wallet reads and transaction submission.
`stablecoin_ffi` owns exact account decoding, PDA derivation, request
validation, and `stablecoin_core::Instruction` serialization.

## API

Every method returns a stable envelope. Success starts with:

```json
{ "status": "ok", "error": "" }
```

Failure returns:

```json
{ "status": "error", "error": "<stable_code>" }
```

### `programInfo()`

Returns the configured Stablecoin Program ID and the derived singleton account
IDs for protocol parameters, stability-fee accumulator, redemption-price
state, stablecoin definition, stablecoin master holding, and `CLOCK_01`. Each
ID is returned in base58 and lowercase hexadecimal form.

### `protocolParameters()`

Reads the singleton Protocol Parameters account through
`lez_core`, verifies its PDA and owner, and exactly decodes its
data. All `u128`, `i128`, and `u64` values are returned as decimal strings.

### `initializeProgram(request)`

Required request fields:

| Field | Type |
| --- | --- |
| `adminId` | base58 or 64-character hexadecimal account ID |
| `freezeAuthorityId` | base58 or 64-character hexadecimal account ID |
| `collateralDefinitionId` | base58 or 64-character hexadecimal account ID |
| `marketPriceOracleId` | base58 or 64-character hexadecimal account ID |
| `initialStabilityFeePerMillisecond` | exact `u128` decimal |
| `initialControllerProportionalGain` | exact `i128` decimal |
| `initialControllerIntegralGain` | exact `i128` decimal |
| `initialMinimumCollateralizationRatio` | exact `u128` decimal |
| `minimumMillisecondsBetweenRateUpdates` | exact `u64` decimal |
| `maximumOraclePriceAgeMilliseconds` | exact `u64` decimal |
| `initialRedemptionPrice` | exact `u128` decimal |
| `stablecoinName` | string accepted by the Stablecoin Program |

The module verifies all five derived target PDAs are uninitialized, validates
the collateral definition, oracle asset pair, and clock accounts, then submits
the exact nine-account instruction. Only `adminId` signs. Success adds
`transactionId` to the response envelope.

Pass numeric values as decimal strings. JSON integers are accepted when their
exact value survives parsing. JSON floating-point values are always rejected.

## Runtime configuration

Set either environment variable on the process hosting the module:

```bash
STABLECOIN_PROGRAM_ID=<base58-or-hex-program-id>
STABLECOIN_PROGRAM_BIN=/absolute/path/to/stablecoin.bin
```

When both are set, they must identify the same program. The binary must be the
exact deployable RISC Zero `.bin`; rebuilding it can change the program ID.

Set `STABLECOIN_DEBUG=1` to emit adapter diagnostics to module stderr.

## Build and test

Run from repository root:

```bash
RISC0_DEV_MODE=1 cargo +1.94.0 test -p stablecoin_ffi
RISC0_SKIP_BUILD=1 cargo +1.94.0 clippy -p stablecoin_ffi --all-targets -- -D warnings
nix build path:.#stablecoin_ffi -L
nix build path:.#stablecoin-module -L
nix build path:.#stablecoin-module-tests -L
```

Use `path:.` while files are untracked. Once tracked, `.#stablecoin-module` is
equivalent.
26 changes: 26 additions & 0 deletions modules/stablecoin/ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "stablecoin_ffi"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
borsh = { workspace = true }
clock_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.4" }
hex = "0.4"
lee_core = { workspace = true }
risc0-binfmt = { version = "=3.0.4", default-features = false }
risc0-zkvm = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
stablecoin_core = { workspace = true }
token_core = { workspace = true }
twap_oracle_core = { workspace = true }

[build-dependencies]
cbindgen = "0.28"

[dev-dependencies]
stablecoin-methods = { path = "../../../programs/stablecoin/methods" }
9 changes: 9 additions & 0 deletions modules/stablecoin/ffi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn main() {
let crate_dir =
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/stablecoin_ffi.h"));
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed=cbindgen.toml");
}
8 changes: 8 additions & 0 deletions modules/stablecoin/ffi/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language = "C"
include_guard = "STABLECOIN_FFI_H"
pragma_once = true
cpp_compat = true
autogen_warning = "/* Generated by cbindgen. Do not edit. */"

[export]
prefix = ""
53 changes: 53 additions & 0 deletions modules/stablecoin/ffi/include/stablecoin_ffi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#ifndef STABLECOIN_FFI_H
#define STABLECOIN_FFI_H

#pragma once

/* Generated by cbindgen. Do not edit. */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/**
* Resolves the stablecoin program ID and derives all singleton account IDs.
*
* # Safety
* `request_json` must be null or point to a live NUL-terminated byte string.
*/
char *stablecoin_program_info(const char *request_json);

/**
* Decodes and validates the singleton `ProtocolParameters` account.
*
* # Safety
* `request_json` must be null or point to a live NUL-terminated byte string.
*/
char *stablecoin_decode_protocol_parameters(const char *request_json);

/**
* Builds the exact wallet submission plan for `InitializeProgram`.
*
* # Safety
* `request_json` must be null or point to a live NUL-terminated byte string.
*/
char *stablecoin_initialize_program_plan(const char *request_json);

/**
* Releases a string returned by a `stablecoin_*` operation.
*
* # Safety
* `value` must be null or a pointer returned by this library that has not been freed.
*/
void stablecoin_free(char *value);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

#endif /* STABLECOIN_FFI_H */
Loading
Loading