Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compressed NFTs #12

Merged
merged 10 commits into from
Jul 24, 2023
709 changes: 519 additions & 190 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
[workspace]
members = ["consumer", "core", "entity", "migration", "indexer"]
resolver = "2"

[workspace.dependencies]
holaplex-hub-nfts-solana-core = { path = "core" }
holaplex-hub-nfts-solana-entity = { path = "entity" }
Comment on lines -5 to -7
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self: why did i do this?

8 changes: 7 additions & 1 deletion consumer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,28 @@ categories = ["cryptography::cryptocurrencies", "web-programming"]
[lib]

[dependencies]
anchor-lang = "0.26.0"
bincode = "1.3.3"

serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93"
solana-program = "1.14.8"
solana-client = "1.14.8"
spl-account-compression = "0.1.10"
spl-noop = "0.1.3"
spl-token = "3.5.0"
solana-sdk = "1.14.8"
spl-associated-token-account = "1.1.2"
mpl-bubblegum = "0.8.0"
mpl-token-metadata = "1.8.3"
holaplex-hub-nfts-solana-core = { path = "../core" }
holaplex-hub-nfts-solana-entity = { path = "../entity" }
jsonrpsee = { version = "0.18.2", features = ["macros", "http-client"] }
bs58 = "0.5.0"

[dependencies.hub-core]
package = "holaplex-hub-core"
version = "0.2.0"
git = "https://github.com/holaplex/hub-core"
branch = "stable"
features = ["kafka"]
features = ["kafka"]
141 changes: 141 additions & 0 deletions consumer/src/asset_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
mod b58 {
use serde::{de::Visitor, Deserializer, Serializer};

pub fn serialize<S: Serializer>(bytes: &[u8], ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_str(&bs58::encode(bytes).into_string())
}

pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<Vec<u8>, D::Error> {
struct Vis;

impl<'a> Visitor<'a> for Vis {
type Value = Vec<u8>;

fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a base58-encoded string")
}

fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
bs58::decode(s).into_vec().map_err(E::custom)
}
}

de.deserialize_str(Vis)
}
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct Base58(#[serde(with = "b58")] pub Vec<u8>);

impl From<Vec<u8>> for Base58 {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}

impl From<Base58> for Vec<u8> {
fn from(Base58(v): Base58) -> Self {
v
}
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct Asset {
pub interface: String,
pub id: Base58,
pub content: serde_json::Value,
pub authorities: Vec<AssetAuthority>,
pub compression: AssetCompression,
pub grouping: Vec<AssetGrouping>,
pub royalty: AssetRoyalty,
pub creators: Vec<AssetCreator>,
pub ownership: AssetOwnership,
pub supply: Option<u32>,
pub mutable: bool,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetAuthority {
pub address: Base58,
pub scopes: Vec<String>,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetCompression {
pub eligible: bool,
pub compressed: bool,
pub data_hash: Base58,
pub creator_hash: Base58,
pub asset_hash: Base58,
pub tree: Base58,
pub seq: u32,
pub leaf_id: u32,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetGrouping {
pub group_key: String,
pub group_value: Base58,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetRoyalty {
pub royalty_model: String,
pub target: Option<serde_json::Number>, // TODO: what type is this
pub percent: serde_json::Number, // TODO: this is fractional, use BCD to avoid rounding error
pub basis_points: u32,
pub primary_sale_happened: bool,
pub locked: bool,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetCreator {
pub address: Base58,
pub share: u32,
pub verified: bool,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetOwnership {
pub frozen: bool,
pub delegated: bool,
pub delegate: Base58,
pub ownership_model: String,
pub owner: Base58,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AssetProof {
pub root: Base58,
pub proof: Vec<Base58>,
pub node_index: u32,
pub leaf: Base58,
pub tree_id: Base58,
}

#[jsonrpsee::proc_macros::rpc(client)]
pub trait Rpc {
#[method(name = "getAsset", param_kind = map)]
fn get_asset(&self, id: &str) -> Result<Asset, Error>;

#[method(name = "getAssetProof", param_kind = map)]
fn get_asset_proof(&self, id: &str) -> Result<AssetProof, Error>;

// Supposedly Triton offers these but their docs were crashing my browser
// so I don't know what the signatures are.

// #[method(name = "getAssetsByAuthority")]
// fn get_assets_by_authority(&self);

// #[method(name = "getAssetsByOwner")]
// fn get_assets_by_owner(&self);

// #[method(name = "getAssetsByGroup")]
// fn get_assets_by_group(&self);

// #[method(name = "getAssetsByCreator")]
// fn get_assets_by_creator(&self);

// #[method(name = "searchAssets")]
// fn search_assets(&self);
}
110 changes: 110 additions & 0 deletions consumer/src/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use holaplex_hub_nfts_solana_core::proto::{
MetaplexMasterEditionTransaction, SolanaPendingTransaction, TransferMetaplexAssetTransaction,
};
use holaplex_hub_nfts_solana_entity::{collection_mints, collections};
use hub_core::prelude::*;
use solana_program::pubkey::Pubkey;

#[derive(Clone)]
pub struct MasterEditionAddresses {
pub metadata: Pubkey,
pub associated_token_account: Pubkey,
pub owner: Pubkey,
pub master_edition: Pubkey,
pub mint: Pubkey,
pub update_authority: Pubkey,
}

#[derive(Clone)]
pub struct MintEditionAddresses {
pub edition: Pubkey,
pub mint: Pubkey,
pub metadata: Pubkey,
pub owner: Pubkey,
pub associated_token_account: Pubkey,
pub recipient: Pubkey,
}

#[derive(Clone)]
pub struct MintMetaplexAddresses {
pub mint: Pubkey,
pub metadata: Pubkey,
pub owner: Pubkey,
pub associated_token_account: Pubkey,
pub recipient: Pubkey,
pub update_authority: Pubkey,
}

#[derive(Clone)]
pub struct MintCompressedMintV1Addresses {
pub owner: Pubkey,
pub recipient: Pubkey,
}

#[derive(Clone)]
pub struct UpdateMasterEditionAddresses {
pub metadata: Pubkey,
pub update_authority: Pubkey,
}

#[derive(Clone)]
pub struct TransferAssetAddresses {
pub owner: Pubkey,
pub recipient: Pubkey,
pub recipient_associated_token_account: Pubkey,
pub owner_associated_token_account: Pubkey,
}

/// Represents a response from a transaction on the blockchain. This struct
/// provides the serialized message and the signatures of the signed message.
pub struct TransactionResponse<A> {
/// The serialized version of the message from the transaction.
pub serialized_message: Vec<u8>,

/// The signatures of the signed message or the public keys of wallets that should sign the transaction. Order matters.
pub signatures_or_signers_public_keys: Vec<String>,

/// Addresses that are related to the transaction.
pub addresses: A,
}

impl<A> From<TransactionResponse<A>> for SolanaPendingTransaction {
fn from(
TransactionResponse {
serialized_message,
signatures_or_signers_public_keys,
..
}: TransactionResponse<A>,
) -> Self {
Self {
serialized_message,
signatures_or_signers_public_keys,
}
}
}

pub trait CollectionBackend {
fn create(
&self,
txn: MetaplexMasterEditionTransaction,
) -> Result<TransactionResponse<MasterEditionAddresses>>;

fn update(
&self,
collection: &collections::Model,
txn: MetaplexMasterEditionTransaction,
) -> Result<TransactionResponse<UpdateMasterEditionAddresses>>;
}

pub trait MintBackend<T, R> {
fn mint(&self, collection: &collections::Model, txn: T) -> Result<TransactionResponse<R>>;
}

#[async_trait]
pub trait TransferBackend {
async fn transfer(
&self,
collection_mint: &collection_mints::Model,
txn: TransferMetaplexAssetTransaction,
) -> Result<TransactionResponse<TransferAssetAddresses>>;
}
Loading
Loading