forked from solana-foundation/explorer
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCompressedNFTInfoCard.tsx
More file actions
133 lines (122 loc) · 5 KB
/
CompressedNFTInfoCard.tsx
File metadata and controls
133 lines (122 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { Account, useAccountInfo, useFetchAccountInfo } from '@providers/accounts';
import { cn } from '@shared/utils';
import { ConcurrentMerkleTreeAccount, MerkleTree } from '@solana/spl-account-compression';
import { PublicKey } from '@solana/web3.js';
import React from 'react';
import { useCluster } from '@/app/providers/cluster';
import {
CompressedNft,
CompressedNftProof,
useCompressedNft,
useCompressedNftProof,
} from '@/app/providers/compressed-nft';
import { toBuffer } from '@/app/shared/lib/bytes';
import { Address } from '../common/Address';
import { TableCardBody } from '../common/TableCardBody';
export function CompressedNFTInfoCard({ account, onNotFound }: { account?: Account; onNotFound: () => never }) {
const { url } = useCluster();
const compressedNft = useCompressedNft({ address: account?.pubkey.toString() ?? '', url });
const proof = useCompressedNftProof({ address: account?.pubkey.toString() ?? '', url });
if (compressedNft && compressedNft.compression.compressed && proof) {
return <DasCompressionInfoCard proof={proof} compressedNft={compressedNft} />;
}
return onNotFound();
}
function DasCompressionInfoCard({ proof, compressedNft }: { proof: CompressedNftProof; compressedNft: CompressedNft }) {
const compressedInfo = compressedNft.compression;
const fetchAccountInfo = useFetchAccountInfo();
const treeAccountInfo = useAccountInfo(compressedInfo.tree);
const treeAddress = new PublicKey(compressedInfo.tree);
React.useEffect(() => {
fetchAccountInfo(treeAddress, 'raw');
}, [compressedInfo.tree]); // eslint-disable-line react-hooks/exhaustive-deps
const root = new PublicKey(proof.root);
const proofVerified = MerkleTree.verify(root.toBuffer(), {
leaf: new PublicKey(compressedNft.compression.asset_hash).toBuffer(),
leafIndex: compressedNft.compression.leaf_id,
proof: proof.proof.map(proofData => new PublicKey(proofData).toBuffer()),
root: root.toBuffer(),
});
const canopyDepth =
treeAccountInfo && treeAccountInfo.data && treeAccountInfo.data.data.raw
? ConcurrentMerkleTreeAccount.fromBuffer(toBuffer(treeAccountInfo.data.data.raw)).getCanopyDepth()
: 0;
const proofSize = proof.proof.length - canopyDepth;
return (
<div className="card">
<div className="card-header align-items-center">
<h3 className="card-header-title">Compression Info</h3>
</div>
<TableCardBody>
<tr>
<td>Concurrent Merkle Tree</td>
<td>
<Address pubkey={treeAddress} alignRight link raw />
</td>
</tr>
<tr>
<td>Current Tree Root {getVerifiedProofPill(proofVerified)}</td>
<td>
<Address pubkey={root} alignRight raw />
</td>
</tr>
<tr>
<td>Proof Size {getProofSizePill(proofSize)}</td>
<td className="text-lg-end">{proofSize}</td>
</tr>
<tr>
<td>Leaf Number</td>
<td className="text-lg-end">{compressedInfo.leaf_id}</td>
</tr>
<tr>
<td>Sequence Number of Last Update</td>
<td className="text-lg-end">{compressedInfo.seq}</td>
</tr>
<tr>
<td>Compressed Nft Hash</td>
<td>
<Address pubkey={new PublicKey(compressedInfo.asset_hash)} alignRight raw />
</td>
</tr>
<tr>
<td>Creators Hash</td>
<td>
<Address pubkey={new PublicKey(compressedInfo.creator_hash)} alignRight raw />
</td>
</tr>
<tr>
<td>Metadata Hash</td>
<td>
<Address pubkey={new PublicKey(compressedInfo.data_hash)} alignRight raw />
</td>
</tr>
</TableCardBody>
</div>
);
}
function getVerifiedProofPill(verified: boolean) {
return (
<div className={'d-inline-flex align-items-center ms-2'}>
<span className={cn('badge badge-pill bg-dark', !verified && 'bg-danger-soft')}>{`Proof ${
verified ? '' : 'Not'
} Verified`}</span>
</div>
);
}
function getProofSizePill(proofSize: number) {
let text: string;
let color = 'bg-dark';
if (proofSize == 0) {
text = 'No Proof Required';
} else if (proofSize > 8) {
text = `Composability Hazard`;
color = 'bg-danger-soft';
} else {
return <div />;
}
return (
<div className={'d-inline-flex align-items-center ms-2'}>
<span className={`badge badge-pill ${color}`}>{text}</span>
</div>
);
}