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
48 changes: 48 additions & 0 deletions veritixpay/contract/token/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
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::{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::dispute::{get_dispute as dispute_get, get_dispute_history_for_escrow, 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};
Expand Down Expand Up @@ -77,6 +83,38 @@ impl VeritixToken {
spend_balance(&e, from.clone(), amount); receive_balance(&e, to.clone(), amount);
e.events().publish((symbol_short!("transfer"), from, to), amount);
}
pub fn initialize(e: Env, admin: Address, name: String, symbol: String, decimal: u32) {
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);
}
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);
spend_balance(&e, from.clone(), amount); decrease_supply(&e, amount);
e.events().publish((symbol_short!("clawback"), admin, from), amount);
}
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 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);
}
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);
}
pub fn initialize(e: Env, admin: Address, name: String, symbol: String, decimal: u32) {
if has_admin(&e) { panic!("already initialized"); }
pub fn initialize(e: Env, admin: Address, name: String, symbol: String, decimal: u32) {
Expand Down Expand Up @@ -190,6 +228,15 @@ impl VeritixToken {
spend_balance(&e, from.clone(), amount); decrease_supply(&e, amount);
e.events().publish((symbol_short!("burn"), from), amount);
}
pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) {
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);
}
spend_allowance(&e, from.clone(), spender.clone(), amount);
spend_balance(&e, from.clone(), amount); decrease_supply(&e, amount);
e.events().publish((symbol_short!("burn"), from), amount);
}
pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) {
from.require_auth(); require_positive_amount(amount);
write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger);
Expand Down Expand Up @@ -285,6 +332,7 @@ impl VeritixToken {
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 get_dispute_history_for_escrow(e: Env, escrow_id: u32) -> Vec<u32> { get_dispute_history_for_escrow(&e, escrow_id) }
pub fn create_split(e: Env, sender: Address, recipients: Vec<SplitRecipient>, 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) }
Expand Down
31 changes: 31 additions & 0 deletions veritixpay/contract/token/src/dispute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ 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};
use soroban_sdk::{contracttype, symbol_short, vec, Address, Env, Symbol, Vec};

pub const APPEAL_WINDOW: u32 = 1000;

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DisputeStatus { Open, ResolvedForBeneficiary, ResolvedForDepositor, Appealed }
pub enum DisputeStatus { Open, ResolvedForBeneficiary, ResolvedForDepositor }

#[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 appeal_deadline_ledger: u32,
}

fn append_dispute_history(e: &Env, escrow_id: u32, dispute_id: u32) {
Expand Down Expand Up @@ -59,6 +64,10 @@ pub fn open_dispute(e: &Env, claimant: Address, escrow_id: u32, resolver: Addres
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, appeal_deadline_ledger: 0,
let count = increment_counter(e, &DataKey::DisputeCount);
let record = DisputeRecord { id: count, escrow_id, claimant: claimant.clone(), resolver, status: DisputeStatus::Open };
if v != id {
updated.push_back(v);
Expand Down Expand Up @@ -127,6 +136,28 @@ pub fn resolve_dispute(e: &Env, resolver: Address, dispute_id: u32, release_to_b
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.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.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);
remove_resolver_dispute(e, &resolver, dispute_id);
dispute.status = if release_to_beneficiary { DisputeStatus::ResolvedForBeneficiary } else { DisputeStatus::ResolvedForDepositor };
e.storage().persistent().set(&dispute_key, &dispute);
Expand Down
Loading