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
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e

CONTRACT_NAME="trustlink-escrow"
Expand Down
112 changes: 73 additions & 39 deletions contracts/escrow/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,35 @@ impl Escrow {
}

pub fn is_action_paused(env: Env, action: Symbol) -> bool {
env.storage()
.instance()
.get(&DataKey::ActionPaused(action))
.unwrap_or(false)
}

/// Sets the fee collector address immediately (not timelocked). Only
/// callable by the current admin, gated at the host level via
/// `admin.require_auth()`. Reverts with `InvalidAddress` if
/// `new_collector` is the all-zero address. Emits
/// `emit_fee_collector_updated`.
pub fn set_fee_collector(env: Env, new_collector: Address) -> Result<(), ContractError> {
let admin = require_admin(&env)?;
admin.require_auth();

let zero = Address::from_string(&String::from_str(&env, crate::ZERO_ADDRESS_STR));
if new_collector == zero {
return Err(ContractError::InvalidAddress);
}

let old_collector: Address = env
.storage()
.instance()
.get(&DataKey::FeeCollector)
.ok_or(ContractError::NotAuthorized)?;
env.storage()
.instance()
.set(&DataKey::FeeCollector, &new_collector);
env.events()
.publish(("FeeCollectorUpdated",), (old_collector, new_collector));
emit_fee_collector_updated(&env, old_collector, new_collector);
Ok(())
}

Expand All @@ -163,10 +187,6 @@ impl Escrow {
}

/// Returns the current arbitration fee in basis points.
.get(&DataKey::ActionPaused(action))
.unwrap_or(false)
}

pub fn get_arbitration_fee(env: Env) -> u32 {
storage::read_fee_config(&env).unwrap_or(FeeConfig { protocol_fee_bps: 0, arbitration_fee_bps: 0 }).arbitration_fee_bps
}
Expand Down Expand Up @@ -197,13 +217,21 @@ impl Escrow {
return Err(ContractError::NotAuthorized);
}

env.storage()
.instance()
.set(&DataKey::TokenAllowlistEnabled, &enabled);
emit_allowlist_toggled(&env, enabled);
Ok(())
}

/// Returns the full list of allowlisted tokens.
pub fn get_allowed_tokens(env: Env) -> soroban_sdk::Vec<Address> {
env.storage()
.instance()
.get(&DataKey::TokenAllowlist)
.unwrap_or(soroban_sdk::Vec::new(&env))
}

pub fn get_platform_fee_bps(env: Env) -> u32 {
read_platform_fee_bps(&env)
}
Expand All @@ -222,21 +250,23 @@ impl Escrow {
return Err(ContractError::NotAuthorized);
}

let mut allowlist: soroban_sdk::Map<Address, bool> = env
let mut allowlist: soroban_sdk::Vec<Address> = env
.storage()
.instance()
.get(&DataKey::TokenAllowlist)
.unwrap_or(soroban_sdk::Map::new(&env));
.unwrap_or(soroban_sdk::Vec::new(&env));

if allowlist.contains_key(token.clone()) {
if crate::internal::contains(&allowlist, &token) {
return Ok(());
}

allowlist.set(token.clone(), true);
allowlist.push_back(token.clone());
env.storage()
.instance()
.get(&DataKey::ResolverStrict)
.unwrap_or(false)
.set(&DataKey::TokenAllowlist, &allowlist);

emit_token_allowlist_updated(&env, token, true);
Ok(())
}

/// Removes `token` from the allowlist. Only callable by admin. Reverts
Expand All @@ -247,37 +277,54 @@ impl Escrow {
caller: Address,
token: Address,
) -> Result<(), ContractError> {

pub fn cancel_timelock_op(env: Env, caller: Address, operation: u32) -> Result<(), ContractError> {
caller.require_auth();
let admin = require_admin(&env)?;
if caller != admin {
return Err(ContractError::NotAuthorized);
}

let mut allowlist: soroban_sdk::Map<Address, bool> = env
let allowlist: soroban_sdk::Vec<Address> = env
.storage()
.instance()
.get(&DataKey::TokenAllowlist)
.unwrap_or(soroban_sdk::Map::new(&env));
.unwrap_or(soroban_sdk::Vec::new(&env));

if !allowlist.contains_key(token.clone()) {
if !crate::internal::contains(&allowlist, &token) {
return Err(ContractError::TokenNotAllowed);
}

allowlist.remove(token.clone());

let mut new_allowlist = soroban_sdk::Vec::new(&env);
for allowed_token in allowlist.iter() {
if allowed_token != token {
new_allowlist.push_back(allowed_token);
}
}
env.storage()
.instance()
.set(&DataKey::TokenAllowlist, &allowlist);
.set(&DataKey::TokenAllowlist, &new_allowlist);

emit_token_allowlist_updated(&env, token, false);
Ok(())
}

/// Returns whether the token allowlist is currently enforced.
pub fn is_token_allowlist_enabled(env: Env) -> bool {
is_token_allowlist_enabled(&env)
/// Cancels a queued (not-yet-executed) timelocked admin operation. Only
/// callable by admin. Reverts with `InvalidState` if no proposal is
/// currently queued for `operation`. Emits `emit_timelock_cancelled`.
pub fn cancel_timelock_op(env: Env, caller: Address, operation: u32) -> Result<(), ContractError> {
caller.require_auth();
let admin = require_admin(&env)?;
if caller != admin {
return Err(ContractError::NotAuthorized);
}

let proposal = storage::read_timelock_proposal(&env, operation)
.ok_or(ContractError::InvalidState)?;

storage::remove_timelock_proposal(&env, operation);
emit_timelock_cancelled(&env, operation, proposal.proposer, caller);
Ok(())
}

// 2. Upgrade
pub fn queue_upgrade(env: Env, caller: Address, new_wasm_hash: BytesN<32>) -> Result<(), ContractError> {
let mut params = Vec::new(&env);
Expand All @@ -295,16 +342,6 @@ impl Escrow {
Ok(())
}

/// Returns the full list of allowlisted tokens.
pub fn get_allowed_tokens(env: Env) -> soroban_sdk::Vec<Address> {
let allowlist: soroban_sdk::Map<Address, bool> = env
.storage()
.instance()
.get(&DataKey::TokenAllowlist)
.unwrap_or(soroban_sdk::Map::new(&env));
allowlist.keys()
}

// 4. SetArbitrationFee
pub fn queue_set_arbitration_fee(env: Env, caller: Address, fee_bps: u32) -> Result<(), ContractError> {
let mut params = Vec::new(&env);
Expand Down Expand Up @@ -362,15 +399,12 @@ impl Escrow {
Ok(())
}

/// Returns the current platform fee in basis points.
pub fn get_platform_fee_bps(env: Env) -> u32 {
read_platform_fee_bps(&env)
}

/// Returns the configured treasury address. Reverts with
/// `NotInitialized` if no treasury has been set via `set_treasury`.
pub fn get_treasury(env: Env) -> Result<Address, ContractError> {
read_treasury(&env)
}

// 7. SetFeeCollector
pub fn queue_set_fee_collector(env: Env, caller: Address, new_collector: Address) -> Result<(), ContractError> {
let mut params = Vec::new(&env);
Expand Down
2 changes: 1 addition & 1 deletion scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail

# TrustLink Mainnet Deployment Script
Expand Down
Loading