penumbra_sdk_auction/auction/
nft.rs

1use crate::auction::id::AuctionId;
2use anyhow::{anyhow, Result};
3use penumbra_sdk_asset::asset::{self, Metadata};
4use penumbra_sdk_proto::{core::component::auction::v1 as pb, DomainType};
5use regex::Regex;
6
7/// An non-fungible token (NFT) tracking the state and ownership of an auction.
8#[derive(Debug, Clone)]
9pub struct AuctionNft {
10    /// The unique identifier for the auction this nft resolves to.
11    pub id: AuctionId,
12    /// The state of an auction, its specific semantics depend on the
13    /// type of auction the NFT resolves to.
14    pub seq: u64,
15    /// The metadata corresponding to the nft denom.
16    pub metadata: asset::Metadata,
17}
18
19impl AuctionNft {
20    pub fn new(id: AuctionId, seq: u64) -> AuctionNft {
21        let metadata = asset::REGISTRY
22            .parse_denom(&format!("auctionnft_{seq}_{id}"))
23            .expect("auction nft denom is valid");
24        AuctionNft { id, seq, metadata }
25    }
26
27    pub fn asset_id(&self) -> asset::Id {
28        self.metadata.id()
29    }
30}
31
32/* Protobuf impls ;*/
33impl DomainType for AuctionNft {
34    type Proto = pb::AuctionNft;
35}
36
37impl From<AuctionNft> for pb::AuctionNft {
38    fn from(domain: AuctionNft) -> Self {
39        Self {
40            id: Some(domain.id.into()),
41            seq: domain.seq,
42        }
43    }
44}
45
46impl TryFrom<pb::AuctionNft> for AuctionNft {
47    type Error = anyhow::Error;
48
49    fn try_from(msg: pb::AuctionNft) -> Result<Self, Self::Error> {
50        let id: AuctionId = msg
51            .id
52            .ok_or_else(|| anyhow!("AuctionNft message is missing an auction id"))?
53            .try_into()?;
54        let seq = msg.seq;
55        Ok(AuctionNft::new(id, seq))
56    }
57}
58
59impl TryFrom<Metadata> for AuctionNft {
60    type Error = anyhow::Error;
61
62    fn try_from(denom: Metadata) -> Result<Self, Self::Error> {
63        let regex = Regex::new(
64            "^auctionnft_(?P<seq_num>[0-9]+)_(?P<auction_id>pauctid1[a-zA-HJ-NP-Z0-9]+)$",
65        )
66        .expect("regex is valid");
67
68        let denom_string = denom.to_string();
69
70        let captures = regex
71            .captures(&denom_string)
72            .ok_or_else(|| anyhow!("denom {} is not a valid auction nft", denom))?;
73
74        let seq_num = captures
75            .name("seq_num")
76            .ok_or_else(|| anyhow!("sequence number not found"))?
77            .as_str();
78        let auction_id = captures
79            .name("auction_id")
80            .ok_or_else(|| anyhow!("auction ID not found"))?
81            .as_str();
82
83        let seq_num: u64 = seq_num
84            .parse()
85            .map_err(|_| anyhow!("Failed to parse seq_num to u64"))?;
86
87        let auction_id: AuctionId = auction_id
88            .parse()
89            .map_err(|_| anyhow!("Failed to parse auction_id to AuctionId"))?;
90
91        Ok(AuctionNft::new(auction_id, seq_num))
92    }
93}