Skip to content
Open
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
30 changes: 21 additions & 9 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Daily at 2 AM UTC (nightly)
- cron: "0 2 * * *" # Daily at 2 AM UTC (nightly)

env:
CARGO_TERM_COLOR: always
Expand All @@ -15,7 +15,7 @@ jobs:
integration-tests:
name: Run Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 15 # Generous timeout for test completion
timeout-minutes: 15 # Generous timeout for test completion

steps:
- name: Checkout repository
Expand All @@ -33,6 +33,22 @@ jobs:
~/.cargo/git
target

- name: Install system deps for tx3up
run: sudo apt-get update && sudo apt-get install -y xz-utils

- name: Install tx3 toolchain
run: |
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/tx3-lang/up/releases/latest/download/tx3up-installer.sh | sh
tx3up
tx3up show
if [ -x "$HOME/.tx3/bin/tx3c" ]; then
"$HOME/.tx3/bin/tx3c" --version
echo "$HOME/.tx3/bin" >> "$GITHUB_PATH"
else
echo "tx3c not found at $HOME/.tx3/bin/tx3c" >&2
exit 1
fi

- name: Run error case tests
working-directory: ./sdk
env:
Expand All @@ -49,10 +65,6 @@ jobs:
TRP_API_KEY_PREPROD: ${{ secrets.TRP_API_KEY_PREPROD }}
run: cargo test --test happy_path -- --nocapture

- name: Test summary
if: always()
run: |
echo "=== Integration Tests Summary ==="
echo "Error case tests: Complete"
echo "Happy path test: Complete"
echo "All TRP integration tests executed successfully"
- name: Run tx3c codegen test
working-directory: ./sdk
run: cargo test --test codegen
11 changes: 5 additions & 6 deletions .trix/client-lib/Cargo.toml.hbs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
[package]
name = "{{protocolName}}"
version = "{{protocolVersion}}"
edition = "2024"
name = "{{tii.protocol.name}}"
version = "{{tii.protocol.version}}"
edition = "2021"

[dependencies]
tx3-sdk = { version = "^0.9" }
tx3-sdk = { version = "^0.9.2" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
once_cell = "1.17"

[lib]
path = "lib.rs"
path = "lib.rs"
110 changes: 38 additions & 72 deletions .trix/client-lib/lib.rs.hbs
Original file line number Diff line number Diff line change
@@ -1,84 +1,50 @@
// This file is auto-generated.

use std::collections::HashMap;
use serde::{Serialize, Deserialize};

pub use tx3_sdk::trp::ClientOptions;
use tx3_sdk::core::{TirEnvelope, BytesEncoding};
use tx3_sdk::trp::{ResolveParams, TxEnvelope, SubmitParams, SubmitResponse};

pub const DEFAULT_TRP_ENDPOINT: &str = "{{trpEndpoint}}";

pub const DEFAULT_HEADERS: &[(&str, &str)] = &[
{{#each headers}}
("{{@key}}", "{{this}}"),
{{/each}}
];

{{#each transactions}}
pub const {{constantCase constant_name}}: &str = "{{ir_bytes}}";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{pascalCase params_name}} {
{{#each parameters}}
pub {{snakeCase name}}: String,
{{/each}}
}
impl {{pascalCase params_name}} {
fn to_map(&self) -> serde_json::Map<String, serde_json::Value> {
let mut map = serde_json::Map::new();

{{#each parameters}}
map.insert("{{snakeCase name}}".to_string(), serde_json::json!(&self.{{snakeCase name}}));
use serde::Serialize;
use serde_json::{json, Value};
use tx3_sdk::core::{Address, ArgMap, TirEncoding, TirEnvelope, UtxoRef};

pub const PROTOCOL_NAME: &str = "{{tii.protocol.name}}";
pub const PROTOCOL_VERSION: &str = "{{tii.protocol.version}}";

pub fn profiles() -> Value {
json!({
{{#each tii.profiles}}
"{{@key}}": {
"environment": {{{environment}}},
"parties": {{{parties}}}
}{{#unless @last}},{{/unless}}
{{/each}}

map.into()
}
})
}

{{#each tii.transactions}}
pub const {{constantCase @key}}_TIR: TirEnvelope = TirEnvelope {
content: "{{tir.content}}".to_string(),
encoding: TirEncoding::Hex,
version: "{{tir.version}}".to_string(),
};

#[derive(Debug, Clone, Serialize)]
pub struct {{pascalCase @key}}Params {
{{#each params.properties}}
pub {{snakeCase @key}}: {{schemaTypeFor this "rust"}},
{{/each}}
pub struct Client {
client: tx3_sdk::trp::Client,
}

impl Client {
pub fn new(options: ClientOptions) -> Self {
Self {
client: tx3_sdk::trp::Client::new(options),
}
}

pub fn with_default_options() -> Self {
let mut headers = HashMap::new();
for (key, value) in DEFAULT_HEADERS {
headers.insert(key.to_string(), value.to_string());
}

Self::new(ClientOptions {
endpoint: DEFAULT_TRP_ENDPOINT.to_string(),
headers: Some(headers),
})
}
{{#each transactions}}

pub async fn {{snakeCase function_name}}(&self, args: {{pascalCase params_name}}) -> Result<TxEnvelope, tx3_sdk::trp::Error> {
let tir_info = TirEnvelope {
content: {{constantCase constant_name}}.to_string(),
encoding: BytesEncoding::Hex,
version: "{{ir_version}}".to_string(),
};
impl From<{{pascalCase @key}}Params> for ArgMap {
fn from(args: {{pascalCase @key}}Params) -> Self {
let mut map = ArgMap::new();

self.client.resolve(ResolveParams {
tir: tir_info,
args: args.to_map(),
}).await
}
{{/each}}
{{#each params.properties}}
map.insert(
"{{@key}}".to_string(),
serde_json::to_value(&args.{{snakeCase @key}})
.expect("failed to serialize tx arg"),
);
{{/each}}

pub async fn submit(&self, params: SubmitParams) -> Result<SubmitResponse, tx3_sdk::trp::Error> {
self.client.submit(params).await
map
}
}

// Create a default client instance
pub static PROTOCOL: once_cell::sync::Lazy<Client> = once_cell::sync::Lazy::new(|| Client::with_default_options());
{{/each}}
13 changes: 0 additions & 13 deletions bindgen/Cargo.toml.hbs

This file was deleted.

95 changes: 0 additions & 95 deletions bindgen/lib.rs.hbs

This file was deleted.

79 changes: 79 additions & 0 deletions sdk/tests/codegen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};

fn resolve_tx3c_path() -> Option<PathBuf> {
if let Ok(path) = env::var("TX3_TX3C_PATH") {
let path = PathBuf::from(path);
if path.is_file() {
return Some(path);
}
}

None
}

fn unique_output_dir() -> PathBuf {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time should be available")
.as_nanos();
env::temp_dir().join(format!("tx3c_codegen_test_{now}"))
}

fn assert_file_exists(path: &Path) {
assert!(path.is_file(), "Expected file to exist: {}", path.display());
}

#[test]
fn test_tx3c_codegen_client_lib_template() {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let tii_path = PathBuf::from(manifest_dir).join("../examples/transfer.tii");
let template_dir = PathBuf::from(manifest_dir).join("../.trix/client-lib");

assert!(
tii_path.is_file(),
"Missing TII file: {}",
tii_path.display()
);
assert!(
template_dir.is_dir(),
"Missing template directory: {}",
template_dir.display()
);

let output_dir = unique_output_dir();

let mut cmd = if let Some(tx3c_path) = resolve_tx3c_path() {
Command::new(tx3c_path)
} else {
Command::new("tx3c")
};

let output = cmd
.arg("codegen")
.arg("--tii")
.arg(&tii_path)
.arg("--template")
.arg(&template_dir)
.arg("--output")
.arg(&output_dir)
.output()
.expect("Failed to execute tx3c. Ensure tx3c is available.");

if !output.status.success() {
panic!(
"tx3c codegen failed.\nSTDOUT:\n{}\nSTDERR:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}

assert!(output_dir.is_dir(), "Output directory not created");
assert_file_exists(&output_dir.join("Cargo.toml"));
assert_file_exists(&output_dir.join("lib.rs"));

let _ = fs::remove_dir_all(&output_dir);
}
Loading