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
69 changes: 69 additions & 0 deletions veritixpay/contract/token/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +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::{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};
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::balance::{
decrease_supply, increase_supply, read_balance, read_total_supply, receive_balance,
spend_balance,
Expand Down Expand Up @@ -38,6 +45,38 @@ pub struct VeritixToken;

#[contractimpl]
impl VeritixToken {
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 @@ -142,6 +181,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 @@ -227,6 +275,27 @@ impl VeritixToken {
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 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) }
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) }

pub fn create_escrow(e: Env, depositor: Address, beneficiary: Address, amount: i128, expiry_ledger: u32) -> u32 {
escrow_create(&e, depositor, beneficiary, amount, expiry_ledger)
Expand Down
31 changes: 21 additions & 10 deletions veritixpay/contract/token/src/dispute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ use soroban_sdk::{contracttype, symbol_short, vec, Address, Env, Symbol, Vec};

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DisputeStatus {
Open,
ResolvedForBeneficiary,
ResolvedForDepositor,
}
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 id: u32, pub escrow_id: u32, pub claimant: Address,
pub resolver: Address, pub status: DisputeStatus,
}

fn append_dispute_history(e: &Env, escrow_id: u32, dispute_id: u32) {
let key = DataKey::EscrowDisputeHistory(escrow_id);
let mut ids: Vec<u32> = e.storage().persistent().get(&key).unwrap_or_else(|| vec![e]);
ids.push_back(dispute_id);
e.storage().persistent().set(&key, &ids);
e.storage().persistent().extend_ttl(&key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT);
}

fn append_resolver_dispute(e: &Env, resolver: &Address, id: u32) {
Expand Down Expand Up @@ -52,6 +53,13 @@ pub fn open_dispute(e: &Env, claimant: Address, escrow_id: u32, resolver: Addres
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"); }
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 };
if v != id {
updated.push_back(v);
}
Expand Down Expand Up @@ -86,6 +94,7 @@ pub fn open_dispute(e: &Env, claimant: Address, escrow_id: u32, resolver: Addres
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);
append_dispute_history(e, escrow_id, count);
append_resolver_dispute(e, &resolver, count);
append_open_dispute(e, count);
e.events().publish((symbol_short!("dispute_opened"), escrow_id, claimant.clone()), ());
Expand Down Expand Up @@ -134,6 +143,8 @@ pub fn get_dispute(e: &Env, dispute_id: u32) -> DisputeRecord {
record
}

pub fn get_dispute_history_for_escrow(e: &Env, escrow_id: u32) -> Vec<u32> {
let key = DataKey::EscrowDisputeHistory(escrow_id);
pub fn get_disputes_by_resolver(e: &Env, resolver: Address) -> Vec<u32> {
let key = DataKey::ResolverDisputes(resolver);
pub fn get_open_disputes(e: &Env) -> Vec<u32> {
Expand Down
34 changes: 11 additions & 23 deletions veritixpay/contract/token/src/storage_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,19 @@ pub const PERSISTENT_BUMP_AMOUNT: u32 = 535000;

#[derive(Clone)]
#[contracttype]
pub struct AllowanceDataKey {
pub from: Address,
pub spender: Address,
}
pub struct AllowanceDataKey { pub from: Address, pub spender: Address }

#[derive(Clone)]
#[contracttype]
pub struct AllowanceValue {
pub amount: i128,
pub expiration_ledger: u32,
}
pub struct AllowanceValue { pub amount: i128, pub expiration_ledger: u32 }

#[derive(Clone)]
#[contracttype]
pub enum DataKey {
Admin, Allowance(AllowanceDataKey), Balance(Address), Metadata, TotalSupply,
EscrowCount, Escrow(u32), RecurringCount, Recurring(u32),
SplitCount, Split(u32), DisputeCount, Dispute(u32),
EscrowDispute(u32), EscrowDisputeHistory(u32), Freeze(Address),
Admin,
Allowance(AllowanceDataKey),
Balance(Address),
Expand All @@ -47,35 +45,25 @@ pub enum DataKey {
}

pub fn read_persistent_record<T>(e: &Env, key: &DataKey, missing_message: &'static str) -> T
where
T: TryFromVal<Env, Val>,
{
where T: TryFromVal<Env, Val> {
let storage = e.storage().persistent();
let value = storage
.get::<DataKey, T>(key)
.unwrap_or_else(|| panic!("{}", missing_message));
let value = storage.get::<DataKey, T>(key).unwrap_or_else(|| panic!("{}", missing_message));
storage.extend_ttl(key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT);
value
}

pub fn write_persistent_record<T>(e: &Env, key: &DataKey, value: &T)
where
T: IntoVal<Env, Val>,
{
where T: IntoVal<Env, Val> {
let storage = e.storage().persistent();
storage.set(key, value);
storage.extend_ttl(key, PERSISTENT_LIFETIME_THRESHOLD, PERSISTENT_BUMP_AMOUNT);
}

pub fn bump_instance(e: &Env) {
e.storage()
.instance()
.extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT);
e.storage().instance().extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT);
}

pub fn read_counter(e: &Env, key: &DataKey) -> u32 {
e.storage().instance().get(key).unwrap_or(0)
}
pub fn read_counter(e: &Env, key: &DataKey) -> u32 { e.storage().instance().get(key).unwrap_or(0) }

pub fn increment_counter(e: &Env, key: &DataKey) -> u32 {
let next = read_counter(e, key) + 1;
Expand Down
Loading