Skip to content

Latest commit

 

History

History
1038 lines (819 loc) · 24.8 KB

File metadata and controls

1038 lines (819 loc) · 24.8 KB

Gathera API Documentation

This document provides comprehensive API documentation for all Gathera smart contracts, including function signatures, parameters, return values, usage examples, and gas costs.

Table of Contents

  1. Overview
  2. Ticket Contract API
  3. Escrow Contract API
  4. Multi-Signature Wallet Contract API
  5. Integration Layer API
  6. Error Handling
  7. Gas Cost Analysis
  8. Usage Examples

Overview

The Gathera platform consists of three main smart contracts:

  1. SoulboundTicketContract: Manages non-transferable event tickets
  2. EscrowContract: Handles secure fund escrow with dispute resolution
  3. MultisigWalletContract: Provides multi-signature wallet functionality

All contracts are built on the Stellar Soroban platform using Rust and compile to WebAssembly.

Ticket Contract API

Contract Address

[TBD] (To be populated after deployment)

Core Functions

issue_ticket

Issues a new soulbound ticket to a recipient.

pub fn issue_ticket(
    env: Env,
    event_id: Symbol,
    recipient: Address,
    metadata: String,
) -> Result<Symbol, TicketError>

Parameters:

  • env (Env): The contract environment
  • event_id (Symbol): Unique identifier for the event
  • recipient (Address): Stellar address of the ticket recipient
  • metadata (String): Additional ticket metadata (JSON format)

Returns:

  • Result<Symbol, TicketError>:
    • Ok(Symbol): Unique ticket identifier
    • Err(TicketError): Error if ticket issuance fails

Gas Cost: ~15,000 gas units

Example:

use soroban_sdk::{Symbol, Address, String, Env};

let env = Env::default();
let event_id = Symbol::new(&env, "conf2024");
let recipient = Address::from_string(&env, "GD...");
let metadata = String::from_str(&env, r#"{"type": "VIP", "seat": "A1"}"#);

let ticket_id = SoulboundTicketContract::issue_ticket(
    env,
    event_id,
    recipient,
    metadata
)?;

verify_ownership

Verifies if a given address owns a specific ticket.

pub fn verify_ownership(
    env: Env,
    ticket_id: Symbol,
    claimed_owner: Address,
) -> bool

Parameters:

  • env (Env): The contract environment
  • ticket_id (Symbol): Unique identifier for the ticket
  • claimed_owner (Address): Address claiming ownership

Returns:

  • bool: True if the claimed_owner owns the ticket, false otherwise

Gas Cost: ~5,000 gas units

Example:

let is_owner = SoulboundTicketContract::verify_ownership(
    env,
    ticket_id,
    claimed_address
);

get_ticket

Retrieves ticket information.

pub fn get_ticket(env: Env, ticket_id: Symbol) -> Result<Ticket, TicketError>

Parameters:

  • env (Env): The contract environment
  • ticket_id (Symbol): Unique identifier for the ticket

Returns:

  • Result<Ticket, TicketError>:
    • Ok(Ticket): Ticket data structure
    • Err(TicketError): Error if ticket not found

Gas Cost: ~8,000 gas units

Example:

let ticket = SoulboundTicketContract::get_ticket(env, ticket_id)?;
println!("Ticket owner: {}", ticket.owner);
println!("Issued at: {}", ticket.issued_at);

Data Structures

Ticket

pub struct Ticket {
    pub ticket_id: Symbol,
    pub event_id: Symbol,
    pub owner: Address,
    pub issued_at: u64,
    pub metadata: String,
}

TicketError

pub enum TicketError {
    TicketAlreadyExists = 1,
    TicketNotFound = 2,
    Unauthorized = 3,
    InvalidEventId = 4,
    NotTransferable = 5,
    EventEnded = 6,
    MaxTicketsReached = 7,
    NotImplemented = 255,
}

Escrow Contract API

Contract Address

[TBD] (To be populated after deployment)

Core Functions

create_escrow

Creates a new escrow arrangement.

pub fn create_escrow(
    env: Env,
    beneficiary: Address,
    amount: u128,
    expires_at: u64,
    terms: String,
    required_confirmations: u32,
) -> Result<Symbol, EscrowError>

Parameters:

  • env (Env): The contract environment
  • beneficiary (Address): Address of the beneficiary
  • amount (u128): Amount to escrow (in stroops)
  • expires_at (u64): Expiration timestamp (Unix timestamp)
  • terms (String): Escrow terms and conditions
  • required_confirmations (u32): Number of confirmations needed for release

Returns:

  • Result<Symbol, EscrowError>:
    • Ok(Symbol): Unique escrow identifier
    • Err(EscrowError): Error if escrow creation fails

Gas Cost: ~25,000 gas units

Example:

let beneficiary = Address::from_string(&env, "GD...");
let amount = 100_000_0000; // 100 XLM in stroops
let expires_at = 1704067200; // January 1, 2024
let terms = String::from_str(&env, "Payment for event ticket");
let required_confirmations = 2;

let escrow_id = EscrowContract::create_escrow(
    env,
    beneficiary,
    amount,
    expires_at,
    terms,
    required_confirmations
)?;

fund_escrow

Funds an existing escrow with tokens.

pub fn fund_escrow(env: Env, escrow_id: Symbol) -> Result<bool, EscrowError>

Parameters:

  • env (Env): The contract environment
  • escrow_id (Symbol): Unique identifier for the escrow

Returns:

  • Result<bool, EscrowError>:
    • Ok(bool): True if funding was successful
    • Err(EscrowError): Error if funding fails

Gas Cost: ~10,000 gas units + token transfer cost

Example:

let success = EscrowContract::fund_escrow(env, escrow_id)?;

release_funds

Releases funds from escrow to the beneficiary.

pub fn release_funds(env: Env, escrow_id: Symbol) -> Result<bool, EscrowError>

Parameters:

  • env (Env): The contract environment
  • escrow_id (Symbol): Unique identifier for the escrow

Returns:

  • Result<bool, EscrowError>:
    • Ok(bool): True if release was successful
    • Err(EscrowError): Error if release fails

Gas Cost: ~15,000 gas units + token transfer cost

Example:

let success = EscrowContract::release_funds(env, escrow_id)?;

create_dispute

Creates a dispute for an escrow.

pub fn create_dispute(
    env: Env,
    escrow_id: Symbol,
    reason: String,
) -> Result<Symbol, EscrowError>

Parameters:

  • env (Env): The contract environment
  • escrow_id (Symbol): Unique identifier for the escrow
  • reason (String): Dispute reason description

Returns:

  • Result<Symbol, EscrowError>:
    • Ok(Symbol): Unique dispute identifier
    • Err(EscrowError): Error if dispute creation fails

Gas Cost: ~12,000 gas units

Example:

let reason = String::from_str(&env, "Service not delivered as agreed");
let dispute_id = EscrowContract::create_dispute(env, escrow_id, reason)?;

resolve_dispute

Resolves a dispute with specified resolution.

pub fn resolve_dispute(
    env: Env,
    dispute_id: Symbol,
    resolution: String,
) -> Result<bool, EscrowError>

Parameters:

  • env (Env): The contract environment
  • dispute_id (Symbol): Unique identifier for the dispute
  • resolution (String): Dispute resolution details

Returns:

  • Result<bool, EscrowError>:
    • Ok(bool): True if resolution was successful
    • Err(EscrowError): Error if resolution fails

Gas Cost: ~18,000 gas units

Example:

let resolution = String::from_str(&env, "Partial refund to beneficiary");
let success = EscrowContract::resolve_dispute(env, dispute_id, resolution)?;

get_escrow

Retrieves escrow information.

pub fn get_escrow(env: Env, escrow_id: Symbol) -> Result<Escrow, EscrowError>

Parameters:

  • env (Env): The contract environment
  • escrow_id (Symbol): Unique identifier for the escrow

Returns:

  • Result<Escrow, EscrowError>:
    • Ok(Escrow): Escrow data structure
    • Err(EscrowError): Error if escrow not found

Gas Cost: ~8,000 gas units

Example:

let escrow = EscrowContract::get_escrow(env, escrow_id)?;
println!("Escrow amount: {}", escrow.amount);
println!("Status: {:?}", escrow.status);

Data Structures

Escrow

pub struct Escrow {
    pub escrow_id: Symbol,
    pub depositor: Address,
    pub beneficiary: Address,
    pub amount: u128,
    pub status: EscrowStatus,
    pub created_at: u64,
    pub expires_at: u64,
    pub terms: String,
    pub required_confirmations: u32,
    pub confirmations: Vec<Address>,
}

EscrowStatus

pub enum EscrowStatus {
    Pending = 0,
    Funded = 1,
    Completed = 2,
    Disputed = 3,
    Refunded = 4,
    Expired = 5,
}

Dispute

pub struct Dispute {
    pub dispute_id: Symbol,
    pub escrow_id: Symbol,
    pub initiator: Address,
    pub reason: String,
    pub resolved: bool,
    pub resolution: Option<String>,
}

EscrowError

pub enum EscrowError {
    EscrowAlreadyExists = 1,
    EscrowNotFound = 2,
    Unauthorized = 3,
    InsufficientFunds = 4,
    InvalidTerms = 5,
    AlreadyCompleted = 6,
    DisputeExists = 7,
    InvalidResolution = 8,
    EscrowExpired = 9,
    NotImplemented = 255,
}

Multi-Signature Wallet Contract API

Contract Address

[TBD] (To be populated after deployment)

Core Functions

initialize

Initializes the multi-signature wallet with owners and settings.

pub fn initialize(
    env: Env,
    owners: Vec<Address>,
    threshold: u32,
    timelock: u64,
    max_amount: u128,
) -> Result<bool, MultisigError>

Parameters:

  • env (Env): The contract environment
  • owners (Vec): List of initial wallet owners
  • threshold (u32): Number of signatures required (1 ≤ threshold ≤ owners.len())
  • timelock (u64): Time-lock period in seconds
  • max_amount (u128): Maximum transaction amount

Returns:

  • Result<bool, MultisigError>:
    • Ok(bool): True if initialization was successful
    • Err(MultisigError): Error if initialization fails

Gas Cost: ~30,000 gas units

Example:

let mut owners = Vec::new(&env);
owners.push_back(Address::from_string(&env, "GD..."));
owners.push_back(Address::from_string(&env, "GD..."));
owners.push_back(Address::from_string(&env, "GD..."));

let threshold = 2;
let timelock = 3600; // 1 hour
let max_amount = 1_000_000_000; // 1000 XLM

let success = MultisigWalletContract::initialize(
    env,
    owners,
    threshold,
    timelock,
    max_amount
)?;

submit_transaction

Submits a new transaction for approval.

pub fn submit_transaction(
    env: Env,
    destination: Address,
    amount: u128,
    data: String,
    expires_at: u64,
) -> Result<Symbol, MultisigError>

Parameters:

  • env (Env): The contract environment
  • destination (Address): Recipient address
  • amount (u128): Amount to transfer (in stroops)
  • data (String): Transaction data/payload
  • expires_at (u64): Expiration timestamp

Returns:

  • Result<Symbol, MultisigError>:
    • Ok(Symbol): Unique transaction identifier
    • Err(MultisigError): Error if submission fails

Gas Cost: ~20,000 gas units

Example:

let destination = Address::from_string(&env, "GD...");
let amount = 50_000_000; // 5 XLM
let data = String::from_str(&env, "Payment for event services");
let expires_at = 1704067200 + 86400; // 24 hours from now

let tx_id = MultisigWalletContract::submit_transaction(
    env,
    destination,
    amount,
    data,
    expires_at
)?;

approve_transaction

Approves a pending transaction.

pub fn approve_transaction(
    env: Env,
    transaction_id: Symbol,
) -> Result<bool, MultisigError>

Parameters:

  • env (Env): The contract environment
  • transaction_id (Symbol): Unique identifier for the transaction

Returns:

  • Result<bool, MultisigError>:
    • Ok(bool): True if approval was successful
    • Err(MultisigError): Error if approval fails

Gas Cost: ~10,000 gas units

Example:

let success = MultisigWalletContract::approve_transaction(env, tx_id)?;

execute_transaction

Executes an approved transaction.

pub fn execute_transaction(
    env: Env,
    transaction_id: Symbol,
) -> Result<bool, MultisigError>

Parameters:

  • env (Env): The contract environment
  • transaction_id (Symbol): Unique identifier for the transaction

Returns:

  • Result<bool, MultisigError>:
    • Ok(bool): True if execution was successful
    • Err(MultisigError): Error if execution fails

Gas Cost: ~15,000 gas units + token transfer cost

Example:

let success = MultisigWalletContract::execute_transaction(env, tx_id)?;

add_owner

Adds a new owner to the wallet (requires governing transaction).

pub fn add_owner(
    env: Env,
    new_owner: Address,
    transaction_id: Symbol,
) -> Result<bool, MultisigError>

Parameters:

  • env (Env): The contract environment
  • new_owner (Address): Address of the new owner
  • transaction_id (Symbol): Governing transaction ID

Returns:

  • Result<bool, MultisigError>:
    • Ok(bool): True if owner addition was successful
    • Err(MultisigError): Error if addition fails

Gas Cost: ~12,000 gas units

Example:

let new_owner = Address::from_string(&env, "GD...");
let success = MultisigWalletContract::add_owner(env, new_owner, gov_tx_id)?;

remove_owner

Removes an owner from the wallet (requires governing transaction).

pub fn remove_owner(
    env: Env,
    owner_to_remove: Address,
    transaction_id: Symbol,
) -> Result<bool, MultisigError>

Parameters:

  • env (Env): The contract environment
  • owner_to_remove (Address): Address of the owner to remove
  • transaction_id (Symbol): Governing transaction ID

Returns:

  • Result<bool, MultisigError>:
    • Ok(bool): True if owner removal was successful
    • Err(MultisigError): Error if removal fails

Gas Cost: ~12,000 gas units

Example:

let owner_to_remove = Address::from_string(&env, "GD...");
let success = MultisigWalletContract::remove_owner(env, owner_to_remove, gov_tx_id)?;

change_threshold

Changes the signature threshold (requires governing transaction).

pub fn change_threshold(
    env: Env,
    new_threshold: u32,
    transaction_id: Symbol,
) -> Result<bool, MultisigError>

Parameters:

  • env (Env): The contract environment
  • new_threshold (u32): New threshold value
  • transaction_id (Symbol): Governing transaction ID

Returns:

  • Result<bool, MultisigError>:
    • Ok(bool): True if threshold change was successful
    • Err(MultisigError): Error if change fails

Gas Cost: ~10,000 gas units

Example:

let new_threshold = 3;
let success = MultisigWalletContract::change_threshold(env, new_threshold, gov_tx_id)?;

get_transaction

Retrieves transaction information.

pub fn get_transaction(
    env: Env,
    transaction_id: Symbol,
) -> Result<Transaction, MultisigError>

Parameters:

  • env (Env): The contract environment
  • transaction_id (Symbol): Unique identifier for the transaction

Returns:

  • Result<Transaction, MultisigError>:
    • Ok(Transaction): Transaction data structure
    • Err(MultisigError): Error if transaction not found

Gas Cost: ~8,000 gas units

Example:

let transaction = MultisigWalletContract::get_transaction(env, tx_id)?;
println!("Amount: {}", transaction.amount);
println!("Status: {:?}", transaction.status);

get_config

Retrieves current wallet configuration.

pub fn get_config(env: Env) -> MultisigConfig

Parameters:

  • env (Env): The contract environment

Returns:

  • MultisigConfig: Current wallet configuration

Gas Cost: ~5,000 gas units

Example:

let config = MultisigWalletContract::get_config(env);
println!("Threshold: {}", config.threshold);
println!("Owners: {:?}", config.owners);

Data Structures

Transaction

pub struct Transaction {
    pub transaction_id: Symbol,
    pub destination: Address,
    pub amount: u128,
    pub data: String,
    pub status: TransactionStatus,
    pub created_at: u64,
    pub expires_at: u64,
    pub required_confirmations: u32,
    pub confirmations: Vec<Address>,
    pub creator: Address,
}

TransactionStatus

pub enum TransactionStatus {
    Pending = 0,
    Approved = 1,
    Rejected = 2,
    Executed = 3,
    Expired = 4,
}

MultisigConfig

pub struct MultisigConfig {
    pub owners: Vec<Address>,
    pub threshold: u32,
    pub timelock: u64,
    pub max_transaction_amount: u128,
}

MultisigError

pub enum MultisigError {
    TransactionAlreadyExists = 1,
    TransactionNotFound = 2,
    Unauthorized = 3,
    InsufficientSignatures = 4,
    InvalidOwner = 5,
    ThresholdNotMet = 6,
    AlreadyExecuted = 7,
    InvalidTransaction = 8,
    WalletLocked = 9,
    DuplicateSignature = 10,
    NotImplemented = 255,
}

Integration Layer API

Core Functions

EventTicketingWorkflow::create_event_with_escrow

Creates a complete event ticketing setup with escrow integration.

pub fn create_event_with_escrow(
    &self,
    event_id: Symbol,
    organizer: Address,
    ticket_price: u128,
    max_tickets: u32,
    escrow_terms: String,
) -> Result<Symbol, WorkflowError>

Parameters:

  • event_id (Symbol): Unique identifier for the event
  • organizer (Address): Event organizer address
  • ticket_price (u128): Price per ticket
  • max_tickets (u32): Maximum number of tickets
  • escrow_terms (String): Escrow terms and conditions

Returns:

  • Result<Symbol, WorkflowError>:
    • Ok(Symbol): Workflow transaction identifier
    • Err(WorkflowError): Error if workflow fails

Gas Cost: ~50,000 gas units

EventTicketingWorkflow::process_ticket_purchase

Processes a ticket purchase with escrow integration.

pub fn process_ticket_purchase(
    &self,
    event_id: Symbol,
    buyer: Address,
    payment_amount: u128,
) -> Result<Symbol, WorkflowError>

Parameters:

  • event_id (Symbol): Unique identifier for the event
  • buyer (Address): Buyer address
  • payment_amount (u128): Payment amount

Returns:

  • Result<Symbol, WorkflowError>:
    • Ok(Symbol): Transaction identifier
    • Err(WorkflowError): Error if purchase fails

Gas Cost: ~35,000 gas units

GatheraClient::get_addresses

Retrieves all contract addresses.

pub fn get_addresses(&self) -> (Address, Address, Address)

Returns:

  • (Address, Address, Address): Tuple of (ticket, escrow, multisig) addresses

Gas Cost: ~3,000 gas units

Error Handling

All contracts use comprehensive error handling with specific error codes:

Common Error Patterns

  1. Authorization Errors: Verify caller permissions
  2. Validation Errors: Check input parameters
  3. State Errors: Verify contract state
  4. Business Logic Errors: Enforce business rules

Error Recovery

  • Check error codes for specific failure reasons
  • Use appropriate retry strategies for temporary failures
  • Implement proper error logging and monitoring

Gas Cost Analysis

Cost Summary (in gas units)

Function Min Cost Max Cost Notes
issue_ticket 15,000 25,000 Depends on metadata size
verify_ownership 5,000 8,000 Simple lookup
get_ticket 8,000 12,000 Data retrieval
create_escrow 25,000 40,000 Depends on terms length
fund_escrow 10,000 15,000 + token transfer
release_funds 15,000 25,000 + token transfer
create_dispute 12,000 18,000 Depends on reason length
resolve_dispute 18,000 30,000 Complex logic
initialize 30,000 45,000 Depends on owner count
submit_transaction 20,000 30,000 Depends on data size
approve_transaction 10,000 15,000 Simple approval
execute_transaction 15,000 25,000 + token transfer

Optimization Tips

  1. Batch Operations: Combine multiple operations when possible
  2. Metadata Optimization: Keep metadata strings concise
  3. Storage Patterns: Optimize data storage layouts
  4. Event Logging: Use events instead of storage when possible

Usage Examples

Complete Event Setup Workflow

use soroban_sdk::{Symbol, Address, String, Env, Vec};

fn setup_complete_event(env: Env) -> Result<(), Box<dyn std::error::Error>> {
    // 1. Initialize multisig wallet
    let mut owners = Vec::new(&env);
    owners.push_back(Address::from_string(&env, "GD...organizer1"));
    owners.push_back(Address::from_string(&env, "GD...organizer2"));
    
    MultisigWalletContract::initialize(
        env.clone(),
        owners,
        2, // threshold
        3600, // 1 hour timelock
        1_000_000_000 // 1000 XLM max
    )?;
    
    // 2. Create event with escrow
    let workflow = EventTicketingWorkflow::new(env.clone());
    let event_id = Symbol::new(&env, "tech_conf_2024");
    let organizer = Address::from_string(&env, "GD...organizer");
    let ticket_price = 100_000_000; // 10 XLM
    let max_tickets = 500;
    let escrow_terms = String::from_str(&env, "Refundable until event start");
    
    workflow.create_event_with_escrow(
        event_id,
        organizer,
        ticket_price,
        max_tickets,
        escrow_terms
    )?;
    
    // 3. Issue tickets to initial participants
    let participants = vec![
        "GD...user1",
        "GD...user2",
        "GD...user3"
    ];
    
    for (i, participant) in participants.iter().enumerate() {
        let ticket_id = SoulboundTicketContract::issue_ticket(
            env.clone(),
            event_id,
            Address::from_string(&env, participant),
            String::from_str(&env, &format!("Ticket #{}", i + 1))
        )?;
        
        println!("Issued ticket {} to {}", ticket_id, participant);
    }
    
    Ok(())
}

Ticket Purchase with Escrow

fn purchase_ticket_with_escrow(
    env: Env,
    event_id: Symbol,
    buyer: Address,
    payment_amount: u128
) -> Result<Symbol, Box<dyn std::error::Error>> {
    // 1. Create escrow for payment
    let escrow_id = EscrowContract::create_escrow(
        env.clone(),
        buyer, // beneficiary (will be refunded if purchase fails)
        payment_amount,
        1704067200, // expiration
        String::from_str(&env, "Ticket purchase escrow"),
        1 // single confirmation needed
    )?;
    
    // 2. Fund the escrow
    EscrowContract::fund_escrow(env.clone(), escrow_id)?;
    
    // 3. Process ticket purchase through workflow
    let workflow = EventTicketingWorkflow::new(env.clone());
    let tx_id = workflow.process_ticket_purchase(
        event_id,
        buyer,
        payment_amount
    )?;
    
    // 4. If successful, release funds to organizer
    EscrowContract::release_funds(env, escrow_id)?;
    
    Ok(tx_id)
}

Multi-Signature Transaction Flow

fn execute_multisig_payment(
    env: Env,
    destination: Address,
    amount: u128,
    owners: Vec<Address>
) -> Result<Symbol, Box<dyn std::error::Error>> {
    // 1. Submit transaction
    let tx_id = MultisigWalletContract::submit_transaction(
        env.clone(),
        destination,
        amount,
        String::from_str(&env, "Event payment"),
        1704067200 + 86400 // expires in 24 hours
    )?;
    
    // 2. Collect approvals from required owners
    let threshold = 2; // Assuming threshold is 2
    let mut approvals = 0;
    
    for owner in owners {
        if approvals >= threshold {
            break;
        }
        
        // In a real implementation, each owner would call this separately
        let approved = MultisigWalletContract::approve_transaction(
            env.clone(),
            tx_id
        )?;
        
        if approved {
            approvals += 1;
        }
    }
    
    // 3. Execute the transaction if threshold met
    if approvals >= threshold {
        MultisigWalletContract::execute_transaction(env, tx_id)?;
        println!("Transaction executed successfully");
    } else {
        println!("Insufficient approvals for transaction");
    }
    
    Ok(tx_id)
}

Best Practices

Security Considerations

  1. Input Validation: Always validate user inputs
  2. Access Control: Implement proper authorization checks
  3. Reentrancy Protection: Guard against reentrancy attacks
  4. Integer Overflow: Use safe arithmetic operations
  5. Event Logging: Emit events for important state changes

Performance Optimization

  1. Gas Efficiency: Optimize for low gas consumption
  2. Storage Patterns: Minimize storage operations
  3. Batch Operations: Combine multiple operations when possible
  4. Caching: Cache frequently accessed data

Testing Strategy

  1. Unit Tests: Test individual functions
  2. Integration Tests: Test contract interactions
  3. Property Tests: Test edge cases and invariants
  4. Gas Tests: Verify gas consumption limits

This API documentation is maintained by the Gathera development team. For the most up-to-date information, please check the official repository.