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

Release 07/27 #28

Merged
merged 29 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
893759c
WIP: partial draft of compressed NFTs
ray-kast Jun 23, 2023
a100069
Scaffolded new backends and asset API client.
ray-kast Jul 7, 2023
450f463
Refactor events.rs.
ray-kast Jul 19, 2023
eff123b
add logs to solana rpc calls
mpwsh Jul 19, 2023
6d3f5ff
Merge pull request #22 from holaplex/mpw/solana-rpc-logs
mpwsh Jul 19, 2023
e94fc36
refactor: support mcc
kespinola Jul 17, 2023
6245577
refactor: adding certified_collections and actions for updating a col…
kespinola Jul 18, 2023
5eab139
refactor: unify collection creation to single backend since both requ…
kespinola Jul 20, 2023
b5b5975
feat: add extra account for doing creator verification of compressed …
kespinola Jul 21, 2023
74b8ff6
Merge pull request #21 from holaplex/espi/mcc-collections
kespinola Jul 21, 2023
fcd1bff
fix: handle signed requests for collections
kespinola Jul 22, 2023
a2c2215
faet: generate instruction and submit transactions for minting to a c…
kespinola Jul 24, 2023
d8f51c0
Merge pull request #12 from holaplex/ryans/compression
kespinola Jul 24, 2023
bd9a6bf
feat: compressed collection minting
kespinola Jul 25, 2023
37e68e8
Merge pull request #26 from holaplex/espi/compression
kespinola Jul 25, 2023
685d4d1
Import Collections
imabdulbasit Jul 20, 2023
8741476
Batch insert, fix models & insertion
imabdulbasit Jul 21, 2023
a55b680
get image url from files + send mint id
imabdulbasit Jul 21, 2023
a6f59ec
delete if collection already exists
imabdulbasit Jul 24, 2023
bae348a
separate out collection import processing
imabdulbasit Jul 25, 2023
bf1deca
Route events between processors.
ray-kast Jul 25, 2023
167f150
...
imabdulbasit Jul 26, 2023
9f9e172
Merge pull request #25 from holaplex/abdul/import-collection
imabdulbasit Jul 26, 2023
7307c59
Make Metadata json symbol optional in asset_api
imabdulbasit Jul 26, 2023
a85fcc5
Merge pull request #27 from holaplex/abdul/make-symbol-optional
imabdulbasit Jul 26, 2023
67060a3
feat: find asset id for compression mint. when processing success han…
kespinola Jul 27, 2023
b5b2fbe
Merge pull request #29 from holaplex/espi/compression
kespinola Jul 27, 2023
fa6d56d
Support import of collection for multiple projects
imabdulbasit Jul 27, 2023
6d0d012
Merge pull request #30 from holaplex/abdul/multi-proj-coll
kespinola Jul 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
711 changes: 521 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" }
21 changes: 13 additions & 8 deletions consumer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ version = "0.1.0"
edition = "2021"
keywords = ["solana", "hub", "holaplex", "nfts"]
publish = false
authors = [
"Holaplex <[email protected]>",
]
authors = ["Holaplex <[email protected]>"]
description = "Holaplex Hub nfts solana service"
readme = "./README.md"
repository = "https://github.com/holaplex/hub-nfts-solana"
Expand All @@ -16,22 +14,29 @@ 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"
solana-program = "1.14.21"
solana-client = "1.14.21"
solana-transaction-status = "1.14.21"
spl-account-compression = "0.1.10"
spl-noop = "0.1.3"
spl-token = "3.5.0"
solana-sdk = "1.14.8"
solana-sdk = "1.14.21"
spl-associated-token-account = "1.1.2"
mpl-bubblegum = "0.7.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"
rand = "0.8.5"

[dependencies.hub-core]
package = "holaplex-hub-core"
version = "0.2.0"
git = "https://github.com/holaplex/hub-core"
branch = "stable"
features = ["kafka"]
features = ["kafka"]
204 changes: 204 additions & 0 deletions consumer/src/asset_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
use core::fmt;
use std::collections::HashMap;

use solana_program::pubkey::Pubkey;
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(Debug, Clone, 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
}
}

impl TryFrom<Base58> for Pubkey {
type Error = std::array::TryFromSliceError;

fn try_from(Base58(v): Base58) -> Result<Self, Self::Error> {
Pubkey::try_from(v.as_slice())
}
}

impl fmt::Display for Base58 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&bs58::encode(&self.0).into_string())
}
}

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

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AssetSupply {
pub print_max_supply: u32,
pub print_current_supply: u32,
pub edition_nonce: u64,
}

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

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

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

#[derive(Debug, Clone, 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(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AssetCreator {
pub address: Base58,
pub share: u32,
pub verified: bool,
}

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

#[derive(Debug, Clone, 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,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SearchAssetsResult {
pub total: u64,
pub limit: u64,
pub page: u64,
pub items: Vec<Asset>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Content {
#[serde(rename = "$schema")]
pub schema: String,
pub json_uri: String,
pub files: Option<Vec<File>>,
pub metadata: Metadata,
pub links: Option<HashMap<String, Option<String>>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct File {
pub uri: String,
pub mime: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde:: Deserialize)]
pub struct Metadata {
pub attributes: Option<Vec<Attribute>>,
pub description: Option<String>,
pub name: String,
pub symbol: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Attribute {
pub value: serde_json::Value,
pub trait_type: serde_json::Value,
}

#[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", param_kind = map)]
fn search_assets(&self, grouping: Vec<&str>, page: u64) -> Result<SearchAssetsResult, Error>;
}
112 changes: 112 additions & 0 deletions consumer/src/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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 merkle_tree: Pubkey,
pub tree_authority: Pubkey,
pub tree_delegate: Pubkey,
pub leaf_owner: 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