datalayer_driver/
dig_coin.rs

1use crate::error::WalletError;
2use crate::wallet::DIG_ASSET_ID;
3use crate::{Bytes32, Coin, Peer};
4use chia::protocol::CoinState;
5use chia::puzzles::cat::CatArgs;
6use chia_wallet_sdk::driver::{Asset, Cat, Puzzle, SpendContext};
7use chia_wallet_sdk::prelude::{TreeHash, MAINNET_CONSTANTS};
8
9pub struct DigCoin {
10    cat: Cat,
11}
12
13impl DigCoin {
14    #[inline]
15    pub fn cat(&self) -> Cat {
16        self.cat
17    }
18
19    pub fn puzzle_hash(wallet_puzzle_hash: Bytes32) -> Bytes32 {
20        let ph_bytes =
21            CatArgs::curry_tree_hash(DIG_ASSET_ID, TreeHash::from(wallet_puzzle_hash)).to_bytes();
22        Bytes32::from(ph_bytes)
23    }
24
25    pub async fn from_coin_state(peer: &Peer, coin_state: &CoinState) -> Result<Self, WalletError> {
26        let coin_created_height = coin_state.created_height.ok_or(WalletError::Parse(
27            "Cannot determine coin creation height".to_string(),
28        ))?;
29        Self::from_coin(peer, &coin_state.coin, coin_created_height).await
30    }
31
32    /// Function to validate that a coin is a $DIG CAT coin. Returns an instantiated DIG token
33    /// CAT for the coin if it's a valid $DIG CAT
34    pub async fn from_coin(
35        peer: &Peer,
36        coin: &Coin,
37        coin_created_height: u32,
38    ) -> Result<Self, WalletError> {
39        let mut ctx = SpendContext::new();
40
41        // 1) Request parent coin state
42        let parent_state_response = peer
43            .request_coin_state(
44                vec![coin.parent_coin_info],
45                None,
46                MAINNET_CONSTANTS.genesis_challenge,
47                false,
48            )
49            .await?;
50
51        let parent_state = parent_state_response.map_err(|_| WalletError::RejectCoinState)?;
52
53        // 2) Request parent puzzle and solution
54        let parent_puzzle_and_solution_response = peer
55            .request_puzzle_and_solution(parent_state.coin_ids[0], coin_created_height)
56            .await?;
57
58        let parent_puzzle_and_solution =
59            parent_puzzle_and_solution_response.map_err(|_| WalletError::RejectPuzzleSolution)?;
60
61        // 3) Convert puzzle to CLVM
62        let parent_puzzle_ptr = ctx.alloc(&parent_puzzle_and_solution.puzzle)?;
63        let parent_puzzle = Puzzle::parse(&ctx, parent_puzzle_ptr);
64
65        // 4) Convert solution to CLVM
66        let parent_solution = ctx.alloc(&parent_puzzle_and_solution.solution)?;
67
68        // 5) Parse CAT
69        let parsed_children = Cat::parse_children(
70            &mut ctx,
71            parent_state.coin_states[0].coin,
72            parent_puzzle,
73            parent_solution,
74        )?
75        .ok_or(WalletError::UnknownCoin)?;
76
77        let proved_cat = parsed_children
78            .into_iter()
79            .find(|parsed_child| {
80                parsed_child.coin_id() == coin.coin_id()
81                    && parsed_child.lineage_proof.is_some()
82                    && parsed_child.info.asset_id == DIG_ASSET_ID
83            })
84            .ok_or_else(|| WalletError::UnknownCoin)?;
85
86        Ok(Self { cat: proved_cat })
87    }
88}