Skip to content

Commit b184ed2

Browse files
authored
Merge pull request #282 from abore9769/feature/trade-templates
feat: reusable trade templates with versioning and validation
2 parents 3ed7077 + b638406 commit b184ed2

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

contract/src/lib.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use soroban_sdk::{contract, contractimpl, token::TokenClient, Address, BytesN, E
2323

2424
pub use errors::ContractError;
2525
pub use types::{
26+
ArbitrationConfig, DisclosureGrant, DisputeResolution, Proposal, ProposalAction,
2627
ArbitrationConfig, ArbitratorVote, DisclosureGrant, DisputeResolution, MultiSigConfig,
2728
Proposal, ProposalAction, ProposalStatus, Subscription, SubscriptionTier, TierConfig,
2829
TemplateTerms, TemplateVersion, Trade, TradePrivacy, TradeStatus, TradeTemplate,
@@ -1901,6 +1902,125 @@ impl StellarEscrowContract {
19011902
pub fn amm_get_lp_position(env: Env, pool_id: u64, provider: Address) -> amm::LpPosition {
19021903
amm::get_lp_position(&env, pool_id, &provider)
19031904
}
1905+
1906+
// -------------------------------------------------------------------------
1907+
// Trade Templates
1908+
// -------------------------------------------------------------------------
1909+
1910+
/// Create a reusable trade template. Owner must authenticate.
1911+
pub fn create_template(
1912+
env: Env,
1913+
owner: Address,
1914+
name: soroban_sdk::String,
1915+
terms: TemplateTerms,
1916+
) -> Result<u64, ContractError> {
1917+
require_initialized(&env)?;
1918+
owner.require_auth();
1919+
templates::create_template(&env, &owner, name, terms)
1920+
}
1921+
1922+
/// Update a template with new terms, bumping its version number.
1923+
/// Only the template owner may call this.
1924+
pub fn update_template(
1925+
env: Env,
1926+
caller: Address,
1927+
template_id: u64,
1928+
name: soroban_sdk::String,
1929+
terms: TemplateTerms,
1930+
) -> Result<(), ContractError> {
1931+
require_initialized(&env)?;
1932+
caller.require_auth();
1933+
templates::update_template(&env, &caller, template_id, name, terms)
1934+
}
1935+
1936+
/// Deactivate a template so it can no longer be used to create trades.
1937+
/// Only the template owner may call this.
1938+
pub fn deactivate_template(
1939+
env: Env,
1940+
caller: Address,
1941+
template_id: u64,
1942+
) -> Result<(), ContractError> {
1943+
require_initialized(&env)?;
1944+
caller.require_auth();
1945+
templates::deactivate_template(&env, &caller, template_id)
1946+
}
1947+
1948+
/// Create a trade from a template, inheriting its default arbitrator and
1949+
/// metadata. If the template has a `fixed_amount`, `amount` must match.
1950+
pub fn create_trade_from_template(
1951+
env: Env,
1952+
seller: Address,
1953+
buyer: Address,
1954+
template_id: u64,
1955+
amount: u64,
1956+
) -> Result<u64, ContractError> {
1957+
require_initialized(&env)?;
1958+
require_not_paused(&env)?;
1959+
if amount == 0 {
1960+
return Err(ContractError::InvalidAmount);
1961+
}
1962+
seller.require_auth();
1963+
1964+
let (terms, version) = templates::resolve_terms(&env, template_id)?;
1965+
1966+
if let Some(fixed) = terms.fixed_amount {
1967+
if amount != fixed {
1968+
return Err(ContractError::TemplateAmountMismatch);
1969+
}
1970+
}
1971+
if let Some(ref arb) = terms.default_arbitrator {
1972+
if !has_arbitrator(&env, arb) {
1973+
return Err(ContractError::ArbitratorNotRegistered);
1974+
}
1975+
}
1976+
1977+
let trade_id = increment_trade_counter(&env)?;
1978+
let fee = calc_fee(&env, &seller, amount)?;
1979+
let trade = Trade {
1980+
id: trade_id,
1981+
seller: seller.clone(),
1982+
buyer: buyer.clone(),
1983+
amount,
1984+
fee,
1985+
arbitrator: terms.default_arbitrator.map(ArbitrationConfig::Single),
1986+
status: TradeStatus::Created,
1987+
expiry_time: None,
1988+
currency: get_usdc_token(&env)?,
1989+
metadata: terms.default_metadata,
1990+
};
1991+
save_trade(&env, trade_id, &trade);
1992+
events::emit_trade_created(&env, trade_id, seller, buyer, amount, trade.currency);
1993+
events::emit_template_trade(&env, trade_id, template_id, version);
1994+
Ok(trade_id)
1995+
}
1996+
1997+
/// Fetch a template by ID.
1998+
pub fn get_template(env: Env, template_id: u64) -> Result<TradeTemplate, ContractError> {
1999+
storage::get_template(&env, template_id)
2000+
}
2001+
2002+
/// Fetch a specific version of a template's terms by version number.
2003+
pub fn get_template_version(
2004+
env: Env,
2005+
template_id: u64,
2006+
version: u32,
2007+
) -> Result<TemplateVersion, ContractError> {
2008+
let template = storage::get_template(&env, template_id)?;
2009+
for i in 0..template.versions.len() {
2010+
let v = template.versions.get(i).unwrap();
2011+
if v.version == version {
2012+
return Ok(v);
2013+
}
2014+
}
2015+
Err(ContractError::TemplateNotFound)
2016+
}
2017+
2018+
/// Returns true if the template exists and is active.
2019+
pub fn is_template_valid(env: Env, template_id: u64) -> bool {
2020+
storage::get_template(&env, template_id)
2021+
.map(|t| t.active)
2022+
.unwrap_or(false)
2023+
}
19042024
}
19052025

19062026

0 commit comments

Comments
 (0)