Skip to content

Commit

Permalink
Add NFT URL button
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Nov 8, 2024
1 parent 9df22c0 commit c774d97
Show file tree
Hide file tree
Showing 13 changed files with 350 additions and 40 deletions.
57 changes: 34 additions & 23 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ tauri-specta = "=2.0.0-rc.18"
# Chia
chia = "0.15.0"
clvmr = "0.9.0"
chia-wallet-sdk = { version = "0.17.0", features = ["rustls"] }
chia-wallet-sdk = { version = "0.18.0", features = ["rustls"] }
bip39 = "2.0.0"
bech32 = "0.9.1"

Expand Down
2 changes: 2 additions & 0 deletions crates/sage-api/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod amount;
mod nft_uri_kind;
mod unit;

pub use amount::*;
pub use nft_uri_kind::*;
pub use unit::*;
10 changes: 10 additions & 0 deletions crates/sage-api/src/types/nft_uri_kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
use specta::Type;

#[derive(Debug, Clone, Copy, Serialize, Deserialize, Type)]
#[serde(rename_all = "snake_case")]
pub enum NftUriKind {
Data,
Metadata,
License,
}
5 changes: 4 additions & 1 deletion crates/sage-wallet/src/sync_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ impl SyncManager {
self.pending_coin_subscriptions.extend(coin_ids);
}
SyncCommand::ConnectionClosed(ip) => {
self.state.lock().await.remove_peer(ip);
self.state
.lock()
.await
.ban(ip, Duration::from_secs(300), "peer disconnected");
debug!("Peer {ip} disconnected");
}
SyncCommand::SetDiscoverPeers(discover_peers) => {
Expand Down
31 changes: 26 additions & 5 deletions crates/sage-wallet/src/sync_manager/peer_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use chia::{
protocol::{Message, NewPeakWallet, ProtocolMessageTypes},
traits::Streamable,
};
use chia_wallet_sdk::{connect_peer, Peer};
use chia_wallet_sdk::{connect_peer, Peer, PeerOptions};
use futures_lite::StreamExt;
use futures_util::stream::FuturesUnordered;
use tokio::{sync::mpsc, time::timeout};
Expand Down Expand Up @@ -153,8 +153,11 @@ impl SyncManager {
let duration = self.options.connection_timeout;

futures.push(async move {
let result =
timeout(duration, connect_peer(network_id, connector, socket_addr)).await;
let result = timeout(
duration,
connect_peer(network_id, connector, socket_addr, PeerOptions::default()),
)
.await;
(socket_addr, result)
});
}
Expand Down Expand Up @@ -229,7 +232,25 @@ impl SyncManager {
let ip = peer.socket_addr().ip();
let sender = self.command_sender.clone();

self.state.lock().await.add_peer(PeerInfo {
let mut state = self.state.lock().await;

for (peer, height) in state.peers_with_heights() {
if message.height < height.saturating_sub(3) {
debug!(
"Peer {} is behind by more than 3 blocks, disconnecting",
peer.socket_addr()
);
return false;
} else if message.height > height.saturating_add(3) {
state.ban(
peer.socket_addr().ip(),
Duration::from_secs(900),
"peer is behind",
);
}
}

state.add_peer(PeerInfo {
peer: WalletPeer::new(peer),
claimed_peak: message.height,
header_hash: message.header_hash,
Expand All @@ -242,7 +263,7 @@ impl SyncManager {
.await
.is_err()
{
return;
break;
}
}

Expand Down
59 changes: 58 additions & 1 deletion crates/sage-wallet/src/wallet/nfts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use chia::{
puzzles::nft::{NftMetadata, NFT_METADATA_UPDATER_PUZZLE_HASH},
};
use chia_wallet_sdk::{
Conditions, Did, DidOwner, HashedPtr, Launcher, Nft, NftMint, SpendContext, StandardLayer,
Conditions, Did, DidOwner, HashedPtr, Launcher, MetadataUpdate, Nft, NftMint, SpendContext,
StandardLayer,
};

use crate::WalletError;
Expand Down Expand Up @@ -136,4 +137,60 @@ impl Wallet {

Ok((ctx.take(), new_nft))
}

pub async fn add_nft_uri(
&self,
nft_id: Bytes32,
fee: u64,
uri: MetadataUpdate,
hardened: bool,
reuse: bool,
) -> Result<(Vec<CoinSpend>, Nft<Program>), WalletError> {
let Some(nft) = self.db.spendable_nft(nft_id).await? else {
return Err(WalletError::MissingNft(nft_id));
};

let total_amount = fee as u128 + 1;
let coins = self.select_p2_coins(total_amount).await?;
let selected: u128 = coins.iter().map(|coin| coin.amount as u128).sum();

let change: u64 = (selected - total_amount)
.try_into()
.expect("change amount overflow");

let p2_puzzle_hash = self.p2_puzzle_hash(hardened, reuse).await?;

let mut ctx = SpendContext::new();

let nft_metadata_ptr = ctx.alloc(&nft.info.metadata)?;
let nft = nft.with_metadata(HashedPtr::from_ptr(&ctx.allocator, nft_metadata_ptr));

let synthetic_key = self.db.synthetic_key(nft.info.p2_puzzle_hash).await?;
let p2 = StandardLayer::new(synthetic_key);

let update_spend = uri.spend(&mut ctx)?;
let new_nft: Nft<HashedPtr> = nft.transfer_with_metadata(
&mut ctx,
&p2,
nft.info.p2_puzzle_hash,
update_spend,
Conditions::new(),
)?;

let mut conditions = Conditions::new().assert_concurrent_spend(nft.coin.coin_id());

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

if change > 0 {
conditions = conditions.create_coin(p2_puzzle_hash, change, Vec::new());
}

self.spend_p2_coins(&mut ctx, coins, conditions).await?;

let new_nft = new_nft.with_metadata(ctx.serialize(&new_nft.info.metadata)?);

Ok((ctx.take(), new_nft))
}
}
46 changes: 43 additions & 3 deletions src-tauri/src/commands/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ use chia::{
puzzles::nft::NftMetadata,
};
use chia_wallet_sdk::{
decode_address, encode_address, AggSigConstants, MAINNET_CONSTANTS, TESTNET11_CONSTANTS,
decode_address, encode_address, AggSigConstants, MetadataUpdate, MAINNET_CONSTANTS,
TESTNET11_CONSTANTS,
};
use hex_literal::hex;
use sage_api::{
Amount, BulkMintNfts, BulkMintNftsResponse, CoinJson, CoinSpendJson, Input, InputKind, Output,
SpendBundleJson, TransactionSummary,
Amount, BulkMintNfts, BulkMintNftsResponse, CoinJson, CoinSpendJson, Input, InputKind,
NftUriKind, Output, SpendBundleJson, TransactionSummary,
};
use sage_database::{CatRow, Database};
use sage_wallet::{
Expand Down Expand Up @@ -508,6 +509,45 @@ pub async fn transfer_nft(
summarize(&state, &wallet, coin_spends, ConfirmationInfo::default()).await
}

#[command]
#[specta]
pub async fn add_nft_uri(
state: State<'_, AppState>,
nft_id: String,
uri: String,
kind: NftUriKind,
fee: Amount,
) -> Result<TransactionSummary> {
let state = state.lock().await;
let wallet = state.wallet()?;

if !state.keychain.has_secret_key(wallet.fingerprint) {
return Err(Error::no_secret_key());
}

let (launcher_id, prefix) = decode_address(&nft_id)?;

if prefix != "nft" {
return Err(Error::invalid_prefix(&prefix));
}

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

let uri = match kind {
NftUriKind::Data => MetadataUpdate::NewDataUri(uri),
NftUriKind::Metadata => MetadataUpdate::NewMetadataUri(uri),
NftUriKind::License => MetadataUpdate::NewLicenseUri(uri),
};

let (coin_spends, _new_nft) = wallet
.add_nft_uri(launcher_id.into(), fee, uri, false, true)
.await?;

summarize(&state, &wallet, coin_spends, ConfirmationInfo::default()).await
}

#[command]
#[specta]
pub async fn transfer_did(
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub fn run() {
commands::bulk_mint_nfts,
commands::transfer_nft,
commands::transfer_did,
commands::add_nft_uri,
commands::sign_transaction,
commands::submit_transaction,
// Peers
Expand Down
Loading

0 comments on commit c774d97

Please sign in to comment.