x402-engineer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +102 -0
- package/README.md +43 -0
- package/dist/cli.cjs +137 -0
- package/package.json +51 -0
- package/skills/stellar-dev/SKILL.md +146 -0
- package/skills/stellar-dev/advanced-patterns.md +188 -0
- package/skills/stellar-dev/api-rpc-horizon.md +521 -0
- package/skills/stellar-dev/common-pitfalls.md +510 -0
- package/skills/stellar-dev/contracts-soroban.md +565 -0
- package/skills/stellar-dev/ecosystem.md +430 -0
- package/skills/stellar-dev/frontend-stellar-sdk.md +651 -0
- package/skills/stellar-dev/resources.md +306 -0
- package/skills/stellar-dev/security.md +491 -0
- package/skills/stellar-dev/standards-reference.md +94 -0
- package/skills/stellar-dev/stellar-assets.md +419 -0
- package/skills/stellar-dev/testing.md +786 -0
- package/skills/stellar-dev/zk-proofs.md +136 -0
- package/skills/x402-add-paywall/SKILL.md +208 -0
- package/skills/x402-add-paywall/references/patterns.md +132 -0
- package/skills/x402-debug/SKILL.md +92 -0
- package/skills/x402-debug/references/checklist.md +146 -0
- package/skills/x402-explain/SKILL.md +136 -0
- package/skills/x402-init/SKILL.md +129 -0
- package/skills/x402-init/templates/env-example.md +17 -0
- package/skills/x402-init/templates/express/config.ts.md +29 -0
- package/skills/x402-init/templates/express/server.ts.md +30 -0
- package/skills/x402-init/templates/fastify/adapter.ts.md +66 -0
- package/skills/x402-init/templates/fastify/config.ts.md +29 -0
- package/skills/x402-init/templates/fastify/server.ts.md +90 -0
- package/skills/x402-init/templates/hono/config.ts.md +29 -0
- package/skills/x402-init/templates/hono/server.ts.md +31 -0
- package/skills/x402-init/templates/next-app-router/config.ts.md +29 -0
- package/skills/x402-init/templates/next-app-router/server.ts.md +31 -0
- package/skills/x402-stellar/SKILL.md +139 -0
- package/skills/x402-stellar/references/api.md +237 -0
- package/skills/x402-stellar/references/patterns.md +276 -0
- package/skills/x402-stellar/references/setup.md +138 -0
- package/skills/x402-stellar/scripts/check-deps.js +218 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
# Soroban Smart Contract Development
|
|
2
|
+
|
|
3
|
+
## When to use Soroban
|
|
4
|
+
Use Soroban when you need:
|
|
5
|
+
- Custom on-chain logic beyond Stellar's built-in operations
|
|
6
|
+
- Programmable escrow, lending, or DeFi primitives
|
|
7
|
+
- Complex authorization rules
|
|
8
|
+
- State management beyond account balances
|
|
9
|
+
- Interoperability with Stellar Assets via SAC
|
|
10
|
+
|
|
11
|
+
## Quick Navigation
|
|
12
|
+
- Initialization and constructors: [Project Setup](#project-setup), [Contract Constructors (Protocol 22+)](#contract-constructors-protocol-22)
|
|
13
|
+
- Core implementation patterns: [Core Contract Structure](#core-contract-structure), [Storage Types](#storage-types), [Authorization](#authorization)
|
|
14
|
+
- Advanced interactions: [Cross-Contract Calls](#cross-contract-calls), [Events](#events), [Error Handling](#error-handling)
|
|
15
|
+
- Delivery workflow: [Building and Deploying](#building-and-deploying), [Unit Testing](#unit-testing), [Best Practices](#best-practices)
|
|
16
|
+
- ZK status guidance: [Zero-Knowledge Cryptography (Status-Sensitive)](#zero-knowledge-cryptography-status-sensitive)
|
|
17
|
+
|
|
18
|
+
## Alternative Languages
|
|
19
|
+
|
|
20
|
+
Rust is the primary and recommended language for Soroban contracts. Community-maintained alternatives exist but are not recommended for production:
|
|
21
|
+
- **AssemblyScript**: [`as-soroban-sdk`](https://github.com/Soneso/as-soroban-sdk) by Soneso — allows TypeScript-like syntax, officially listed on Stellar docs, but may lag behind the latest protocol version
|
|
22
|
+
- **Solidity**: [Hyperledger Solang](https://github.com/hyperledger-solang/solang) — SDF-funded, compiles Solidity to Soroban WASM, currently **pre-alpha** ([docs](https://developers.stellar.org/docs/learn/migrate/evm/solidity-support-via-solang))
|
|
23
|
+
|
|
24
|
+
## Architecture Overview
|
|
25
|
+
|
|
26
|
+
### Host-Guest Model
|
|
27
|
+
Soroban uses a WebAssembly sandbox with strict separation:
|
|
28
|
+
- **Host Environment**: Provides storage, crypto, cross-contract calls
|
|
29
|
+
- **Guest Contract**: Your Rust code compiled to WASM
|
|
30
|
+
- Contracts reference host objects via handles (not direct memory)
|
|
31
|
+
|
|
32
|
+
### Key Constraints
|
|
33
|
+
- `#![no_std]` required - no Rust standard library
|
|
34
|
+
- 64KB contract size limit (use release optimizations)
|
|
35
|
+
- Limited heap allocation
|
|
36
|
+
- No string type (use `String` from soroban-sdk or `Symbol` for short strings)
|
|
37
|
+
- `Symbol` limited to 32 characters (was 10 in earlier versions)
|
|
38
|
+
|
|
39
|
+
## Project Setup
|
|
40
|
+
|
|
41
|
+
### Initialize a new contract
|
|
42
|
+
```bash
|
|
43
|
+
stellar contract init my-contract
|
|
44
|
+
cd my-contract
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This creates:
|
|
48
|
+
```
|
|
49
|
+
my-contract/
|
|
50
|
+
├── Cargo.toml
|
|
51
|
+
├── src/
|
|
52
|
+
│ └── lib.rs
|
|
53
|
+
└── contracts/
|
|
54
|
+
└── hello_world/
|
|
55
|
+
├── Cargo.toml
|
|
56
|
+
└── src/
|
|
57
|
+
└── lib.rs
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Cargo.toml configuration
|
|
61
|
+
```toml
|
|
62
|
+
[package]
|
|
63
|
+
name = "my-contract"
|
|
64
|
+
version = "0.1.0"
|
|
65
|
+
edition = "2021"
|
|
66
|
+
|
|
67
|
+
[lib]
|
|
68
|
+
crate-type = ["cdylib"]
|
|
69
|
+
|
|
70
|
+
[dependencies]
|
|
71
|
+
soroban-sdk = "25.0.1" # check https://crates.io/crates/soroban-sdk for latest
|
|
72
|
+
|
|
73
|
+
[dev-dependencies]
|
|
74
|
+
soroban-sdk = { version = "25.0.1", features = ["testutils"] } # match above
|
|
75
|
+
|
|
76
|
+
[profile.release]
|
|
77
|
+
opt-level = "z"
|
|
78
|
+
overflow-checks = true
|
|
79
|
+
debug = 0
|
|
80
|
+
strip = "symbols"
|
|
81
|
+
debug-assertions = false
|
|
82
|
+
panic = "abort"
|
|
83
|
+
codegen-units = 1
|
|
84
|
+
lto = true
|
|
85
|
+
|
|
86
|
+
[profile.release-with-logs]
|
|
87
|
+
inherits = "release"
|
|
88
|
+
debug-assertions = true
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Contract Constructors (Protocol 22+)
|
|
92
|
+
|
|
93
|
+
Use constructors for atomic initialization when protocol support is available. This avoids a separate `initialize` transaction and reduces front-running risk.
|
|
94
|
+
|
|
95
|
+
### Constructor pattern
|
|
96
|
+
```rust
|
|
97
|
+
#![no_std]
|
|
98
|
+
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};
|
|
99
|
+
|
|
100
|
+
#[contracttype]
|
|
101
|
+
#[derive(Clone)]
|
|
102
|
+
pub enum DataKey {
|
|
103
|
+
Admin,
|
|
104
|
+
Value,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#[contract]
|
|
108
|
+
pub struct MyContract;
|
|
109
|
+
|
|
110
|
+
#[contractimpl]
|
|
111
|
+
impl MyContract {
|
|
112
|
+
// Runs once at deployment time.
|
|
113
|
+
pub fn __constructor(env: Env, admin: Address, initial_value: u32) {
|
|
114
|
+
env.storage().instance().set(&DataKey::Admin, &admin);
|
|
115
|
+
env.storage().instance().set(&DataKey::Value, &initial_value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Deploy with constructor args (CLI)
|
|
121
|
+
```bash
|
|
122
|
+
stellar contract deploy \
|
|
123
|
+
--wasm target/wasm32-unknown-unknown/release/my_contract.wasm \
|
|
124
|
+
--source alice \
|
|
125
|
+
--network testnet \
|
|
126
|
+
-- \
|
|
127
|
+
--admin alice \
|
|
128
|
+
--initial_value 100
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Rules
|
|
132
|
+
1. Name must be `__constructor` exactly.
|
|
133
|
+
2. Constructor returns `()` (no return value).
|
|
134
|
+
3. Runs only at creation time and does not run on upgrade.
|
|
135
|
+
4. If constructor fails, deployment fails atomically.
|
|
136
|
+
|
|
137
|
+
### Backwards compatibility
|
|
138
|
+
If targeting older protocol environments, use guarded `initialize` patterns and prevent re-initialization explicitly.
|
|
139
|
+
|
|
140
|
+
## Core Contract Structure
|
|
141
|
+
|
|
142
|
+
### Basic Contract
|
|
143
|
+
```rust
|
|
144
|
+
#![no_std]
|
|
145
|
+
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};
|
|
146
|
+
|
|
147
|
+
#[contract]
|
|
148
|
+
pub struct HelloContract;
|
|
149
|
+
|
|
150
|
+
#[contractimpl]
|
|
151
|
+
impl HelloContract {
|
|
152
|
+
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
|
|
153
|
+
vec![&env, symbol_short!("Hello"), to]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Contract with State
|
|
159
|
+
```rust
|
|
160
|
+
#![no_std]
|
|
161
|
+
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};
|
|
162
|
+
|
|
163
|
+
#[contracttype]
|
|
164
|
+
#[derive(Clone)]
|
|
165
|
+
pub enum DataKey {
|
|
166
|
+
Counter,
|
|
167
|
+
Admin,
|
|
168
|
+
UserBalance(Address),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#[contract]
|
|
172
|
+
pub struct CounterContract;
|
|
173
|
+
|
|
174
|
+
#[contractimpl]
|
|
175
|
+
impl CounterContract {
|
|
176
|
+
pub fn initialize(env: Env, admin: Address) {
|
|
177
|
+
if env.storage().instance().has(&DataKey::Admin) {
|
|
178
|
+
panic!("already initialized");
|
|
179
|
+
}
|
|
180
|
+
env.storage().instance().set(&DataKey::Admin, &admin);
|
|
181
|
+
env.storage().instance().set(&DataKey::Counter, &0u32);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
pub fn increment(env: Env) -> u32 {
|
|
185
|
+
let mut count: u32 = env.storage().instance().get(&DataKey::Counter).unwrap_or(0);
|
|
186
|
+
count += 1;
|
|
187
|
+
env.storage().instance().set(&DataKey::Counter, &count);
|
|
188
|
+
|
|
189
|
+
// Extend TTL to prevent archival
|
|
190
|
+
env.storage().instance().extend_ttl(100, 518400); // threshold, ~30 days
|
|
191
|
+
|
|
192
|
+
count
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
pub fn get_count(env: Env) -> u32 {
|
|
196
|
+
env.storage().instance().get(&DataKey::Counter).unwrap_or(0)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Storage Types
|
|
202
|
+
|
|
203
|
+
Soroban has three storage types with different costs and lifetimes:
|
|
204
|
+
|
|
205
|
+
### Instance Storage
|
|
206
|
+
- Tied to contract instance lifetime
|
|
207
|
+
- Shared across all users
|
|
208
|
+
- Best for: admin addresses, global config, counters
|
|
209
|
+
```rust
|
|
210
|
+
env.storage().instance().set(&key, &value);
|
|
211
|
+
env.storage().instance().get(&key);
|
|
212
|
+
env.storage().instance().extend_ttl(min_ttl, extend_to);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Persistent Storage
|
|
216
|
+
- Survives archival (can be restored)
|
|
217
|
+
- Per-key TTL management
|
|
218
|
+
- Best for: user balances, important state
|
|
219
|
+
```rust
|
|
220
|
+
env.storage().persistent().set(&key, &value);
|
|
221
|
+
env.storage().persistent().get(&key);
|
|
222
|
+
env.storage().persistent().extend_ttl(&key, min_ttl, extend_to);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Temporary Storage
|
|
226
|
+
- Cheapest, automatically deleted when TTL expires
|
|
227
|
+
- Cannot be restored after archival
|
|
228
|
+
- Best for: caches, temporary flags, session data
|
|
229
|
+
```rust
|
|
230
|
+
env.storage().temporary().set(&key, &value);
|
|
231
|
+
env.storage().temporary().get(&key);
|
|
232
|
+
env.storage().temporary().extend_ttl(&key, min_ttl, extend_to);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### TTL Management
|
|
236
|
+
```rust
|
|
237
|
+
// Check remaining TTL
|
|
238
|
+
let ttl = env.storage().persistent().get_ttl(&key);
|
|
239
|
+
|
|
240
|
+
// Extend if below threshold
|
|
241
|
+
const MIN_TTL: u32 = 17280; // ~1 day at 5s ledgers
|
|
242
|
+
const EXTEND_TO: u32 = 518400; // ~30 days
|
|
243
|
+
|
|
244
|
+
if ttl < MIN_TTL {
|
|
245
|
+
env.storage().persistent().extend_ttl(&key, MIN_TTL, EXTEND_TO);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Data Types
|
|
250
|
+
|
|
251
|
+
### Primitive Types
|
|
252
|
+
```rust
|
|
253
|
+
use soroban_sdk::{Address, Bytes, BytesN, Map, String, Symbol, Vec, I128, U256};
|
|
254
|
+
|
|
255
|
+
// Address - account or contract identifier
|
|
256
|
+
let addr: Address = env.current_contract_address();
|
|
257
|
+
|
|
258
|
+
// Symbol - short strings (max 32 chars)
|
|
259
|
+
let sym: Symbol = symbol_short!("transfer");
|
|
260
|
+
|
|
261
|
+
// String - longer strings
|
|
262
|
+
let s: String = String::from_str(&env, "Hello, Stellar!");
|
|
263
|
+
|
|
264
|
+
// Fixed-size bytes
|
|
265
|
+
let hash: BytesN<32> = env.crypto().sha256(&bytes);
|
|
266
|
+
|
|
267
|
+
// Collections
|
|
268
|
+
let v: Vec<u32> = vec![&env, 1, 2, 3];
|
|
269
|
+
let m: Map<Symbol, u32> = Map::new(&env);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Custom Types
|
|
273
|
+
```rust
|
|
274
|
+
#[contracttype]
|
|
275
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
276
|
+
pub struct TokenMetadata {
|
|
277
|
+
pub name: String,
|
|
278
|
+
pub symbol: Symbol,
|
|
279
|
+
pub decimals: u32,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
#[contracttype]
|
|
283
|
+
#[derive(Clone)]
|
|
284
|
+
pub enum DataKey {
|
|
285
|
+
Admin,
|
|
286
|
+
Balance(Address),
|
|
287
|
+
Allowance(Address, Address), // (owner, spender)
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Authorization
|
|
292
|
+
|
|
293
|
+
### Requiring Authorization
|
|
294
|
+
```rust
|
|
295
|
+
#[contractimpl]
|
|
296
|
+
impl TokenContract {
|
|
297
|
+
pub fn transfer(env: Env, from: Address, to: Address, amount: i128) {
|
|
298
|
+
// Require 'from' to authorize this call
|
|
299
|
+
from.require_auth();
|
|
300
|
+
|
|
301
|
+
// Or require auth for specific arguments
|
|
302
|
+
from.require_auth_for_args((&to, amount).into_val(&env));
|
|
303
|
+
|
|
304
|
+
// Transfer logic...
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Admin Patterns
|
|
310
|
+
```rust
|
|
311
|
+
fn require_admin(env: &Env) {
|
|
312
|
+
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
|
|
313
|
+
admin.require_auth();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
pub fn set_admin(env: Env, new_admin: Address) {
|
|
317
|
+
require_admin(&env);
|
|
318
|
+
env.storage().instance().set(&DataKey::Admin, &new_admin);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Cross-Contract Calls
|
|
323
|
+
|
|
324
|
+
### Calling Another Contract
|
|
325
|
+
```rust
|
|
326
|
+
use soroban_sdk::{contract, contractimpl, Address, Env};
|
|
327
|
+
|
|
328
|
+
mod token_contract {
|
|
329
|
+
soroban_sdk::contractimport!(
|
|
330
|
+
file = "../token/target/wasm32-unknown-unknown/release/token.wasm"
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
#[contract]
|
|
335
|
+
pub struct VaultContract;
|
|
336
|
+
|
|
337
|
+
#[contractimpl]
|
|
338
|
+
impl VaultContract {
|
|
339
|
+
pub fn deposit(env: Env, user: Address, token: Address, amount: i128) {
|
|
340
|
+
user.require_auth();
|
|
341
|
+
|
|
342
|
+
// Create client for token contract
|
|
343
|
+
let token_client = token_contract::Client::new(&env, &token);
|
|
344
|
+
|
|
345
|
+
// Call transfer on token contract
|
|
346
|
+
token_client.transfer(&user, &env.current_contract_address(), &amount);
|
|
347
|
+
|
|
348
|
+
// Update vault state...
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Using Stellar Asset Contract (SAC)
|
|
354
|
+
```rust
|
|
355
|
+
use soroban_sdk::token::Client as TokenClient;
|
|
356
|
+
|
|
357
|
+
pub fn transfer_asset(env: Env, from: Address, to: Address, asset: Address, amount: i128) {
|
|
358
|
+
from.require_auth();
|
|
359
|
+
|
|
360
|
+
let token = TokenClient::new(&env, &asset);
|
|
361
|
+
token.transfer(&from, &to, &amount);
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Events
|
|
366
|
+
|
|
367
|
+
### Emitting Events
|
|
368
|
+
```rust
|
|
369
|
+
use soroban_sdk::{contract, contractevent, contractimpl, Address, Env};
|
|
370
|
+
|
|
371
|
+
#[contractevent(topics = ["transfer"])]
|
|
372
|
+
pub struct TransferEvent {
|
|
373
|
+
pub from: Address,
|
|
374
|
+
pub to: Address,
|
|
375
|
+
pub amount: i128,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
#[contract]
|
|
379
|
+
pub struct TokenContract;
|
|
380
|
+
|
|
381
|
+
#[contractimpl]
|
|
382
|
+
impl TokenContract {
|
|
383
|
+
pub fn transfer(env: Env, from: Address, to: Address, amount: i128) {
|
|
384
|
+
// ... transfer logic ...
|
|
385
|
+
|
|
386
|
+
// Emit event
|
|
387
|
+
TransferEvent { from, to, amount }.publish(&env);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Error Handling
|
|
393
|
+
|
|
394
|
+
### Custom Errors
|
|
395
|
+
```rust
|
|
396
|
+
use soroban_sdk::contracterror;
|
|
397
|
+
|
|
398
|
+
#[contracterror]
|
|
399
|
+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
400
|
+
#[repr(u32)]
|
|
401
|
+
pub enum ContractError {
|
|
402
|
+
AlreadyInitialized = 1,
|
|
403
|
+
NotInitialized = 2,
|
|
404
|
+
InsufficientBalance = 3,
|
|
405
|
+
Unauthorized = 4,
|
|
406
|
+
InvalidAmount = 5,
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Usage
|
|
410
|
+
pub fn transfer(env: Env, from: Address, to: Address, amount: i128) -> Result<(), ContractError> {
|
|
411
|
+
if amount <= 0 {
|
|
412
|
+
return Err(ContractError::InvalidAmount);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
let balance: i128 = get_balance(&env, &from);
|
|
416
|
+
if balance < amount {
|
|
417
|
+
return Err(ContractError::InsufficientBalance);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ... transfer logic ...
|
|
421
|
+
Ok(())
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## Building and Deploying
|
|
426
|
+
|
|
427
|
+
### Build Contract
|
|
428
|
+
```bash
|
|
429
|
+
# Build optimized WASM
|
|
430
|
+
stellar contract build
|
|
431
|
+
|
|
432
|
+
# Output: target/wasm32-unknown-unknown/release/my_contract.wasm
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Deploy to Testnet
|
|
436
|
+
```bash
|
|
437
|
+
# Generate and fund a new identity
|
|
438
|
+
stellar keys generate --global alice --network testnet --fund
|
|
439
|
+
|
|
440
|
+
# Deploy contract
|
|
441
|
+
stellar contract deploy \
|
|
442
|
+
--wasm target/wasm32-unknown-unknown/release/my_contract.wasm \
|
|
443
|
+
--source alice \
|
|
444
|
+
--network testnet
|
|
445
|
+
|
|
446
|
+
# Returns: CONTRACT_ID (starts with 'C')
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Initialize Contract
|
|
450
|
+
```bash
|
|
451
|
+
stellar contract invoke \
|
|
452
|
+
--id CONTRACT_ID \
|
|
453
|
+
--source alice \
|
|
454
|
+
--network testnet \
|
|
455
|
+
-- \
|
|
456
|
+
initialize \
|
|
457
|
+
--admin alice
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Invoke Functions
|
|
461
|
+
```bash
|
|
462
|
+
stellar contract invoke \
|
|
463
|
+
--id CONTRACT_ID \
|
|
464
|
+
--source alice \
|
|
465
|
+
--network testnet \
|
|
466
|
+
-- \
|
|
467
|
+
increment
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## Unit Testing
|
|
471
|
+
|
|
472
|
+
```rust
|
|
473
|
+
#![cfg(test)]
|
|
474
|
+
|
|
475
|
+
use super::*;
|
|
476
|
+
use soroban_sdk::testutils::Address as _;
|
|
477
|
+
use soroban_sdk::Env;
|
|
478
|
+
|
|
479
|
+
#[test]
|
|
480
|
+
fn test_increment() {
|
|
481
|
+
let env = Env::default();
|
|
482
|
+
let contract_id = env.register_contract(None, CounterContract);
|
|
483
|
+
let client = CounterContractClient::new(&env, &contract_id);
|
|
484
|
+
|
|
485
|
+
let admin = Address::generate(&env);
|
|
486
|
+
client.initialize(&admin);
|
|
487
|
+
|
|
488
|
+
assert_eq!(client.get_count(), 0);
|
|
489
|
+
assert_eq!(client.increment(), 1);
|
|
490
|
+
assert_eq!(client.increment(), 2);
|
|
491
|
+
assert_eq!(client.get_count(), 2);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
#[test]
|
|
495
|
+
fn test_transfer_with_auth() {
|
|
496
|
+
let env = Env::default();
|
|
497
|
+
env.mock_all_auths(); // Auto-approve all auth requests
|
|
498
|
+
|
|
499
|
+
let contract_id = env.register_contract(None, TokenContract);
|
|
500
|
+
let client = TokenContractClient::new(&env, &contract_id);
|
|
501
|
+
|
|
502
|
+
let alice = Address::generate(&env);
|
|
503
|
+
let bob = Address::generate(&env);
|
|
504
|
+
|
|
505
|
+
// Mint tokens to alice
|
|
506
|
+
client.mint(&alice, &1000);
|
|
507
|
+
|
|
508
|
+
// Transfer from alice to bob
|
|
509
|
+
client.transfer(&alice, &bob, &100);
|
|
510
|
+
|
|
511
|
+
assert_eq!(client.balance(&alice), 900);
|
|
512
|
+
assert_eq!(client.balance(&bob), 100);
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## Best Practices
|
|
517
|
+
|
|
518
|
+
### Contract Size Optimization
|
|
519
|
+
- Use `symbol_short!()` for symbols under 9 chars (more efficient)
|
|
520
|
+
- Avoid unnecessary string operations
|
|
521
|
+
- Use appropriate storage type for data lifetime
|
|
522
|
+
- Consider splitting large contracts
|
|
523
|
+
|
|
524
|
+
### Storage Efficiency
|
|
525
|
+
- Use compact data structures
|
|
526
|
+
- Clean up temporary storage
|
|
527
|
+
- Batch storage operations when possible
|
|
528
|
+
- Manage TTLs proactively to avoid archival
|
|
529
|
+
|
|
530
|
+
### Security
|
|
531
|
+
- Always validate inputs
|
|
532
|
+
- Use `require_auth()` for sensitive operations
|
|
533
|
+
- Check contract ownership in initialization
|
|
534
|
+
- Prevent reinitialization attacks
|
|
535
|
+
- Validate cross-contract call targets
|
|
536
|
+
|
|
537
|
+
### Gas/Resource Optimization
|
|
538
|
+
- Minimize storage reads/writes
|
|
539
|
+
- Use events for data that doesn't need on-chain queries
|
|
540
|
+
- Batch operations where possible
|
|
541
|
+
- Profile resource usage with `stellar contract invoke --sim`
|
|
542
|
+
|
|
543
|
+
## Zero-Knowledge Cryptography (Status-Sensitive)
|
|
544
|
+
|
|
545
|
+
Stellar's ZK cryptography capabilities are evolving. Treat availability as protocol- and network-dependent.
|
|
546
|
+
|
|
547
|
+
- [CAP-0059](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0059.md): BLS12-381 primitives
|
|
548
|
+
- [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md): BN254 host functions (proposed)
|
|
549
|
+
- [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md): Poseidon/Poseidon2 host functions (proposed)
|
|
550
|
+
|
|
551
|
+
Before implementation, always verify:
|
|
552
|
+
1. CAP status in the CAP preamble (`Accepted`/`Implemented` vs draft/awaiting decision)
|
|
553
|
+
2. Target network software version and protocol support
|
|
554
|
+
3. `soroban-sdk` release support for the target host functions
|
|
555
|
+
|
|
556
|
+
### Practical guidance
|
|
557
|
+
- Use BLS12-381 features where supported and documented in your target SDK/network.
|
|
558
|
+
- For BN254/Poseidon plans, design feature flags and graceful fallbacks until support is active.
|
|
559
|
+
- Keep cryptographic assumptions explicit in audits and deployment notes.
|
|
560
|
+
|
|
561
|
+
### Example references
|
|
562
|
+
- [Groth16 Verifier](https://github.com/stellar/soroban-examples/tree/main/groth16_verifier)
|
|
563
|
+
- [Soroban examples repository](https://github.com/stellar/soroban-examples)
|
|
564
|
+
|
|
565
|
+
> See [zk-proofs.md](zk-proofs.md) for Groth16 verification patterns, Poseidon usage, Noir/RISC Zero integration, and implementation guidance.
|