Skip to content

Commit 2ee179e

Browse files
authored
Merge pull request #310 from Sage-senpai/perf/optimize-contract-storage
Perf/optimize contract storage
2 parents d983806 + e54dc37 commit 2ee179e

32 files changed

Lines changed: 5039 additions & 311 deletions

File tree

soroban-client/rust/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock

soroban-client/rust/Cargo.toml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
[package]
2+
name = "crowdpass-soroban-client"
3+
version = "0.1.0"
4+
edition = "2021"
5+
rust-version = "1.75"
6+
description = "Typed, ergonomic Rust client for interacting with deployed CrowdPass Soroban contracts."
7+
license = "MIT OR Apache-2.0"
8+
repository = "https://github.com/crowdpass-live/tokenbound_impl"
9+
homepage = "https://github.com/crowdpass-live/tokenbound_impl"
10+
readme = "README.md"
11+
keywords = ["soroban", "stellar", "crowdpass", "blockchain", "client"]
12+
categories = ["api-bindings", "asynchronous", "cryptography::cryptocurrencies"]
13+
14+
[lib]
15+
name = "crowdpass_soroban_client"
16+
path = "src/lib.rs"
17+
18+
[dependencies]
19+
async-trait = "0.1"
20+
thiserror = "1.0"
21+
tokio = { version = "1", default-features = false, features = ["sync", "time", "rt", "macros"] }
22+
serde = { version = "1", features = ["derive"] }
23+
serde_json = "1"
24+
hex = "0.4"
25+
tracing = { version = "0.1", default-features = false, features = ["std"] }
26+
27+
[dev-dependencies]
28+
tokio = { version = "1", default-features = false, features = ["macros", "rt", "rt-multi-thread", "time", "sync"] }
29+
30+
[features]
31+
default = []
32+
# Enable when integrating against a transport that performs real RPC. The
33+
# library remains transport-agnostic; downstream apps wire in their own
34+
# Soroban RPC client via the [`Transport`] trait.
35+
docs = []
36+
37+
[package.metadata.docs.rs]
38+
all-features = true
39+
rustdoc-args = ["--cfg", "docsrs"]

soroban-client/rust/README.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# crowdpass-soroban-client
2+
3+
A typed, ergonomic Rust client for interacting with deployed
4+
[CrowdPass](https://github.com/crowdpass-live/tokenbound_impl) Soroban
5+
contracts on the Stellar network.
6+
7+
The crate is intentionally **transport-agnostic**: it focuses on encoding
8+
arguments, decoding responses, retry/timeout policy, and clean error mapping.
9+
You plug in your own RPC implementation (or the bundled `MockTransport` for
10+
tests) via the `Transport` trait.
11+
12+
## Features
13+
14+
- `SorobanContractClient` builder with RPC endpoint, network passphrase, and
15+
per-contract address configuration.
16+
- Typed wrappers for every major CrowdPass contract:
17+
- `EventManagerClient` — events, tiers, refunds, withdrawals.
18+
- `TicketFactoryClient` — deploy / look up per-event ticket NFT contracts.
19+
- `TicketNftClient` — mint, transfer, burn, metadata reads.
20+
- `TbaRegistryClient` / `TbaAccountClient` — token-bound accounts.
21+
- `MarketplaceClient` — listings, sales, history.
22+
- `PoapNftClient` — POAP minting and minter lookup.
23+
- Async first via `tokio` with configurable timeouts and an exponential-backoff
24+
retry policy.
25+
- Domain-mapped `ClientError` covering builder, transport, RPC, simulation,
26+
decode, and contract-level errors.
27+
- `MockTransport` for unit / integration tests — exercises the full encode /
28+
dispatch / decode pipeline with no network.
29+
30+
## Adding to your project
31+
32+
```toml
33+
# Cargo.toml
34+
[dependencies]
35+
crowdpass-soroban-client = { path = "../soroban-client/rust" }
36+
tokio = { version = "1", features = ["full"] }
37+
```
38+
39+
When this crate is published to crates.io, replace the `path = ...` entry with
40+
a normal version string.
41+
42+
## Quickstart
43+
44+
```rust,no_run
45+
use std::sync::Arc;
46+
47+
use crowdpass_soroban_client::{
48+
ContractAddresses, NetworkPassphrase, RpcConfig, SorobanContractClient,
49+
transport::MockTransport,
50+
};
51+
52+
#[tokio::main]
53+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
54+
// For production, swap MockTransport for your own JSON-RPC transport.
55+
let transport = Arc::new(MockTransport::new());
56+
57+
let client = SorobanContractClient::builder()
58+
.rpc(RpcConfig::new("https://soroban-testnet.stellar.org"))
59+
.network_passphrase(NetworkPassphrase::testnet())
60+
.contracts(
61+
ContractAddresses::builder()
62+
.event_manager("CDXXXEVT...")
63+
.ticket_factory("CDXXXTFT...")
64+
.marketplace("CDXXXMKT...")
65+
.poap_nft("CDXXXPOAP...")
66+
.tba_registry("CDXXXTBA...")
67+
.build(),
68+
)
69+
.transport(transport)
70+
.build()?;
71+
72+
let total = client.event_manager().get_event_count().await?;
73+
println!("events on chain: {total}");
74+
75+
Ok(())
76+
}
77+
```
78+
79+
## Plugging in a real RPC transport
80+
81+
Implement the `Transport` trait against your favourite Soroban RPC client
82+
(e.g. `reqwest` + `stellar_xdr`). Each invocation is described by an
83+
`InvocationRequest` (contract id, method symbol, typed args, and whether to
84+
simulate or submit) and you return an `InvocationResponse`.
85+
86+
```rust,ignore
87+
use async_trait::async_trait;
88+
use crowdpass_soroban_client::transport::{
89+
InvocationRequest, InvocationResponse, Transport,
90+
};
91+
use crowdpass_soroban_client::ClientError;
92+
93+
struct JsonRpcTransport { /* http client, signer, ... */ }
94+
95+
#[async_trait]
96+
impl Transport for JsonRpcTransport {
97+
async fn invoke(&self, request: InvocationRequest)
98+
-> Result<InvocationResponse, ClientError>
99+
{
100+
// 1. Encode `request.args` as XDR ScVals.
101+
// 2. Build a Soroban operation targeting `request.contract_id`.
102+
// 3. Either simulateTransaction (Simulate) or sendTransaction (Submit).
103+
// 4. Decode the return ScVal into a `ContractValue`.
104+
// 5. Map errors onto `ClientError`.
105+
todo!()
106+
}
107+
}
108+
```
109+
110+
## Testing strategy
111+
112+
The crate ships a `MockTransport` that behaves like a local sandbox: register
113+
handlers per `(contract_id, method)`, drive the typed clients normally, and
114+
inspect the recorded invocation log.
115+
116+
```rust,no_run
117+
use std::sync::Arc;
118+
use crowdpass_soroban_client::{
119+
ContractAddresses, ContractValue, RpcConfig, SorobanContractClient,
120+
transport::MockTransport, InvocationResponse,
121+
};
122+
123+
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
124+
let transport = Arc::new(MockTransport::new());
125+
transport.register("CEVT", "get_event_count", |_req| {
126+
Ok(InvocationResponse::value(ContractValue::U32(7)))
127+
});
128+
129+
let client = SorobanContractClient::builder()
130+
.rpc(RpcConfig::new("https://rpc.example"))
131+
.contracts(ContractAddresses::builder().event_manager("CEVT").build())
132+
.transport(transport.clone())
133+
.build()?;
134+
135+
assert_eq!(client.event_manager().get_event_count().await?, 7);
136+
assert_eq!(transport.invocations().len(), 1);
137+
# Ok(()) }
138+
```
139+
140+
The integration tests in [`tests/`](./tests) cover this pattern end-to-end for
141+
every wrapped contract, plus retry/backoff and timeout behaviour.
142+
143+
## Verifying the crate
144+
145+
From this directory:
146+
147+
```bash
148+
cargo test
149+
cargo clippy -- -D warnings
150+
cargo doc --no-deps
151+
```
152+
153+
## Layout
154+
155+
```
156+
soroban-client/rust/
157+
├── Cargo.toml
158+
├── README.md
159+
├── src/
160+
│ ├── client.rs # SorobanContractClient + builder
161+
│ ├── config.rs # RpcConfig, RetryPolicy, NetworkPassphrase
162+
│ ├── contracts/ # typed contract wrappers
163+
│ ├── error.rs # ClientError enum
164+
│ ├── lib.rs
165+
│ ├── transport/ # Transport trait + MockTransport
166+
│ └── types.rs # Address, ContractValue, domain types
167+
└── tests/ # async integration tests
168+
```
169+
170+
## License
171+
172+
Dual-licensed under MIT or Apache-2.0.

0 commit comments

Comments
 (0)