From 99bb9a2c864bd47dff6649f471c7979cfc8cc4c7 Mon Sep 17 00:00:00 2001 From: Leothosine Date: Sat, 30 May 2026 09:20:34 +0100 Subject: [PATCH 1/2] feat(dispute): add appeal_deadline_ledger and appeal_dispute --- veritixpay/contract/token/src/dispute.rs | 184 +++++++---------------- 1 file changed, 52 insertions(+), 132 deletions(-) diff --git a/veritixpay/contract/token/src/dispute.rs b/veritixpay/contract/token/src/dispute.rs index b5e3b0f..8820f00 100644 --- a/veritixpay/contract/token/src/dispute.rs +++ b/veritixpay/contract/token/src/dispute.rs @@ -1,182 +1,102 @@ -use crate::balance::{receive_balance, spend_balance}; +use crate::balance::{receive_balance, spend_balance}; use crate::escrow::get_escrow; -use crate::storage_types::{increment_counter, write_persistent_record, DataKey, PERSISTENT_BUMP_AMOUNT, PERSISTENT_LIFETIME_THRESHOLD}; +use crate::storage_types::{ + increment_counter, write_persistent_record, DataKey, PERSISTENT_BUMP_AMOUNT, + PERSISTENT_LIFETIME_THRESHOLD, +}; use soroban_sdk::{contracttype, symbol_short, Address, Env, Symbol}; +pub const APPEAL_WINDOW: u32 = 1000; + #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] -pub enum DisputeStatus { - Open, - ResolvedForBeneficiary, - ResolvedForDepositor, -} +pub enum DisputeStatus { Open, ResolvedForBeneficiary, ResolvedForDepositor, Appealed } #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] pub struct DisputeRecord { - pub id: u32, - pub escrow_id: u32, - pub claimant: Address, - pub resolver: Address, - pub status: DisputeStatus, + pub id: u32, pub escrow_id: u32, pub claimant: Address, + pub resolver: Address, pub status: DisputeStatus, + pub appeal_deadline_ledger: u32, } -/// Opens a dispute against an existing escrow. pub fn open_dispute(e: &Env, claimant: Address, escrow_id: u32, resolver: Address) -> u32 { claimant.require_auth(); - let escrow = get_escrow(e, escrow_id); - - if escrow.released || escrow.refunded { - panic!("InvalidState: Cannot open dispute on a settled escrow"); - } - - if claimant != escrow.depositor && claimant != escrow.beneficiary { - panic!("Unauthorized: Only depositor or beneficiary can open a dispute"); - } - if resolver == claimant { - panic!("InvalidResolver: resolver cannot be the claimant"); - } - if resolver == escrow.depositor { - panic!("InvalidResolver: resolver cannot be the depositor"); - } - if resolver == escrow.beneficiary { - panic!("InvalidResolver: resolver cannot be the beneficiary"); - } - - // Prevent multiple open disputes for the same escrow. - // NOTE: All validation must complete before incrementing the counter so that - // rejected calls do not consume a dispute ID and leave gaps in the sequence. - if e.storage() - .persistent() - .has(&DataKey::EscrowDispute(escrow_id)) - { - panic!("DisputeAlreadyOpen: An open dispute already exists for this escrow"); - } - - // Increment only after all validation passes — counter must not advance on rejected calls. + if escrow.released || escrow.refunded { panic!("InvalidState: Cannot open dispute on a settled escrow"); } + if claimant != escrow.depositor && claimant != escrow.beneficiary { panic!("Unauthorized: Only depositor or beneficiary can open a dispute"); } + if resolver == claimant { panic!("InvalidResolver: resolver cannot be the claimant"); } + if resolver == escrow.depositor { panic!("InvalidResolver: resolver cannot be the depositor"); } + if resolver == escrow.beneficiary { panic!("InvalidResolver: resolver cannot be the beneficiary"); } + if e.storage().persistent().has(&DataKey::EscrowDispute(escrow_id)) { panic!("DisputeAlreadyOpen: An open dispute already exists for this escrow"); } let count = increment_counter(e, &DataKey::DisputeCount); - let record = DisputeRecord { - id: count, - escrow_id, - claimant: claimant.clone(), - resolver, - status: DisputeStatus::Open, + id: count, escrow_id, claimant: claimant.clone(), resolver, + status: DisputeStatus::Open, appeal_deadline_ledger: 0, }; - let dispute_key = DataKey::Dispute(count); let escrow_dispute_key = DataKey::EscrowDispute(escrow_id); e.storage().persistent().set(&dispute_key, &record); - e.storage() - .persistent() - .extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); + e.storage().persistent().extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); e.storage().persistent().set(&escrow_dispute_key, &count); - e.storage() - .persistent() - .extend_ttl(&escrow_dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); - - e.events().publish( - (symbol_short!("dispute_opened"), escrow_id, claimant.clone()), - (), - ); - + e.storage().persistent().extend_ttl(&escrow_dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); + e.events().publish((symbol_short!("dispute_opened"), escrow_id, claimant.clone()), ()); count } -/// Private helper: settle an escrow by outcome without requiring depositor/beneficiary auth. -/// The resolver has already been authenticated by `resolve_dispute`. fn settle_escrow_by_outcome(e: &Env, escrow_id: u32, release_to_beneficiary: bool) { let mut escrow = get_escrow(e, escrow_id); - - if escrow.released || escrow.refunded { - panic!("AlreadySettled: escrow is already settled"); - } - + if escrow.released || escrow.refunded { panic!("AlreadySettled: escrow is already settled"); } if release_to_beneficiary { escrow.released = true; write_persistent_record(e, &DataKey::Escrow(escrow_id), &escrow); spend_balance(e, e.current_contract_address(), escrow.amount); receive_balance(e, escrow.beneficiary.clone(), escrow.amount); - e.events().publish( - ( - symbol_short!("escrow_released"), - escrow_id, - escrow.beneficiary.clone(), - ), - escrow.amount, - ); + e.events().publish((symbol_short!("escrow_released"), escrow_id, escrow.beneficiary.clone()), escrow.amount); } else { escrow.refunded = true; write_persistent_record(e, &DataKey::Escrow(escrow_id), &escrow); spend_balance(e, e.current_contract_address(), escrow.amount); receive_balance(e, escrow.depositor.clone(), escrow.amount); - e.events().publish( - ( - symbol_short!("escrow_refunded"), - escrow_id, - escrow.depositor.clone(), - ), - escrow.amount, - ); + e.events().publish((symbol_short!("escrow_refunded"), escrow_id, escrow.depositor.clone()), escrow.amount); } } -/// Resolves an open dispute. Only the designated resolver can call this. -/// Settlement does not require beneficiary/depositor auth. pub fn resolve_dispute(e: &Env, resolver: Address, dispute_id: u32, release_to_beneficiary: bool) { resolver.require_auth(); - let dispute_key = DataKey::Dispute(dispute_id); - let mut dispute: DisputeRecord = e - .storage() - .persistent() - .get(&dispute_key) - .expect("Dispute not found"); - e.storage() - .persistent() - .extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); - - if dispute.status != DisputeStatus::Open { - panic!("AlreadyResolved: This dispute has already been resolved"); - } - - if dispute.resolver != resolver { - panic!("UnauthorizedResolver: Only the designated resolver can resolve this"); - } - + let mut dispute: DisputeRecord = e.storage().persistent().get(&dispute_key).expect("Dispute not found"); + e.storage().persistent().extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); + if dispute.status != DisputeStatus::Open { panic!("AlreadyResolved: This dispute has already been resolved"); } + if dispute.resolver != resolver { panic!("UnauthorizedResolver: Only the designated resolver can resolve this"); } settle_escrow_by_outcome(e, dispute.escrow_id, release_to_beneficiary); - - dispute.status = if release_to_beneficiary { - DisputeStatus::ResolvedForBeneficiary - } else { - DisputeStatus::ResolvedForDepositor - }; - + dispute.status = if release_to_beneficiary { DisputeStatus::ResolvedForBeneficiary } else { DisputeStatus::ResolvedForDepositor }; + dispute.appeal_deadline_ledger = e.ledger().sequence() + APPEAL_WINDOW; e.storage().persistent().set(&dispute_key, &dispute); - e.storage() - .persistent() - .extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); - e.storage() - .persistent() - .remove(&DataKey::EscrowDispute(dispute.escrow_id)); + e.storage().persistent().extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); + e.storage().persistent().remove(&DataKey::EscrowDispute(dispute.escrow_id)); + e.events().publish((symbol_short!("dispute_resolved"), dispute_id, resolver), release_to_beneficiary); +} - e.events().publish( - (symbol_short!("dispute_resolved"), dispute_id, resolver), - release_to_beneficiary, - ); +pub fn appeal_dispute(e: &Env, appellant: Address, dispute_id: u32, new_resolver: Address) { + appellant.require_auth(); + let dispute_key = DataKey::Dispute(dispute_id); + let mut dispute: DisputeRecord = e.storage().persistent().get(&dispute_key).expect("Dispute not found"); + if dispute.status == DisputeStatus::Open || dispute.status == DisputeStatus::Appealed { + panic!("InvalidState: dispute must be resolved before appeal"); + } + if e.ledger().sequence() > dispute.appeal_deadline_ledger { panic!("AppealWindowClosed: appeal window has passed"); } + if appellant != dispute.claimant { panic!("Unauthorized: only the claimant can appeal"); } + dispute.status = DisputeStatus::Appealed; + dispute.resolver = new_resolver.clone(); + e.storage().persistent().set(&dispute_key, &dispute); + e.storage().persistent().extend_ttl(&dispute_key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); + e.events().publish((symbol_short!("dispute_appealed"), dispute_id, appellant), new_resolver); } -/// Helper to read a dispute record. pub fn get_dispute(e: &Env, dispute_id: u32) -> DisputeRecord { let key = DataKey::Dispute(dispute_id); - let record = e - .storage() - .persistent() - .get(&key) - .expect("Dispute not found"); - e.storage() - .persistent() - .extend_ttl(&key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); + let record = e.storage().persistent().get(&key).expect("Dispute not found"); + e.storage().persistent().extend_ttl(&key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT); record -} +} \ No newline at end of file From 42f56bd9cd7bafb433353a37f6835d5a161ac89a Mon Sep 17 00:00:00 2001 From: Leothosine Date: Sat, 30 May 2026 09:20:36 +0100 Subject: [PATCH 2/2] feat(dispute): expose appeal_dispute in contract --- veritixpay/contract/token/src/contract.rs | 346 +++++----------------- 1 file changed, 69 insertions(+), 277 deletions(-) diff --git a/veritixpay/contract/token/src/contract.rs b/veritixpay/contract/token/src/contract.rs index e5ac271..f3fd09e 100644 --- a/veritixpay/contract/token/src/contract.rs +++ b/veritixpay/contract/token/src/contract.rs @@ -1,26 +1,12 @@ -use crate::admin::{check_admin, has_admin, read_admin, transfer_admin, write_admin}; +use crate::admin::{check_admin, has_admin, read_admin, transfer_admin, write_admin}; use crate::allowance::{read_allowance, spend_allowance, write_allowance}; -use crate::balance::{ - decrease_supply, increase_supply, read_balance, read_total_supply, receive_balance, - spend_balance, -}; -use crate::dispute::{get_dispute as dispute_get, open_dispute, resolve_dispute, DisputeRecord}; -use crate::escrow::{ - admin_settle_escrow as escrow_admin_settle, create_escrow as escrow_create, - get_escrow as escrow_get, refund_escrow as escrow_refund, release_escrow as escrow_release, - EscrowRecord, -}; +use crate::balance::{decrease_supply, increase_supply, read_balance, read_total_supply, receive_balance, spend_balance}; +use crate::dispute::{appeal_dispute, get_dispute as dispute_get, open_dispute, resolve_dispute, DisputeRecord}; +use crate::escrow::{admin_settle_escrow as escrow_admin_settle, create_escrow as escrow_create, get_escrow as escrow_get, refund_escrow as escrow_refund, release_escrow as escrow_release, EscrowRecord}; use crate::freeze::{freeze_account, is_frozen as read_frozen_status, unfreeze_account}; -use crate::metadata::{ - read_decimal, read_name, read_symbol, validate_metadata, write_metadata, TokenMetadata, -}; -use crate::recurring::{ - cancel_recurring, execute_recurring, get_recurring, setup_recurring, RecurringRecord, -}; -use crate::splitter::{ - cancel_split as split_cancel, create_split as split_create, distribute as split_distribute, - get_split as split_get, SplitRecord, SplitRecipient, -}; +use crate::metadata::{read_decimal, read_name, read_symbol, validate_metadata, write_metadata, TokenMetadata}; +use crate::recurring::{cancel_recurring, execute_recurring, get_recurring, setup_recurring, RecurringRecord}; +use crate::splitter::{cancel_split as split_cancel, create_split as split_create, distribute as split_distribute, get_split as split_get, SplitRecord, SplitRecipient}; use crate::validation::{require_not_frozen_account, require_positive_amount}; use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String, Vec}; @@ -29,275 +15,81 @@ pub struct VeritixToken; #[contractimpl] impl VeritixToken { - // --- Admin & metadata --- - - /// Sets admin and metadata. Panics if already initialized. pub fn initialize(e: Env, admin: Address, name: String, symbol: String, decimal: u32) { - if has_admin(&e) { - panic!("already initialized"); - } - + if has_admin(&e) { panic!("already initialized"); } admin.require_auth(); - - let meta = TokenMetadata { - name, - symbol, - decimal, - }; - validate_metadata(&meta); - write_admin(&e, &admin); - write_metadata(&e, meta); + let meta = TokenMetadata { name, symbol, decimal }; + validate_metadata(&meta); write_admin(&e, &admin); write_metadata(&e, meta); } - - /// Rotates the contract administrator. Requires current admin auth. - pub fn set_admin(e: Env, new_admin: Address) { - transfer_admin(&e, new_admin); - } - - /// Admin-only. Reclaims tokens from an address and destroys them. + pub fn set_admin(e: Env, new_admin: Address) { transfer_admin(&e, new_admin); } pub fn clawback(e: Env, admin: Address, from: Address, amount: i128) { - check_admin(&e, &admin); - require_positive_amount(amount); - - // Deduct balance without redistributing, effectively burning the tokens - spend_balance(&e, from.clone(), amount); - decrease_supply(&e, amount); - - // Emit transparency event - e.events() - .publish((symbol_short!("clawback"), admin, from), amount); + check_admin(&e, &admin); require_positive_amount(amount); + spend_balance(&e, from.clone(), amount); decrease_supply(&e, amount); + e.events().publish((symbol_short!("clawback"), admin, from), amount); } - - // --- Freeze controls --- - - pub fn freeze(e: Env, target: Address) { - let admin = read_admin(&e); - check_admin(&e, &admin); - freeze_account(&e, admin, target); + pub fn freeze(e: Env, target: Address) { let admin = read_admin(&e); check_admin(&e, &admin); freeze_account(&e, admin, target); } + pub fn unfreeze(e: Env, target: Address) { let admin = read_admin(&e); check_admin(&e, &admin); unfreeze_account(&e, admin, target); } + pub fn mint(e: Env, admin: Address, to: Address, amount: i128) { + check_admin(&e, &admin); require_positive_amount(amount); require_not_frozen_account(&e, &to); + receive_balance(&e, to.clone(), amount); increase_supply(&e, amount); + e.events().publish((symbol_short!("mint"), admin, to), amount); } - - pub fn unfreeze(e: Env, target: Address) { - let admin = read_admin(&e); - check_admin(&e, &admin); - unfreeze_account(&e, admin, target); + pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { + from.require_auth(); require_positive_amount(amount); + require_not_frozen_account(&e, &from); require_not_frozen_account(&e, &to); + spend_balance(&e, from.clone(), amount); receive_balance(&e, to.clone(), amount); + e.events().publish((symbol_short!("transfer"), from, to), amount); } - - // --- Mint / burn & supply tracking --- - - /// Admin-only. Mints new tokens to a specific address. - pub fn mint(e: Env, admin: Address, to: Address, amount: i128) { - check_admin(&e, &admin); - require_positive_amount(amount); - receive_balance(&e, to.clone(), amount); - increase_supply(&e, amount); - e.events() - .publish((symbol_short!("mint"), admin, to), amount); + pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { + spender.require_auth(); require_positive_amount(amount); + require_not_frozen_account(&e, &from); require_not_frozen_account(&e, &to); + spend_allowance(&e, from.clone(), spender.clone(), amount); + spend_balance(&e, from.clone(), amount); receive_balance(&e, to.clone(), amount); + e.events().publish((symbol_short!("transfer"), from, to), amount); } - - /// Caller burns their own tokens. pub fn burn(e: Env, from: Address, amount: i128) { - require_not_frozen_account(&e, &from); - require_positive_amount(amount); - from.require_auth(); - spend_balance(&e, from.clone(), amount); - decrease_supply(&e, amount); + from.require_auth(); require_positive_amount(amount); require_not_frozen_account(&e, &from); + spend_balance(&e, from.clone(), amount); decrease_supply(&e, amount); e.events().publish((symbol_short!("burn"), from), amount); } - - /// Spender burns tokens from an account using their allowance. pub fn burn_from(e: Env, spender: Address, from: Address, amount: i128) { - require_not_frozen_account(&e, &from); - require_not_frozen_account(&e, &spender); - require_positive_amount(amount); - spender.require_auth(); - spend_allowance(&e, from.clone(), spender.clone(), amount); - spend_balance(&e, from.clone(), amount); - decrease_supply(&e, amount); - e.events() - .publish((symbol_short!("burn"), spender, from), amount); - } - - // --- Transfers & allowance --- - - /// Standard token transfer between two addresses. - pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { - require_not_frozen_account(&e, &from); - require_positive_amount(amount); - from.require_auth(); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - e.events() - .publish((symbol_short!("transfer"), from, to), amount); - } - - /// Transfer tokens on behalf of a user via allowance. - pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { - require_not_frozen_account(&e, &from); - require_not_frozen_account(&e, &spender); - require_positive_amount(amount); - spender.require_auth(); + spender.require_auth(); require_positive_amount(amount); require_not_frozen_account(&e, &from); spend_allowance(&e, from.clone(), spender.clone(), amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - e.events() - .publish((symbol_short!("transfer"), from, to), amount); + spend_balance(&e, from.clone(), amount); decrease_supply(&e, amount); + e.events().publish((symbol_short!("burn"), from), amount); } - - /// Sets an allowance for a spender. - /// Frozen accounts cannot create new approvals. pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { - require_not_frozen_account(&e, &from); - from.require_auth(); + from.require_auth(); require_positive_amount(amount); write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger); - e.events() - .publish((symbol_short!("approve"), from, spender), amount); - } - - // --- Read-only views --- - - pub fn total_supply(e: Env) -> i128 { - read_total_supply(&e) - } - - pub fn balance(e: Env, id: Address) -> i128 { - read_balance(&e, id) - } - - pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { - read_allowance(&e, from, spender).amount - } - - pub fn admin(e: Env) -> Address { - read_admin(&e) - } - - pub fn is_frozen(e: Env, id: Address) -> bool { - read_frozen_status(&e, &id) - } - - pub fn decimals(e: Env) -> u32 { - read_decimal(&e) - } - - pub fn name(e: Env) -> String { - read_name(&e) - } - - pub fn symbol(e: Env) -> String { - read_symbol(&e) - } - - // --- Escrow --- - - pub fn create_escrow(e: Env, depositor: Address, beneficiary: Address, amount: i128, expiry_ledger: u32) -> u32 { - escrow_create(&e, depositor, beneficiary, amount, expiry_ledger) - } - - pub fn release_escrow(e: Env, caller: Address, escrow_id: u32) { - escrow_release(&e, caller, escrow_id) - } - - pub fn refund_escrow(e: Env, caller: Address, escrow_id: u32) { - escrow_refund(&e, caller, escrow_id) - } - - pub fn get_escrow(e: Env, escrow_id: u32) -> EscrowRecord { - escrow_get(&e, escrow_id) - } - - /// Returns the current number of escrows created (monotonically increasing counter). - pub fn escrow_count(e: Env) -> u32 { - crate::storage_types::bump_instance(&e); - crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::EscrowCount) - } - - /// Admin escape hatch: forcibly settles a stuck escrow when the normal - /// beneficiary or depositor is frozen. Sends funds to `recipient`. - pub fn admin_settle_escrow(e: Env, admin: Address, escrow_id: u32, recipient: Address) { - escrow_admin_settle(&e, admin, escrow_id, recipient) - } - - // --- Dispute --- - - pub fn open_dispute(e: Env, claimant: Address, escrow_id: u32, resolver: Address) -> u32 { - open_dispute(&e, claimant, escrow_id, resolver) - } - - pub fn resolve_dispute( - e: Env, - resolver: Address, - dispute_id: u32, - release_to_beneficiary: bool, - ) { - resolve_dispute(&e, resolver, dispute_id, release_to_beneficiary) - } - - pub fn get_dispute(e: Env, dispute_id: u32) -> DisputeRecord { - dispute_get(&e, dispute_id) - } - - /// Returns the current number of disputes created (monotonically increasing counter). - pub fn dispute_count(e: Env) -> u32 { - crate::storage_types::bump_instance(&e); - crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::DisputeCount) - } - - // --- Splitter --- - - pub fn create_split( - e: Env, - sender: Address, - recipients: Vec, - total_amount: i128, - ) -> u32 { - split_create(&e, sender, recipients, total_amount) - } - - pub fn distribute(e: Env, caller: Address, split_id: u32) { - split_distribute(&e, caller, split_id) - } - - pub fn cancel_split(e: Env, caller: Address, split_id: u32) { - split_cancel(&e, caller, split_id) - } - - pub fn get_split(e: Env, split_id: u32) -> SplitRecord { - split_get(&e, split_id) - } - - /// Returns the current number of splits created (monotonically increasing counter). - pub fn split_count(e: Env) -> u32 { - crate::storage_types::bump_instance(&e); - crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::SplitCount) - } - - // --- Recurring Payments --- - - pub fn setup_recurring( - e: Env, - payer: Address, - payee: Address, - amount: i128, - interval: u32, - ) -> u32 { - setup_recurring(&e, payer, payee, amount, interval) - } - - pub fn execute_recurring(e: Env, recurring_id: u32) { - execute_recurring(&e, recurring_id) - } - - pub fn cancel_recurring(e: Env, caller: Address, recurring_id: u32) { - cancel_recurring(&e, caller, recurring_id) - } - - pub fn get_recurring(e: Env, recurring_id: u32) -> RecurringRecord { - get_recurring(&e, recurring_id) - } - - /// Returns the current number of recurring payments created (monotonically increasing counter). - pub fn recurring_count(e: Env) -> u32 { - crate::storage_types::bump_instance(&e); - crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::RecurringCount) - } -} + e.events().publish((symbol_short!("approve"), from, spender), amount); + } + pub fn total_supply(e: Env) -> i128 { read_total_supply(&e) } + pub fn balance(e: Env, id: Address) -> i128 { read_balance(&e, id) } + pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { read_allowance(&e, from, spender).amount } + pub fn admin(e: Env) -> Address { read_admin(&e) } + pub fn is_frozen(e: Env, id: Address) -> bool { read_frozen_status(&e, &id) } + pub fn decimals(e: Env) -> u32 { read_decimal(&e) } + pub fn name(e: Env) -> String { read_name(&e) } + pub fn symbol(e: Env) -> String { read_symbol(&e) } + pub fn create_escrow(e: Env, depositor: Address, beneficiary: Address, amount: i128, expiry_ledger: u32) -> u32 { escrow_create(&e, depositor, beneficiary, amount, expiry_ledger) } + pub fn release_escrow(e: Env, caller: Address, escrow_id: u32) { escrow_release(&e, caller, escrow_id) } + pub fn refund_escrow(e: Env, caller: Address, escrow_id: u32) { escrow_refund(&e, caller, escrow_id) } + pub fn get_escrow(e: Env, escrow_id: u32) -> EscrowRecord { escrow_get(&e, escrow_id) } + pub fn escrow_count(e: Env) -> u32 { crate::storage_types::bump_instance(&e); crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::EscrowCount) } + pub fn admin_settle_escrow(e: Env, admin: Address, escrow_id: u32, recipient: Address) { escrow_admin_settle(&e, admin, escrow_id, recipient) } + pub fn open_dispute(e: Env, claimant: Address, escrow_id: u32, resolver: Address) -> u32 { open_dispute(&e, claimant, escrow_id, resolver) } + pub fn resolve_dispute(e: Env, resolver: Address, dispute_id: u32, release_to_beneficiary: bool) { resolve_dispute(&e, resolver, dispute_id, release_to_beneficiary) } + pub fn get_dispute(e: Env, dispute_id: u32) -> DisputeRecord { dispute_get(&e, dispute_id) } + pub fn dispute_count(e: Env) -> u32 { crate::storage_types::bump_instance(&e); crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::DisputeCount) } + pub fn appeal_dispute(e: Env, appellant: Address, dispute_id: u32, new_resolver: Address) { appeal_dispute(&e, appellant, dispute_id, new_resolver) } + pub fn create_split(e: Env, sender: Address, recipients: Vec, total_amount: i128) -> u32 { split_create(&e, sender, recipients, total_amount) } + pub fn distribute(e: Env, caller: Address, split_id: u32) { split_distribute(&e, caller, split_id) } + pub fn cancel_split(e: Env, caller: Address, split_id: u32) { split_cancel(&e, caller, split_id) } + pub fn get_split(e: Env, split_id: u32) -> SplitRecord { split_get(&e, split_id) } + pub fn split_count(e: Env) -> u32 { crate::storage_types::bump_instance(&e); crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::SplitCount) } + pub fn setup_recurring(e: Env, payer: Address, payee: Address, amount: i128, interval: u32) -> u32 { setup_recurring(&e, payer, payee, amount, interval) } + pub fn execute_recurring(e: Env, recurring_id: u32) { execute_recurring(&e, recurring_id) } + pub fn cancel_recurring(e: Env, caller: Address, recurring_id: u32) { cancel_recurring(&e, caller, recurring_id) } + pub fn get_recurring(e: Env, recurring_id: u32) -> RecurringRecord { get_recurring(&e, recurring_id) } + pub fn recurring_count(e: Env) -> u32 { crate::storage_types::bump_instance(&e); crate::storage_types::read_counter(&e, &crate::storage_types::DataKey::RecurringCount) } +} \ No newline at end of file