TrustLink is a Soroban smart contract that provides a reusable trust layer for the Stellar blockchain. It enables trusted issuers (anchors, fintech apps, institutions) to create, manage, and revoke attestations about wallet addresses, allowing other contracts and applications to verify claims before executing financial operations.
TrustLink solves the problem of decentralized identity verification and trust establishment on-chain. Instead of each application building its own KYC/verification system, TrustLink provides a shared attestation infrastructure that can be queried by any smart contract or dApp.
- Authorized Issuers: Admin-controlled registry of trusted attestation issuers
- Flexible Claims: Support for any claim type (KYC_PASSED, ACCREDITED_INVESTOR, MERCHANT_VERIFIED, etc.)
- Expiration Support: Optional time-based expiration for attestations
- Revocation: Issuers can revoke attestations at any time
- Deterministic IDs: Attestations have unique, reproducible identifiers
- Event Emission: All state changes emit events for off-chain indexing
- Query Interface: Easy verification of claims for other contracts
- Pagination: Efficient listing of attestations per subject or issuer
src/
├── lib.rs # Main contract implementation
├── types.rs # Data structures and error definitions
├── storage.rs # Storage patterns and key management
├── validation.rs # Authorization and access control
├── events.rs # Event emission for indexers
└── test.rs # Comprehensive unit tests
Attestation Structure:
{
id: String, // Deterministic hash-based ID
issuer: Address, // Who issued the attestation
subject: Address, // Who the attestation is about
claim_type: String, // Type of claim (e.g., "KYC_PASSED")
timestamp: u64, // When it was created
expiration: Option<u64>, // Optional expiration time
revoked: bool // Revocation status
}Storage Keys:
Admin: Contract administrator addressIssuer(Address): Authorized issuer registryAttestation(String): Individual attestation dataSubjectAttestations(Address): Index of attestations per subjectIssuerAttestations(Address): Index of attestations per issuer
// Deploy and initialize with admin
contract.initialize(&admin_address);// Admin registers a trusted issuer
contract.register_issuer(&admin, &issuer_address);
// Check if address is authorized
let is_authorized = contract.is_issuer(&issuer_address);// Issuer creates a KYC attestation
let attestation_id = contract.create_attestation(
&issuer,
&user_address,
&String::from_str(&env, "KYC_PASSED"),
&None // No expiration
);
// Create attestation with expiration
let expiration_time = current_timestamp + 365 * 24 * 60 * 60; // 1 year
let attestation_id = contract.create_attestation(
&issuer,
&user_address,
&String::from_str(&env, "ACCREDITED_INVESTOR"),
&Some(expiration_time)
);// Check if user has valid KYC
let has_kyc = contract.has_valid_claim(
&user_address,
&String::from_str(&env, "KYC_PASSED")
);
if has_kyc {
// Proceed with financial operation
}// Issuer revokes an attestation
contract.revoke_attestation(&issuer, &attestation_id);// Get specific attestation
let attestation = contract.get_attestation(&attestation_id);
// Check status
let status = contract.get_attestation_status(&attestation_id);
// Returns: Valid, Expired, or Revoked
// List user's attestations (paginated)
let attestations = contract.get_subject_attestations(&user_address, &0, &10);
// List issuer's attestations
let issued = contract.get_issuer_attestations(&issuer_address, &0, &10);Here's how another contract would verify attestations:
use soroban_sdk::{contract, contractimpl, Address, Env, String};
#[contract]
pub struct LendingContract;
#[contractimpl]
impl LendingContract {
pub fn borrow(
env: Env,
borrower: Address,
trustlink_contract: Address,
amount: i128
) -> Result<(), Error> {
borrower.require_auth();
// Create client for TrustLink contract
let trustlink = trustlink::Client::new(&env, &trustlink_contract);
// Verify borrower has valid KYC
let kyc_claim = String::from_str(&env, "KYC_PASSED");
let has_kyc = trustlink.has_valid_claim(&borrower, &kyc_claim);
if !has_kyc {
return Err(Error::KYCRequired);
}
// Proceed with lending logic
// ...
Ok(())
}
}TrustLink defines clear error types:
AlreadyInitialized: Contract already initializedNotInitialized: Contract not yet initializedUnauthorized: Caller lacks required permissionsNotFound: Attestation doesn't existDuplicateAttestation: Attestation with same hash already existsAlreadyRevoked: Attestation already revokedExpired: Attestation has expired
TrustLink emits events for off-chain indexing:
AttestationCreated:
topics: ["created", subject_address]
data: (attestation_id, issuer, claim_type, timestamp)AttestationRevoked:
topics: ["revoked", issuer_address]
data: attestation_id- Rust 1.70+
- Soroban CLI
- wasm32-unknown-unknown target
# Run tests
make test
# Build contract
make build
# Build optimized version
make optimize
# Clean artifacts
make clean
# Format code
make fmt
# Run linter
make clippycargo testTests cover:
- Initialization and admin management
- Issuer registration and removal
- Attestation creation with validation
- Duplicate prevention
- Revocation logic
- Expiration handling
- Authorization enforcement
- Pagination
- Cross-contract verification
- Authorization: Only admin can manage issuers; only issuers can create attestations
- Deterministic IDs: Prevents replay attacks and ensures uniqueness
- Immutable History: Attestations are never deleted, only marked as revoked
- Time-based Expiration: Automatic invalidation of expired claims
- Event Transparency: All changes are logged for auditability
- DeFi Protocols: Verify KYC before lending/borrowing
- Token Sales: Ensure accredited investor status
- Payment Systems: Verify merchant credentials
- Governance: Validate voter eligibility
- Marketplaces: Confirm seller reputation
- Insurance: Verify policyholder identity
# Build optimized contract
make optimize
# Deploy to network
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/trustlink.wasm \
--network testnet
# Initialize
soroban contract invoke \
--id <CONTRACT_ID> \
--network testnet \
-- initialize \
--admin <ADMIN_ADDRESS>MIT
See CHANGELOG.md for a history of notable changes.
Contributions welcome! See CONTRIBUTING.md for setup instructions, code style requirements, and the PR process.
For issues or questions, please open a GitHub issue.