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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ api-docs.json
# Vercel local link (may contain project ids)
.vercel

# Rust build outputs
target/

# OS
.DS_Store
Thumbs.db
Expand Down
6 changes: 6 additions & 0 deletions Contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Smart contracts for on-chain credit score attestation on Stellar.

Stores portable credit scores attested by the ZCore oracle. Lenders and DeFi protocols can read scores directly from chain without calling the ZCore API.

## mock-score-registry

TEST ONLY local mock for Server integration tests. It implements the same `get_score(wallet)` read shape and `interface_version()` value as `score-registry`, but has no admin authorization and exposes `set_mock_score(wallet, score, tier)` so tests can seed scores quickly.

Build it from `Contracts/mock-score-registry` with `stellar contract build`, deploy to a local sandbox, then point Server tests at it with `withMockContractId("<MOCK_CONTRACT_ID>")`.

### Functions

| Function | Auth | Description |
Expand Down
2 changes: 2 additions & 0 deletions Contracts/interfaces/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,7 @@ Topic: `("score_updated",)` — emitted on every `set_score` with previous and n
## Related

- Implementation: `Contracts/score-registry/src/lib.rs`
- **Test mock:** `Contracts/mock-score-registry/` (TEST ONLY — local/CI)
- Server bindings: `Server/src/services/soroban.service.ts`
- Test helper: `Server/src/test-helpers/mock-soroban.ts`
- Issue #32 tracking
25 changes: 25 additions & 0 deletions Contracts/mock-score-registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "mock-score-registry"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
crate-type = ["cdylib"]
doctest = false

[dependencies]
soroban-sdk = "22.0.7"

[dev-dependencies]
soroban-sdk = { version = "22.0.7", features = ["testutils"] }

[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true
44 changes: 44 additions & 0 deletions Contracts/mock-score-registry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Mock Score Registry (TEST ONLY)

Simplified Soroban contract for local integration tests. **Never deploy to mainnet.**

## Functions

| Function | Description |
|----------|-------------|
| `init()` | Marks contract initialized (no admin auth) |
| `interface_version()` | Returns `1` |
| `set_mock_score(wallet, score, tier)` | Anyone can set a mock score |
| `get_score(wallet)` | Returns configured `ScoreRecord` |

## Build

```bash
cd Contracts/mock-score-registry
cargo test
stellar contract build
```

## Local deploy (testnet/sandbox)

```bash
stellar contract deploy \
--wasm target/wasm32v1-none/release/mock_score_registry.wasm \
--source-account YOUR_TESTNET_KEY \
--network testnet
```

Use the returned contract ID in tests:

```typescript
import { withMockContractId, clearMockContractId } from "../test-helpers/mock-soroban";

withMockContractId("C...");
// run readOnChainScore(...)
clearMockContractId();
```

## Related

- Production registry: `Contracts/score-registry/`
- Interface spec: `Contracts/interfaces/README.md`
74 changes: 74 additions & 0 deletions Contracts/mock-score-registry/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

/// TEST ONLY — do not deploy to mainnet.
/// Simplified score registry for local integration tests and CI.
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ScoreRecord {
pub score: u32,
pub tier: u32,
pub updated_at: u64,
pub valid_until: u64,
}

const INIT_KEY: &str = "INIT";

#[contract]
pub struct MockScoreRegistry;

#[contractimpl]
impl MockScoreRegistry {
pub fn init(env: Env) {
env.storage().instance().set(&INIT_KEY, &true);
}

pub fn interface_version(_env: Env) -> u32 {
1
}

pub fn set_mock_score(env: Env, wallet: Address, score: u32, tier: u32) {
let updated_at = env.ledger().timestamp();
let record = ScoreRecord {
score,
tier,
updated_at,
valid_until: 0,
};
env.storage().persistent().set(&wallet, &record);
}

pub fn get_score(env: Env, wallet: Address) -> ScoreRecord {
env.storage()
.persistent()
.get(&wallet)
.unwrap_or(ScoreRecord {
score: 0,
tier: 0,
updated_at: 0,
valid_until: 0,
})
}
}

#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::{testutils::Address as _, Env};

#[test]
fn stores_and_reads_mock_score() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register(MockScoreRegistry, ());
let client = MockScoreRegistryClient::new(&env, &contract_id);
let wallet = Address::generate(&env);

client.init();
client.set_mock_score(&wallet, &420, &2);

let record = client.get_score(&wallet);
assert_eq!(record.score, 420);
assert_eq!(record.tier, 2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"generators": {
"address": 1,
"nonce": 0
},
"auth": [
[],
[]
],
"ledger": {
"protocol_version": 22,
"sequence_number": 0,
"timestamp": 0,
"network_id": "0000000000000000000000000000000000000000000000000000000000000000",
"base_reserve": 0,
"min_persistent_entry_ttl": 4096,
"min_temp_entry_ttl": 16,
"max_entry_ttl": 6312000,
"ledger_entries": [
[
{
"contract_data": {
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"key": "ledger_key_contract_instance",
"durability": "persistent"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"key": "ledger_key_contract_instance",
"durability": "persistent",
"val": {
"contract_instance": {
"executable": {
"wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"storage": null
}
}
}
},
"ext": "v0"
},
4095
]
],
[
{
"contract_code": {
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_code": {
"ext": "v0",
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"code": ""
}
},
"ext": "v0"
},
4095
]
]
]
},
"events": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"generators": {
"address": 2,
"nonce": 0
},
"auth": [
[],
[]
],
"ledger": {
"protocol_version": 22,
"sequence_number": 0,
"timestamp": 0,
"network_id": "0000000000000000000000000000000000000000000000000000000000000000",
"base_reserve": 0,
"min_persistent_entry_ttl": 4096,
"min_temp_entry_ttl": 16,
"max_entry_ttl": 6312000,
"ledger_entries": [
[
{
"contract_data": {
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"key": "ledger_key_contract_instance",
"durability": "persistent"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"key": "ledger_key_contract_instance",
"durability": "persistent",
"val": {
"contract_instance": {
"executable": {
"wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"storage": null
}
}
}
},
"ext": "v0"
},
4095
]
],
[
{
"contract_code": {
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_code": {
"ext": "v0",
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"code": ""
}
},
"ext": "v0"
},
4095
]
]
]
},
"events": []
}
Loading