use chia_protocol::Coin;
use chia_puzzle_types::offer::{NotarizedPayment, Payment, SettlementPaymentsSolution};
use chia_puzzle_types::Memos;
use chia_puzzles::SETTLEMENT_PAYMENT_HASH;
use chia_wallet_sdk::driver::{
Layer, OptionType, SettlementLayer, SingletonInfo, SpendContext, SpendWithConditions,
};
use chia_wallet_sdk::types::Conditions;
use crate::error::{Error, Result};
use crate::types::{CreatedOption, OptionSpend, Owner};
#[derive(Clone, Copy, Debug)]
pub struct StrikePayment {
pub funding_coin: Coin,
}
pub fn exercise(
ctx: &mut SpendContext,
holder: &Owner,
created: &CreatedOption,
strike: &StrikePayment,
) -> Result<OptionSpend> {
let OptionType::Xch {
amount: strike_amount,
} = created.underlying.strike_type
else {
return Err(Error::invalid(
"CAT/NFT strike exercise not yet supported — see dig-options CAT/NFT follow-up",
));
};
if strike.funding_coin.amount < strike_amount {
return Err(Error::invalid(format!(
"strike funding coin amount {} is too small: need {strike_amount} for the XCH strike",
strike.funding_coin.amount
)));
}
created.option.exercise(ctx, holder, Conditions::new())?;
created.underlying.exercise_coin_spend(
ctx,
created.underlying_coin,
created.option.info.inner_puzzle_hash().into(),
created.option.coin.amount,
)?;
let underlying_settlement_coin = Coin::new(
created.underlying_coin.coin_id(),
SETTLEMENT_PAYMENT_HASH.into(),
created.underlying.amount,
);
let holder_payment = NotarizedPayment::new(
created.option.info.launcher_id,
vec![Payment::new(
created.option.info.p2_puzzle_hash,
created.underlying.amount,
Memos::None,
)],
);
let underlying_claim = SettlementLayer.construct_coin_spend(
ctx,
underlying_settlement_coin,
SettlementPaymentsSolution::new(vec![holder_payment]),
)?;
ctx.insert(underlying_claim);
let mut strike_conditions =
Conditions::new().create_coin(SETTLEMENT_PAYMENT_HASH.into(), strike_amount, Memos::None);
if let Some(change_amount) = strike
.funding_coin
.amount
.checked_sub(strike_amount)
.filter(|excess| *excess > 0)
{
strike_conditions = strike_conditions.create_coin(
strike.funding_coin.puzzle_hash,
change_amount,
Memos::None,
);
}
let strike_inner = holder.spend_with_conditions(ctx, strike_conditions)?;
ctx.spend(strike.funding_coin, strike_inner)?;
let settlement_coin = Coin::new(
strike.funding_coin.coin_id(),
SETTLEMENT_PAYMENT_HASH.into(),
strike_amount,
);
let payment = created.underlying.requested_payment(&mut **ctx)?;
let coin_spend = SettlementLayer.construct_coin_spend(
ctx,
settlement_coin,
SettlementPaymentsSolution::new(vec![payment]),
)?;
ctx.insert(coin_spend);
Ok(OptionSpend {
coin_spends: ctx.take(),
created: None,
})
}