Skip to content

Commit

Permalink
WIP offers
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Nov 11, 2024
1 parent e99b4ba commit 5630513
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 8 deletions.
1 change: 1 addition & 0 deletions crates/sage-api/src/requests/make_offer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::Amount;
pub struct MakeOffer {
pub requested_assets: Assets,
pub offered_assets: Assets,
pub fee: Amount,
}

#[derive(Debug, Clone, Serialize, Deserialize, Type)]
Expand Down
61 changes: 54 additions & 7 deletions crates/sage-wallet/src/wallet/make_offer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use chia::{
protocol::Bytes32,
puzzles::{
cat::CatArgs,
offer::{Payment, SETTLEMENT_PAYMENTS_PUZZLE_HASH},
offer::{NotarizedPayment, Payment, SETTLEMENT_PAYMENTS_PUZZLE_HASH},
},
};
use chia_wallet_sdk::{
calculate_nft_trace_price, Condition, Conditions, HashedPtr, Layer, NftInfo, Offer,
OfferBuilder, SpendContext, StandardLayer, TradePrice,
calculate_nft_royalty, calculate_nft_trace_price, payment_assertion, Condition, Conditions,
HashedPtr, Layer, NftInfo, Offer, OfferBuilder, SpendContext, StandardLayer, TradePrice,
};
use indexmap::IndexMap;

Expand All @@ -32,8 +32,10 @@ impl Wallet {
let mut coin_ids = Vec::new();

// Select coins for the XCH being offered.
let p2_coins = if offered.xch > 0 {
self.select_p2_coins(offered.xch as u128).await?
let total_xch = offered.xch + offered.fee;

let p2_coins = if total_xch > 0 {
self.select_p2_coins(total_xch as u128).await?
} else {
Vec::new()
};
Expand Down Expand Up @@ -119,6 +121,45 @@ impl Wallet {
)?;
}

// Add royalty payments for NFTs you are offering.
let mut royalty_assertions = Vec::new();

if !nfts.is_empty() {
for (asset_id, amount) in [(None, requested.xch)].into_iter().chain(
requested
.cats
.iter()
.map(|(asset_id, amount)| (Some(*asset_id), *amount)),
) {
let trade_price = calculate_nft_trace_price(amount, nfts.len())
.ok_or(WalletError::InvalidTradePrice)?;

for NftOfferSpend { nft, .. } in &nfts {
let royalty =
calculate_nft_royalty(trade_price, nft.info.royalty_ten_thousandths)
.ok_or(WalletError::InvalidRoyaltyAmount)?;

let mut puzzle_hash = SETTLEMENT_PAYMENTS_PUZZLE_HASH;

if let Some(asset_id) = asset_id {
puzzle_hash = CatArgs::curry_tree_hash(asset_id, puzzle_hash);
}

let notarized_payment = NotarizedPayment {
nonce: nft.info.launcher_id,
payments: vec![Payment::with_memos(
nft.info.royalty_puzzle_hash,
royalty,
vec![nft.info.royalty_puzzle_hash.into()],
)],
};

royalty_assertions
.push(payment_assertion(puzzle_hash.into(), &notarized_payment));
}
}
}

// Add requested CAT payments.
for (asset_id, amount) in requested.cats {
builder = builder.request(
Expand Down Expand Up @@ -161,13 +202,15 @@ impl Wallet {
}

// Finish the requested payments and get the list of announcement assertions.
let (assertions, builder) = builder.finish();
let (mut assertions, builder) = builder.finish();
assertions.extend(royalty_assertions);

self.spend_assets(
&mut ctx,
OfferSpend {
p2_coins,
p2_amount: offered.xch,
fee: offered.fee,
cats: cats
.into_iter()
.map(|(asset_id, coins)| CatOfferSpend {
Expand Down Expand Up @@ -252,7 +295,7 @@ impl Wallet {
}

let total: u128 = spend.p2_coins.iter().map(|coin| coin.amount as u128).sum();
let change = total - spend.p2_amount as u128;
let change = total - spend.p2_amount as u128 - spend.fee as u128;

if change > 0 {
conditions = conditions.create_coin(
Expand All @@ -262,6 +305,10 @@ impl Wallet {
);
}

if spend.fee > 0 {
conditions = conditions.reserve_fee(spend.fee);
}

self.spend_p2_coins(ctx, spend.p2_coins, conditions).await?;
}

Expand Down
2 changes: 2 additions & 0 deletions crates/sage-wallet/src/wallet/offer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct OfferedCoins {
pub xch: u64,
pub cats: IndexMap<Bytes32, u64>,
pub nfts: IndexSet<Bytes32>,
pub fee: u64,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct UnsignedOffer {
pub struct OfferSpend {
pub p2_coins: Vec<Coin>,
pub p2_amount: u64,
pub fee: u64,
pub cats: Vec<CatOfferSpend>,
pub nfts: Vec<NftOfferSpend>,
pub assertions: Vec<AssertPuzzleAnnouncement>,
Expand Down
5 changes: 5 additions & 0 deletions src-tauri/src/commands/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,12 +657,17 @@ pub async fn make_offer(state: State<'_, AppState>, request: MakeOffer) -> Resul
requested_nfts.insert(nft_id, offer_details);
}

let Some(fee) = request.fee.to_mojos(state.unit.decimals) else {
return Err(Error::invalid_amount(&request.fee));
};

let unsigned = wallet
.make_offer(
OfferedCoins {
xch: offered_xch,
cats: offered_cats,
nfts: offered_nfts,
fee,
},
OfferRequest {
xch: requested_xch,
Expand Down
2 changes: 1 addition & 1 deletion src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ export type GetCollectionNfts = { collection_id: string | null; offset: number;
export type GetNftCollections = { offset: number; limit: number; include_hidden: boolean }
export type GetNfts = { offset: number; limit: number; sort_mode: NftSortMode; include_hidden: boolean }
export type Input = ({ type: "unknown" } | { type: "xch" } | { type: "launcher" } | { type: "cat"; asset_id: string; name: string | null; ticker: string | null; icon_url: string | null } | { type: "did"; launcher_id: string; name: string | null } | { type: "nft"; launcher_id: string; image_data: string | null; image_mime_type: string | null; name: string | null }) & { coin_id: string; amount: Amount; address: string; outputs: Output[] }
export type MakeOffer = { requested_assets: Assets; offered_assets: Assets }
export type MakeOffer = { requested_assets: Assets; offered_assets: Assets; fee: Amount }
export type Network = { default_port: number; ticker: string; address_prefix: string; precision: number; genesis_challenge: string; agg_sig_me: string; dns_introducers: string[] }
export type NetworkConfig = { network_id?: string; target_peers?: number; discover_peers?: boolean }
export type NftCollectionRecord = { collection_id: string; did_id: string; metadata_collection_id: string; visible: boolean; name: string | null; icon: string | null; nfts: number; visible_nfts: number }
Expand Down
1 change: 1 addition & 0 deletions src/pages/MakeOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function MakeOffer() {
.makeOffer({
offered_assets: offerAssets,
requested_assets: requestAssets,
fee,
})
.then((result) => {
console.log(result);
Expand Down
5 changes: 5 additions & 0 deletions src/pages/Token.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import CoinList from '@/components/CoinList';
import ConfirmationDialog from '@/components/ConfirmationDialog';
import Container from '@/components/Container';
import { CopyBox } from '@/components/CopyBox';
import Header from '@/components/Header';
import { ReceiveAddress } from '@/components/ReceiveAddress';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -176,6 +177,10 @@ export default function Token() {
<Header title={asset ? (asset.name ?? 'Unknown asset') : ''} />
<Container>
<div className='flex flex-col gap-8 max-w-screen-lg'>
{asset?.asset_id !== 'xch' && (
<CopyBox title='Asset Id' content={asset?.asset_id ?? ''} />
)}

<Card>
<CardHeader className='flex flex-row justify-between items-center space-y-0 space-x-2 pb-2'>
<div className='flex text-xl sm:text-4xl font-medium font-mono truncate'>
Expand Down

0 comments on commit 5630513

Please sign in to comment.