use chia_protocol::{Bytes32, SpendBundle};
use chia_sdk_test::{sign_transaction, BlsPairWithCoin, Simulator};
use chia_wallet_sdk::driver::{
Action, Cat, Id, Nft, Relation, SpendContext, Spends, StandardLayer,
};
use chia_wallet_sdk::prelude::{SecretKey, Signature};
use chia_wallet_sdk::types::{Conditions, TESTNET11_CONSTANTS};
use indexmap::{indexmap, IndexMap};
use crate::sign::required_signatures;
pub(crate) fn sign_for_sim(
coin_spends: &[chia_protocol::CoinSpend],
secret_keys: &[SecretKey],
) -> anyhow::Result<Signature> {
let reported =
required_signatures(coin_spends, TESTNET11_CONSTANTS.agg_sig_me_additional_data)?;
assert!(
!reported.is_empty(),
"a spend must report required signatures"
);
Ok(sign_transaction(coin_spends, secret_keys)?)
}
pub(crate) fn issue_cat_to(
sim: &mut Simulator,
ctx: &mut SpendContext,
owner: &BlsPairWithCoin,
amount: u64,
) -> anyhow::Result<(Cat, Bytes32)> {
let funding = sim.new_coin(owner.puzzle_hash, amount);
let p2 = StandardLayer::new(owner.pk);
let hint = ctx.hint(owner.puzzle_hash)?;
let (issue, cats) = Cat::single_issuance(
ctx,
funding.coin_id(),
None,
amount,
Conditions::new().create_coin(owner.puzzle_hash, amount, hint),
)?;
p2.spend(ctx, funding, issue)?;
let asset_id = cats[0].info.asset_id;
sim.spend_coins(ctx.take(), std::slice::from_ref(&owner.sk))?;
Ok((cats[0], asset_id))
}
pub(crate) fn mint_nft_for(
sim: &mut Simulator,
ctx: &mut SpendContext,
owner: &BlsPairWithCoin,
royalty_basis_points: u16,
) -> anyhow::Result<Nft> {
let mut spends = Spends::new(owner.puzzle_hash);
spends.add(owner.coin);
let deltas = spends.apply(
ctx,
&[Action::mint_empty_royalty_nft(
owner.puzzle_hash,
royalty_basis_points,
)],
)?;
let outputs = spends.finish_with_keys(
ctx,
&deltas,
Relation::AssertConcurrent,
&indexmap! { owner.puzzle_hash => owner.pk },
)?;
let nft = outputs.nfts[&Id::New(0)];
sim.spend_coins(ctx.take(), std::slice::from_ref(&owner.sk))?;
Ok(nft)
}
pub(crate) fn owner_keys(
owner: &BlsPairWithCoin,
) -> IndexMap<Bytes32, chia_wallet_sdk::prelude::PublicKey> {
indexmap! { owner.puzzle_hash => owner.pk }
}
pub(crate) fn signed_bundle(
coin_spends: Vec<chia_protocol::CoinSpend>,
signature: Signature,
) -> SpendBundle {
SpendBundle::new(coin_spends, signature)
}
pub(crate) fn maker_offer(
ctx: &mut SpendContext,
offered: crate::OfferedSide<'_>,
requested: crate::RequestedSide,
fee: u64,
secret_keys: &[SecretKey],
) -> anyhow::Result<String> {
let unsigned = crate::make_build(ctx, offered, requested, fee)?;
assert!(
unsigned
.coin_spends
.iter()
.all(|cs| cs.coin.parent_coin_info != Bytes32::default()),
"make_build must produce only real (non-phantom) maker spends"
);
let signature = sign_for_sim(&unsigned.coin_spends, secret_keys)?;
Ok(crate::make_assemble(
ctx,
signed_bundle(unsigned.coin_spends, signature),
unsigned.requested_payments,
unsigned.requested_asset_info,
)?)
}
pub(crate) fn sample_cat_for_xch(
sim: &mut Simulator,
ctx: &mut SpendContext,
offered_cat: u64,
requested_xch: u64,
) -> anyhow::Result<(String, BlsPairWithCoin, Bytes32)> {
let maker = sim.bls(0);
let (maker_cat, asset) = issue_cat_to(sim, ctx, &maker, offered_cat)?;
let offered = crate::OfferedSide {
change_puzzle_hash: maker.puzzle_hash,
owner_keys: owner_keys(&maker),
xch_coins: vec![],
cat_coins: vec![maker_cat],
nfts: vec![],
offer_xch: 0,
offer_cats: vec![(asset, offered_cat)],
_pd: std::marker::PhantomData,
};
let requested = crate::RequestedSide {
payee_puzzle_hash: maker.puzzle_hash,
xch: requested_xch,
cats: vec![],
nfts: vec![],
};
let offer_str = maker_offer(ctx, offered, requested, 0, std::slice::from_ref(&maker.sk))?;
Ok((offer_str, maker, asset))
}
pub(crate) fn taker_settle(
ctx: &mut SpendContext,
offer_str: &str,
funds: crate::TakerFunds<'_>,
fee: u64,
secret_keys: &[SecretKey],
) -> anyhow::Result<SpendBundle> {
let unsigned = crate::take_build(ctx, offer_str, funds, fee)?;
let signature = sign_for_sim(&unsigned.coin_spends, secret_keys)?;
Ok(crate::take_combine(
unsigned.offer,
signed_bundle(unsigned.coin_spends, signature),
))
}