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: 30 additions & 0 deletions bindings/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "trustlink-client"
version = "0.1.0"
edition = "2021"
description = "Rust RPC client for the TrustLink on-chain attestation contract on Stellar"
license = "MIT"
repository = "https://github.com/afurious/TrustLink"
keywords = ["stellar", "soroban", "attestation", "trustlink", "kyc"]
categories = ["api-bindings", "web-programming"]
readme = "README.md"

[dependencies]
stellar-rpc-client = { version = "21.4.0", package = "stellar-rpc-client" }
stellar-xdr = { version = "21.2.0", features = ["std", "serde"] }
stellar-strkey = "0.0.8"
reqwest = { version = "0.11.27", default-features = false, features = ["json", "rustls-tls"] }
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.120"
thiserror = "1.0.61"
tokio = { version = "1.38.0", features = ["rt", "macros"], optional = true }
hex = "0.4.3"
base64 = "0.22.1"

[dev-dependencies]
tokio = { version = "1.38.0", features = ["rt-multi-thread", "macros"] }
mockito = "1.4.0"

[features]
default = ["async"]
async = ["tokio"]
122 changes: 122 additions & 0 deletions bindings/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# trustlink-client

Rust RPC client for the [TrustLink](https://github.com/afurious/TrustLink) on-chain attestation contract on Stellar/Soroban.

This crate is a **thin async HTTP client** that talks to a Soroban RPC node. It is distinct from the on-chain contract crate (`trustlink`) — no Soroban SDK or WASM target is required. It lets Rust backend services, CLIs, and Rust-based indexers query TrustLink without going through a TypeScript or Python SDK.

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
trustlink-client = { path = "../bindings/rust" }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```

## Quick Start

```rust
use trustlink_client::{TrustLinkClient, Networks};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TrustLinkClient::new(
"CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCN8",
Networks::TESTNET,
)?;

// Check a wallet's KYC status
let has_kyc = client
.has_valid_claim("GABC...SUBJECT_ADDRESS", "KYC_PASSED")
.await?;
println!("Has valid KYC: {has_kyc}");

// Fetch a single attestation by ID
let att = client.get_attestation("att_abc123").await?;
println!("Claim: {} issued by {}", att.claim_type, att.issuer);

// Page through all attestations for a subject
let page = client
.get_subject_attestations("GABC...SUBJECT_ADDRESS", 0, 20)
.await?;
println!("First page: {} attestations", page.len());

Ok(())
}
```

## API Reference

### Claim verification

| Method | Contract function |
|---|---|
| `has_valid_claim(subject, claim_type)` | `has_valid_claim` |
| `has_valid_claim_from_issuer(subject, claim_type, issuer)` | `has_valid_claim_from_issuer` |
| `has_any_claim(subject, &[claim_types])` | `has_any_claim` |
| `has_all_claims(subject, &[claim_types])` | `has_all_claims` |

### Attestation queries

| Method | Contract function |
|---|---|
| `get_attestation(id)` | `get_attestation` |
| `get_attestation_status(id)` | `get_attestation_status` |
| `get_subject_attestations(subject, offset, limit)` | `get_subject_attestations` |
| `get_issuer_attestations(issuer, offset, limit)` | `get_issuer_attestations` |
| `is_issuer(address)` | `is_issuer` |
| `get_global_stats()` | `get_global_stats` |

### Networks

```rust
Networks::TESTNET // https://soroban-testnet.stellar.org
Networks::MAINNET // Stellar mainnet RPC
Networks::LOCAL // http://localhost:8000/soroban/rpc
```

Or pass any custom URL string directly to `TrustLinkClient::new`.

## Error handling

All methods return `Result<T, TrustLinkError>`. Contract-level errors (e.g. `NotFound`, `Unauthorized`) surface as `TrustLinkError::Contract` with a typed `ContractErrorCode`:

```rust
use trustlink_client::{TrustLinkError, ContractErrorCode};

match client.get_attestation("bad_id").await {
Ok(att) => println!("{}", att.id),
Err(TrustLinkError::Contract { code: ContractErrorCode::NotFound, .. }) => {
eprintln!("attestation not found");
}
Err(e) => eprintln!("error: {e}"),
}
```

## Design

All queries are executed as **simulated** (read-only) Soroban transactions via the JSON-RPC `simulateTransaction` endpoint. No signing key or XLM balance is required. The client is a pure async HTTP client — no Soroban SDK runtime or WASM toolchain dependency.

## Testing

```bash
cd bindings/rust
cargo test
```

Tests use `mockito` to stub the Soroban RPC endpoint and run fully offline.

## Relationship to other packages

| Package | Purpose |
|---|---|
| `trustlink` (repo root) | On-chain Soroban contract (WASM) |
| `bindings/rust` (this crate) | **Rust RPC client for off-chain use** |
| `bindings/typescript` | Auto-generated TypeScript contract bindings |
| `sdk/typescript` | Higher-level TypeScript SDK |
| `bindings/python` | Python RPC client |

## License

MIT
Loading
Loading