Summary
There is no on-chain user identity or KYC attestation contract. KYC status is stored only in the backend database, meaning it can be bypassed by directly calling contract functions with a Stellar keypair that was never KYC-verified.
Impact
If a user obtains a Stellar keypair and the contract address, they can interact directly with the savings or swap contracts without going through the app's KYC flow. This violates AML/KYC compliance requirements.
Proposed contract
A Soroban KYC attestation contract that:
- Stores a mapping of
Address -> KycLevel (NONE, BASIC, FULL).
- Can only be written to by a trusted attestor address (the backend's signing key).
- Is read by other contracts (savings lock, swap) to gate access:
pub trait KycRegistry {
fn attest(env: Env, attestor: Address, user: Address, level: u32);
fn revoke(env: Env, attestor: Address, user: Address);
fn get_kyc_level(env: Env, user: Address) -> u32;
fn require_kyc(env: Env, user: Address, min_level: u32); // panics if not met
}
Other contracts call kyc_contract.require_kyc(&env, &user, KYC_BASIC) before allowing deposits above a threshold.
Files affected:
- New:
src/kyc_registry/lib.rs
- New:
src/kyc_registry/Cargo.toml
Summary
There is no on-chain user identity or KYC attestation contract. KYC status is stored only in the backend database, meaning it can be bypassed by directly calling contract functions with a Stellar keypair that was never KYC-verified.
Impact
If a user obtains a Stellar keypair and the contract address, they can interact directly with the savings or swap contracts without going through the app's KYC flow. This violates AML/KYC compliance requirements.
Proposed contract
A Soroban KYC attestation contract that:
Address -> KycLevel(NONE, BASIC, FULL).Other contracts call
kyc_contract.require_kyc(&env, &user, KYC_BASIC)before allowing deposits above a threshold.Files affected:
src/kyc_registry/lib.rssrc/kyc_registry/Cargo.toml