Skip to content
Merged
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
14 changes: 2 additions & 12 deletions contracts/atomic_swap/src/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
mod tests {
use ip_registry::{IpRegistry, IpRegistryClient};
use soroban_sdk::{
testutils::{Address as _, Ledger},
testutils::{Address as _, Events, Ledger},
token::{Client as TokenClient, StellarAssetClient},
Address, BytesN, Env,
Address, BytesN, Env, IntoVal,
};

use crate::{AtomicSwap, AtomicSwapClient, DataKey, SwapStatus, KeyRevealedEvent, SwapCancelledEvent};

// ── Helpers ───────────────────────────────────────────────────────────────

/// Register IpRegistry, commit an IP with a known secret+blinding_factor.
/// Returns (registry_id, ip_id, secret, blinding_factor).
fn setup_registry(
env: &Env,
owner: &Address,
Expand All @@ -32,14 +30,6 @@ mod tests {
(registry_id, ip_id, secret, blinding_factor)
}

fn setup_token(env: &Env, admin: &Address, recipient: &Address, amount: i128) -> Address {
let token_id = env
.register_stellar_asset_contract_v2(admin.clone())
.address();
StellarAssetClient::new(env, &token_id).mint(recipient, &amount);
token_id
}

fn setup_swap(env: &Env, registry_id: &Address) -> Address {
let contract_id = env.register(AtomicSwap, ());
AtomicSwapClient::new(env, &contract_id).initialize(registry_id);
Expand Down
27 changes: 27 additions & 0 deletions contracts/atomic_swap/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#[repr(u32)]
pub enum ContractError {
SwapNotFound = 1,
InvalidKey = 2,
PriceMustBeGreaterThanZero = 3,
SellerIsNotTheIPOwner = 4,
ActiveSwapAlreadyExistsForThisIpId = 5,
SwapNotPending = 6,
OnlyTheSellerCanRevealTheKey = 7,
SwapNotAccepted = 8,
OnlyTheSellerOrBuyerCanCancel = 9,
OnlyPendingSwapsCanBeCancelledThisWay = 10,
SwapNotInAcceptedState = 11,
OnlyTheBuyerCanCancelAnExpiredSwap = 12,
SwapHasNotExpiredYet = 13,
IpIsRevoked = 14,
UnauthorizedUpgrade = 15,
InvalidFeeBps = 16,
DisputeWindowExpired = 17,
OnlyBuyerCanDispute = 18,
SwapNotDisputed = 19,
OnlyAdminCanResolve = 20,
AlreadyInitialized = 21,
NotInitialized = 22,
ContractPaused = 23,
Unauthorized = 24,
}
124 changes: 124 additions & 0 deletions contracts/atomic_swap/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use soroban_sdk::{contracttype, Address};

// ── TTL ───────────────────────────────────────────────────────────────────────

/// Minimum ledger TTL bump applied to every persistent storage write.
/// ~1 year at ~5s per ledger: 365 * 24 * 3600 / 5 ≈ 6_307_200 ledgers.
pub const LEDGER_BUMP: u32 = 6_307_200;

// ── Storage Keys ──────────────────────────────────────────────────────────────

#[contracttype]
#[derive(Debug, PartialEq)]
pub enum DataKey {
Swap(u64),
NextId,
/// The IpRegistry contract address set once at initialization.
IpRegistry,
/// Maps ip_id → swap_id for any swap currently in Pending or Accepted state.
/// Cleared when a swap reaches Completed or Cancelled.
ActiveSwap(u64),
/// Maps seller address → Vec<u64> of all swap IDs they have initiated.
SellerSwaps(Address),
/// Maps buyer address → Vec<u64> of all swap IDs they are party to.
BuyerSwaps(Address),
Admin,
ProtocolConfig,
/// Maps ip_id → Vec<u64> of all swap IDs ever created for that IP.
IpSwaps(u64),
/// Whether the contract is paused (blocks initiate_swap and accept_swap).
Paused,
}

// ── Types ─────────────────────────────────────────────────────────────────────

#[contracttype]
#[derive(Clone, PartialEq, Debug)]
pub enum SwapStatus {
Pending,
Accepted,
Completed,
Disputed,
Cancelled,
}

#[contracttype]
#[derive(Clone)]
pub struct SwapRecord {
pub ip_id: u64,
pub seller: Address,
pub buyer: Address,
pub price: i128,
pub token: Address,
pub status: SwapStatus,
/// Ledger timestamp after which the buyer may cancel an Accepted swap
/// if reveal_key has not been called. Set at initiation time.
pub expiry: u64,
pub accept_timestamp: u64,
}

// ── Events ────────────────────────────────────────────────────────────────────

/// Payload published when a swap is successfully initiated.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct SwapInitiatedEvent {
pub swap_id: u64,
pub ip_id: u64,
pub seller: Address,
pub buyer: Address,
pub price: i128,
}

/// Payload published when a swap is successfully accepted.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct SwapAcceptedEvent {
pub swap_id: u64,
pub buyer: Address,
}

/// Payload published when a swap is successfully cancelled.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct SwapCancelledEvent {
pub swap_id: u64,
pub canceller: Address,
}

/// Payload published when a swap is successfully revealed and the swap completes.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct KeyRevealedEvent {
pub swap_id: u64,
}

/// Payload published when protocol fee is deducted on swap completion.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct ProtocolFeeEvent {
pub swap_id: u64,
pub fee_amount: i128,
pub treasury: Address,
}

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct DisputeRaisedEvent {
pub swap_id: u64,
}

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct DisputeResolvedEvent {
pub swap_id: u64,
pub refunded: bool,
}

#[contracttype]
#[derive(Clone)]
pub struct ProtocolConfig {
pub protocol_fee_bps: u32, // 0-10000 (0.00% - 100.00%)
pub treasury: Address,
pub dispute_window_seconds: u64,
}
Loading