diff --git a/blockbuster/src/programs/mod.rs b/blockbuster/src/programs/mod.rs index 8da2feed0..474bce179 100644 --- a/blockbuster/src/programs/mod.rs +++ b/blockbuster/src/programs/mod.rs @@ -2,12 +2,14 @@ use bubblegum::BubblegumInstruction; use mpl_core_program::MplCoreAccountState; use token_account::TokenProgramAccount; use token_extensions::TokenExtensionsProgramAccount; +use token_inscriptions::TokenInscriptionAccount; use token_metadata::TokenMetadataAccountState; pub mod bubblegum; pub mod mpl_core_program; pub mod token_account; pub mod token_extensions; +pub mod token_inscriptions; pub mod token_metadata; // Note: `ProgramParseResult` used to contain the following variants that have been deprecated and @@ -30,5 +32,6 @@ pub enum ProgramParseResult<'a> { TokenMetadata(&'a TokenMetadataAccountState), TokenProgramAccount(&'a TokenProgramAccount), TokenExtensionsProgramAccount(&'a TokenExtensionsProgramAccount), + TokenInscriptionAccount(&'a TokenInscriptionAccount), Unknown, } diff --git a/blockbuster/src/programs/token_inscriptions/mod.rs b/blockbuster/src/programs/token_inscriptions/mod.rs new file mode 100644 index 000000000..7e40f5e07 --- /dev/null +++ b/blockbuster/src/programs/token_inscriptions/mod.rs @@ -0,0 +1,141 @@ +use serde::{Deserialize, Serialize}; +use solana_sdk::{pubkey::Pubkey, pubkeys}; + +use crate::{ + error::BlockbusterError, + program_handler::{ParseResult, ProgramParser}, +}; + +use super::ProgramParseResult; + +pubkeys!( + inscription_program_id, + "inscokhJarcjaEs59QbQ7hYjrKz25LEPRfCbP8EmdUp" +); + +pub struct TokenInscriptionParser; + +#[derive(Debug, Serialize, Deserialize)] +pub struct InscriptionData { + pub authority: String, + pub root: String, + pub content: String, + pub encoding: String, + pub inscription_data: String, + pub order: u64, + pub size: u32, + pub validation_hash: Option, +} + +impl InscriptionData { + pub const BASE_SIZE: usize = 121; + pub const INSCRIPTION_ACC_DATA_DISC: [u8; 8] = [232, 120, 205, 47, 153, 239, 229, 224]; + + pub fn try_unpack_data(data: &[u8]) -> Result { + let acc_disc = &data[0..8]; + + if acc_disc != Self::INSCRIPTION_ACC_DATA_DISC { + return Err(BlockbusterError::InvalidAccountType); + } + + if data.len() < Self::BASE_SIZE { + return Err(BlockbusterError::CustomDeserializationError( + "Inscription Data is too short".to_string(), + )); + } + + let authority = Pubkey::try_from(&data[8..40]).unwrap(); + let mint = Pubkey::try_from(&data[40..72]).unwrap(); + let inscription_data = Pubkey::try_from(&data[72..104]).unwrap(); + let order = u64::from_le_bytes(data[104..112].try_into().unwrap()); + let size = u32::from_le_bytes(data[112..116].try_into().unwrap()); + let content_type_len = u32::from_le_bytes(data[116..120].try_into().unwrap()) as usize; + let content = String::from_utf8(data[120..120 + content_type_len].to_vec()).unwrap(); + let encoding_len = u32::from_le_bytes( + data[120 + content_type_len..124 + content_type_len] + .try_into() + .unwrap(), + ) as usize; + + let encoding = String::from_utf8( + data[124 + content_type_len..124 + content_type_len + encoding_len].to_vec(), + ) + .unwrap(); + + let validation_exists = u8::from_le_bytes( + data[124 + content_type_len + encoding_len..124 + content_type_len + encoding_len + 1] + .try_into() + .unwrap(), + ); + + let validation_hash = if validation_exists == 1 { + let validation_hash_len = u32::from_le_bytes( + data[124 + content_type_len + encoding_len + 1 + ..128 + content_type_len + encoding_len + 1] + .try_into() + .unwrap(), + ) as usize; + Some( + String::from_utf8( + data[128 + content_type_len + encoding_len + 1 + ..128 + content_type_len + encoding_len + 1 + validation_hash_len] + .to_vec(), + ) + .unwrap(), + ) + } else { + None + }; + Ok(InscriptionData { + authority: authority.to_string(), + root: mint.to_string(), + content, + encoding, + inscription_data: inscription_data.to_string(), + order, + size, + validation_hash, + }) + } +} + +pub struct TokenInscriptionAccount { + pub data: InscriptionData, +} + +impl ParseResult for TokenInscriptionAccount { + fn result(&self) -> &Self + where + Self: Sized, + { + self + } + fn result_type(&self) -> ProgramParseResult { + ProgramParseResult::TokenInscriptionAccount(self) + } +} + +impl ProgramParser for TokenInscriptionParser { + fn key(&self) -> Pubkey { + inscription_program_id() + } + fn key_match(&self, key: &Pubkey) -> bool { + key == &inscription_program_id() + } + + fn handles_account_updates(&self) -> bool { + true + } + + fn handles_instructions(&self) -> bool { + false + } + + fn handle_account( + &self, + account_data: &[u8], + ) -> Result, BlockbusterError> { + let data = InscriptionData::try_unpack_data(account_data)?; + Ok(Box::new(TokenInscriptionAccount { data })) + } +} diff --git a/digital_asset_types/src/dao/extensions/asset.rs b/digital_asset_types/src/dao/extensions/asset.rs index ace692d7e..1d8e7f1d3 100644 --- a/digital_asset_types/src/dao/extensions/asset.rs +++ b/digital_asset_types/src/dao/extensions/asset.rs @@ -4,6 +4,7 @@ use crate::dao::{ asset, asset_authority, asset_creators, asset_data, asset_grouping, asset_v1_account_attachments, sea_orm_active_enums::{OwnerType, RoyaltyTargetType}, + token_accounts, }; #[derive(Copy, Clone, Debug, EnumIter)] @@ -13,6 +14,7 @@ pub enum Relation { AssetAuthority, AssetCreators, AssetGrouping, + TokenAccounts, } impl RelationTrait for Relation { @@ -22,6 +24,10 @@ impl RelationTrait for Relation { .from(asset::Column::AssetData) .to(asset_data::Column::Id) .into(), + Self::TokenAccounts => asset::Entity::belongs_to(token_accounts::Entity) + .from(asset::Column::Id) + .to(token_accounts::Column::Mint) + .into(), Self::AssetV1AccountAttachments => { asset::Entity::has_many(asset_v1_account_attachments::Entity).into() } @@ -62,6 +68,12 @@ impl Related for asset::Entity { } } +impl Related for asset::Entity { + fn to() -> RelationDef { + Relation::TokenAccounts.def() + } +} + impl Default for RoyaltyTargetType { fn default() -> Self { Self::Creators diff --git a/digital_asset_types/src/dao/extensions/mod.rs b/digital_asset_types/src/dao/extensions/mod.rs index bcfc3e130..af3baad66 100644 --- a/digital_asset_types/src/dao/extensions/mod.rs +++ b/digital_asset_types/src/dao/extensions/mod.rs @@ -5,3 +5,4 @@ pub mod asset_data; pub mod asset_grouping; pub mod asset_v1_account_attachment; pub mod instruction; +pub mod token_accounts; diff --git a/digital_asset_types/src/dao/extensions/token_accounts.rs b/digital_asset_types/src/dao/extensions/token_accounts.rs new file mode 100644 index 000000000..e48408f74 --- /dev/null +++ b/digital_asset_types/src/dao/extensions/token_accounts.rs @@ -0,0 +1,25 @@ +use sea_orm::{EntityTrait, EnumIter, Related, RelationDef, RelationTrait}; + +use crate::dao::{asset, token_accounts}; + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Asset, +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Asset => token_accounts::Entity::belongs_to(asset::Entity) + .from(token_accounts::Column::Mint) + .to(asset::Column::Id) + .into(), + } + } +} + +impl Related for token_accounts::Entity { + fn to() -> RelationDef { + Relation::Asset.def() + } +} diff --git a/digital_asset_types/src/dao/full_asset.rs b/digital_asset_types/src/dao/full_asset.rs index 1d901c2ce..41ad23fdc 100644 --- a/digital_asset_types/src/dao/full_asset.rs +++ b/digital_asset_types/src/dao/full_asset.rs @@ -1,12 +1,17 @@ use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; +use super::asset_v1_account_attachments; +use super::tokens; + #[derive(Clone, Debug, PartialEq)] pub struct FullAsset { pub asset: asset::Model, - pub data: Option, + pub data: asset_data::Model, + pub token_info: Option, pub authorities: Vec, pub creators: Vec, pub groups: Vec, + pub inscription: Option, } #[derive(Clone, Debug, PartialEq)] pub struct AssetRelated { diff --git a/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs b/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs index e4d0e012d..cf7470c6f 100644 --- a/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs +++ b/digital_asset_types/src/dao/generated/sea_orm_active_enums.rs @@ -66,6 +66,8 @@ pub enum V1AccountAttachments { MasterEditionV1, #[sea_orm(string_value = "master_edition_v2")] MasterEditionV2, + #[sea_orm(string_value = "token_inscription")] + TokenInscription, #[sea_orm(string_value = "unknown")] Unknown, } diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index b6530e3dc..b9d470e7e 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -5,7 +5,7 @@ use crate::{ cl_audits_v2, extensions::{self, instruction::PascalCase}, sea_orm_active_enums::{Instruction, V1AccountAttachments}, - token_accounts, Cursor, FullAsset, GroupingSize, Pagination, + token_accounts, tokens, Cursor, FullAsset, GroupingSize, Pagination, }, rpc::{ filter::AssetSortDirection, @@ -69,7 +69,7 @@ pub async fn get_by_creator( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let mut condition = Condition::all() .add(asset_creators::Column::Creator.eq(creator.clone())) @@ -85,7 +85,7 @@ pub async fn get_by_creator( sort_direction, pagination, limit, - show_unverified_collections, + options, Some(creator), ) .await @@ -121,13 +121,13 @@ pub async fn get_by_grouping( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let mut condition = asset_grouping::Column::GroupKey .eq(group_key) .and(asset_grouping::Column::GroupValue.eq(group_value)); - if !show_unverified_collections { + if !options.show_unverified_collections { condition = condition.and( asset_grouping::Column::Verified .eq(true) @@ -145,7 +145,7 @@ pub async fn get_by_grouping( sort_direction, pagination, limit, - show_unverified_collections, + options, None, ) .await @@ -158,11 +158,12 @@ pub async fn get_assets_by_owner( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let cond = Condition::all() - .add(asset::Column::Owner.eq(owner)) + .add(asset::Column::Owner.eq(owner.clone())) .add(asset::Column::Supply.gt(0)); + get_assets_by_condition( conn, cond, @@ -171,7 +172,7 @@ pub async fn get_assets_by_owner( sort_direction, pagination, limit, - show_unverified_collections, + options, ) .await } @@ -181,10 +182,12 @@ pub async fn get_assets( asset_ids: Vec>, pagination: &Pagination, limit: u64, + options: &Options, ) -> Result, DbErr> { let cond = Condition::all() .add(asset::Column::Id.is_in(asset_ids)) .add(asset::Column::Supply.gt(0)); + get_assets_by_condition( conn, cond, @@ -194,7 +197,7 @@ pub async fn get_assets( Order::Asc, pagination, limit, - false, + options, ) .await } @@ -206,7 +209,7 @@ pub async fn get_by_authority( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let cond = Condition::all() .add(asset_authority::Column::Authority.eq(authority)) @@ -219,7 +222,7 @@ pub async fn get_by_authority( sort_direction, pagination, limit, - show_unverified_collections, + options, None, ) .await @@ -234,7 +237,7 @@ async fn get_by_related_condition( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, required_creator: Option>, ) -> Result, DbErr> where @@ -253,19 +256,19 @@ where let assets = paginate(pagination, limit, stmt, sort_direction, asset::Column::Id) .all(conn) .await?; - get_related_for_assets(conn, assets, show_unverified_collections, required_creator).await + get_related_for_assets(conn, assets, options, required_creator).await } pub async fn get_related_for_assets( conn: &impl ConnectionTrait, assets: Vec, - show_unverified_collections: bool, + options: &Options, required_creator: Option>, ) -> Result, DbErr> { let asset_ids = assets.iter().map(|a| a.id.clone()).collect::>(); let asset_data: Vec = asset_data::Entity::find() - .filter(asset_data::Column::Id.is_in(asset_ids)) + .filter(asset_data::Column::Id.is_in(asset_ids.clone())) .all(conn) .await?; let asset_data_map = asset_data.into_iter().fold(HashMap::new(), |mut acc, ad| { @@ -283,10 +286,12 @@ pub async fn get_related_for_assets( let id = asset.id.clone(); let fa = FullAsset { asset, - data: Some(ad.clone()), + data: ad.clone(), authorities: vec![], creators: vec![], groups: vec![], + inscription: None, + token_info: None, }; acc.insert(id, fa); }; @@ -296,7 +301,7 @@ pub async fn get_related_for_assets( // Get all creators for all assets in `assets_map``. let creators = asset_creators::Entity::find() - .filter(asset_creators::Column::AssetId.is_in(ids.clone())) + .filter(asset_creators::Column::AssetId.is_in(ids)) .order_by_asc(asset_creators::Column::AssetId) .order_by_asc(asset_creators::Column::Position) .all(conn) @@ -334,7 +339,18 @@ pub async fn get_related_for_assets( } } - let cond = if show_unverified_collections { + if options.show_fungible { + find_tokens(conn, ids.clone()) + .await? + .into_iter() + .for_each(|t| { + if let Some(asset) = assets_map.get_mut(&t.mint.clone()) { + asset.token_info = Some(t); + } + }); + } + + let cond = if options.show_unverified_collections { Condition::all() } else { Condition::any() @@ -351,6 +367,20 @@ pub async fn get_related_for_assets( .order_by_asc(asset_grouping::Column::AssetId) .all(conn) .await?; + + if options.show_inscription { + let attachments = asset_v1_account_attachments::Entity::find() + .filter(asset_v1_account_attachments::Column::AssetId.is_in(asset_ids)) + .all(conn) + .await?; + + for a in attachments.into_iter() { + if let Some(asset) = assets_map.get_mut(&a.id) { + asset.inscription = Some(a); + } + } + } + for g in grouping.into_iter() { if let Some(asset) = assets_map.get_mut(&g.asset_id) { asset.groups.push(g); @@ -369,7 +399,7 @@ pub async fn get_assets_by_condition( sort_direction: Order, pagination: &Pagination, limit: u64, - show_unverified_collections: bool, + options: &Options, ) -> Result, DbErr> { let mut stmt = asset::Entity::find(); for def in joins { @@ -385,8 +415,7 @@ pub async fn get_assets_by_condition( let assets = paginate(pagination, limit, stmt, sort_direction, asset::Column::Id) .all(conn) .await?; - let full_assets = - get_related_for_assets(conn, assets, show_unverified_collections, None).await?; + let full_assets = get_related_for_assets(conn, assets, options, None).await?; Ok(full_assets) } @@ -394,19 +423,35 @@ pub async fn get_by_id( conn: &impl ConnectionTrait, asset_id: Vec, include_no_supply: bool, + options: &Options, ) -> Result { let mut asset_data = asset::Entity::find_by_id(asset_id.clone()).find_also_related(asset_data::Entity); if !include_no_supply { asset_data = asset_data.filter(Condition::all().add(asset::Column::Supply.gt(0))); } - let asset_data: (asset::Model, Option) = + + let inscription = if options.show_inscription { + get_inscription_by_mint(conn, asset_id.clone()).await.ok() + } else { + None + }; + + let token_info = if options.show_fungible { + find_tokens(conn, vec![asset_id.clone()]) + .await? + .into_iter() + .next() + } else { + None + }; + + let (asset, data): (asset::Model, asset_data::Model) = asset_data.one(conn).await.and_then(|o| match o { - Some((a, d)) => Ok((a, d)), + Some((a, Some(d))) => Ok((a, d)), _ => Err(DbErr::RecordNotFound("Asset Not Found".to_string())), })?; - let (asset, data) = asset_data; let authorities: Vec = asset_authority::Entity::find() .filter(asset_authority::Column::AssetId.eq(asset.id.clone())) .order_by_asc(asset_authority::Column::AssetId) @@ -439,6 +484,8 @@ pub async fn get_by_id( authorities, creators, groups: grouping, + inscription, + token_info, }) } @@ -605,11 +652,11 @@ pub async fn get_token_accounts( Ok(token_accounts) } -pub fn get_edition_data_from_json(data: Value) -> Result { +fn get_edition_data_from_json(data: Value) -> Result { serde_json::from_value(data).map_err(|e| DbErr::Custom(e.to_string())) } -pub fn attachment_to_nft_edition( +fn attachment_to_nft_edition( attachment: asset_v1_account_attachments::Model, ) -> Result { let data: Edition = attachment @@ -711,3 +758,35 @@ pub async fn get_nft_editions( cursor, }) } + +async fn get_inscription_by_mint( + conn: &impl ConnectionTrait, + mint: Vec, +) -> Result { + asset_v1_account_attachments::Entity::find() + .filter( + asset_v1_account_attachments::Column::Data + .is_not_null() + .and(Expr::cust(&format!( + "data->>'root' = '{}'", + bs58::encode(mint).into_string() + ))), + ) + .one(conn) + .await + .and_then(|o| match o { + Some(t) => Ok(t), + _ => Err(DbErr::RecordNotFound("Inscription Not Found".to_string())), + }) +} + +async fn find_tokens( + conn: &impl ConnectionTrait, + ids: Vec>, +) -> Result, DbErr> { + tokens::Entity::find() + .filter(tokens::Column::Mint.is_in(ids)) + .all(conn) + .await + .map_err(|_| DbErr::RecordNotFound("Token (s) Not Found".to_string())) +} diff --git a/digital_asset_types/src/dapi/assets_by_authority.rs b/digital_asset_types/src/dapi/assets_by_authority.rs index 59404f3e0..b52891062 100644 --- a/digital_asset_types/src/dapi/assets_by_authority.rs +++ b/digital_asset_types/src/dapi/assets_by_authority.rs @@ -24,7 +24,7 @@ pub async fn get_assets_by_authority( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/assets_by_creator.rs b/digital_asset_types/src/dapi/assets_by_creator.rs index 9ce5de591..3a2b1e53e 100644 --- a/digital_asset_types/src/dapi/assets_by_creator.rs +++ b/digital_asset_types/src/dapi/assets_by_creator.rs @@ -27,7 +27,7 @@ pub async fn get_assets_by_creator( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/assets_by_group.rs b/digital_asset_types/src/dapi/assets_by_group.rs index 68784b9f4..36f4ae534 100644 --- a/digital_asset_types/src/dapi/assets_by_group.rs +++ b/digital_asset_types/src/dapi/assets_by_group.rs @@ -27,7 +27,7 @@ pub async fn get_assets_by_group( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/assets_by_owner.rs b/digital_asset_types/src/dapi/assets_by_owner.rs index c3c4da3a5..5f342fe9a 100644 --- a/digital_asset_types/src/dapi/assets_by_owner.rs +++ b/digital_asset_types/src/dapi/assets_by_owner.rs @@ -24,7 +24,7 @@ pub async fn get_assets_by_owner( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/dapi/common/asset.rs b/digital_asset_types/src/dapi/common/asset.rs index 6caa1f059..f354550f9 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -8,11 +8,14 @@ use crate::rpc::options::Options; use crate::rpc::response::TokenAccountList; use crate::rpc::response::TransactionSignatureList; use crate::rpc::response::{AssetList, DasError}; +use crate::rpc::TokenInfo; +use crate::rpc::TokenInscriptionInfo; use crate::rpc::{ Asset as RpcAsset, Authority, Compression, Content, Creator, File, Group, Interface, MetadataMap, MplCoreInfo, Ownership, Royalty, Scope, Supply, TokenAccount as RpcTokenAccount, Uses, }; +use blockbuster::programs::token_inscriptions::InscriptionData; use jsonpath_lib::JsonPathError; use log::warn; use mime_guess::Mime; @@ -348,36 +351,33 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Some(MplCoreInfo { @@ -388,6 +388,47 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result None, }; + let inscription = if options.show_inscription { + inscription + .and_then(|i| { + i.data.map(|d| -> Result { + let deserialized_data: InscriptionData = + serde_json::from_value(d).map_err(|e| { + DbErr::Custom(format!("Failed to deserialize inscription data: {}", e)) + })?; + Ok(TokenInscriptionInfo { + authority: deserialized_data.authority, + root: deserialized_data.root, + content: deserialized_data.content, + encoding: deserialized_data.encoding, + inscription_data: deserialized_data.inscription_data, + order: deserialized_data.order, + size: deserialized_data.size, + validation_hash: deserialized_data.validation_hash, + }) + }) + }) + .and_then(|i| i.ok()) + } else { + None + }; + + let token_info = if options.show_fungible { + token_info.map(|token_info| TokenInfo { + supply: token_info.supply.try_into().unwrap_or(0), + decimals: token_info.decimals as u8, + mint_authority: token_info + .mint_authority + .map(|s| bs58::encode(s).into_string()), + freeze_authority: token_info + .freeze_authority + .map(|s| bs58::encode(s).into_string()), + token_program: bs58::encode(token_info.token_program).into_string(), + }) + } else { + None + }; + Ok(RpcAsset { interface: interface.clone(), id: bs58::encode(asset.id).into_string(), @@ -446,7 +487,9 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result, options: &Options, ) -> Result { - let asset = scopes::asset::get_by_id(db, id, false).await?; + let asset = scopes::asset::get_by_id(db, id, false, options).await?; asset_to_rpc(asset, options) } @@ -22,7 +22,7 @@ pub async fn get_assets( options: &Options, ) -> Result, DbErr> { let pagination = Pagination::Page { page: 1 }; - let assets = scopes::asset::get_assets(db, ids, &pagination, limit).await?; + let assets = scopes::asset::get_assets(db, ids, &pagination, limit, options).await?; let asset_list = build_asset_response(assets, limit, &pagination, options); let asset_map = asset_list .items diff --git a/digital_asset_types/src/dapi/search_assets.rs b/digital_asset_types/src/dapi/search_assets.rs index a7ee65509..85a0f190d 100644 --- a/digital_asset_types/src/dapi/search_assets.rs +++ b/digital_asset_types/src/dapi/search_assets.rs @@ -23,7 +23,7 @@ pub async fn search_assets( sort_direction, &pagination, page_options.limit, - options.show_unverified_collections, + options, ) .await?; Ok(build_asset_response( diff --git a/digital_asset_types/src/rpc/asset.rs b/digital_asset_types/src/rpc/asset.rs index f8f7aab98..f60c00a13 100644 --- a/digital_asset_types/src/rpc/asset.rs +++ b/digital_asset_types/src/rpc/asset.rs @@ -379,6 +379,29 @@ pub struct MplCoreInfo { pub plugins_json_version: Option, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct TokenInscriptionInfo { + pub authority: String, + pub root: String, + pub inscription_data: String, + pub content: String, + pub encoding: String, + pub order: u64, + pub size: u32, + pub validation_hash: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct TokenInfo { + pub supply: u64, + pub decimals: u8, + pub token_program: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub mint_authority: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub freeze_authority: Option, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] pub struct Asset { pub interface: Interface, @@ -406,6 +429,10 @@ pub struct Asset { #[serde(skip_serializing_if = "Option::is_none")] pub mint_extensions: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub inscription: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub token_info: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub plugins: Option, #[serde(skip_serializing_if = "Option::is_none")] pub unknown_plugins: Option, diff --git a/digital_asset_types/src/rpc/options.rs b/digital_asset_types/src/rpc/options.rs index 219f27c6e..643a896f9 100644 --- a/digital_asset_types/src/rpc/options.rs +++ b/digital_asset_types/src/rpc/options.rs @@ -8,4 +8,8 @@ pub struct Options { pub show_unverified_collections: bool, #[serde(default)] pub show_zero_balance: bool, + #[serde(default)] + pub show_inscription: bool, + #[serde(default)] + pub show_fungible: bool, } diff --git a/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..a4776424f Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..1470bf74a Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..14163db13 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..eee1c6f44 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..2b0af9ec3 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..200463c35 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..607ad146f Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..fdc448537 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..a0da0ece2 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx new file mode 100644 index 000000000..035a8fc7e Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 new file mode 100644 index 000000000..fb22589cd Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 differ diff --git a/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs new file mode 100644 index 000000000..07a90f4b0 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 new file mode 100644 index 000000000..7670328be Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC new file mode 100644 index 000000000..69f99d0d5 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw new file mode 100644 index 000000000..f273bd7c1 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 000000000..de4e24d36 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 000000000..3ffe5ac43 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM new file mode 100644 index 000000000..4922084e3 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo new file mode 100644 index 000000000..824ca37b0 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX new file mode 100644 index 000000000..4e6e98e0e Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn new file mode 100644 index 000000000..4a427f28f Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v new file mode 100644 index 000000000..7443f1f46 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ new file mode 100644 index 000000000..1478546cd Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 new file mode 100644 index 000000000..b6b5149df Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 new file mode 100644 index 000000000..cc574d2ce Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 differ diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP new file mode 100644 index 000000000..ee9399855 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP differ diff --git a/integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 b/integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 new file mode 100644 index 000000000..a05a88603 Binary files /dev/null and b/integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 differ diff --git a/integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 b/integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 new file mode 100644 index 000000000..3a695164b Binary files /dev/null and b/integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 differ diff --git a/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs b/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs index 6115b3103..b320cd479 100644 --- a/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +++ b/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs @@ -1,13 +1,12 @@ +use super::common::*; +use das_api::{ + api::{self, ApiContract}, + error::DasApiError, +}; use function_name::named; - -use das_api::api::{self, ApiContract}; - use itertools::Itertools; - use serial_test::serial; -use super::common::*; - #[tokio::test] #[serial] #[named] @@ -134,7 +133,7 @@ async fn test_fungible_token_get_asset_scenario_1() { #[tokio::test] #[serial] #[named] -async fn test_fungible_token_get_asset_scenario_2() { +async fn test_token_keg_fungible_with_no_metadata() { let name = trim_test_name(function_name!()); let setup = TestSetup::new_with_options( name.clone(), @@ -156,7 +155,7 @@ async fn test_fungible_token_get_asset_scenario_2() { "#; let request: api::GetAsset = serde_json::from_str(request).unwrap(); - let response = setup.das_api.get_asset(request).await.unwrap(); + let response = setup.das_api.get_asset(request).await; - insta::assert_json_snapshot!(name, response); + assert!(matches!(response, Err(DasApiError::DatabaseError(_)))); } diff --git a/integration_tests/tests/integration_tests/main.rs b/integration_tests/tests/integration_tests/main.rs index 62eafb887..950c90ca2 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -6,5 +6,7 @@ mod general_scenario_tests; mod mpl_core_tests; mod nft_editions_tests; mod regular_nft_tests; +mod show_fungible_flag_tests; +mod show_inscription_flag_tests; mod test_show_zero_balance_filter; mod token_accounts_tests; diff --git a/integration_tests/tests/integration_tests/show_fungible_flag_tests.rs b/integration_tests/tests/integration_tests/show_fungible_flag_tests.rs new file mode 100644 index 000000000..3a7626106 --- /dev/null +++ b/integration_tests/tests/integration_tests/show_fungible_flag_tests.rs @@ -0,0 +1,269 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw", + "7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8", + "8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw", + "displayOptions": { + "showFungible": true + } + + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_2() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_3() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "displayOptions": { + "showFungible": true + } + + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_fungible_scenario_4() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", + "8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_by_owner_with_show_fungible() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAssetsByOwner = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets_by_owner(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_by_authority_with_show_fungible() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "authorityAddress": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAssetsByAuthority = serde_json::from_str(request).unwrap(); + let response = setup + .das_api + .get_assets_by_authority(request) + .await + .unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_by_creator_with_show_fungible() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK", + "8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "creatorAddress": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "displayOptions": { + "showFungible": true + } + } + "#; + + let request: api::GetAssetsByCreator = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets_by_creator(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/show_inscription_flag_tests.rs b/integration_tests/tests/integration_tests/show_inscription_flag_tests.rs new file mode 100644 index 000000000..7c5dffdff --- /dev/null +++ b/integration_tests/tests/integration_tests/show_inscription_flag_tests.rs @@ -0,0 +1,46 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_asset_with_show_inscription_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Mainnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP", + "DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "displayOptions": { + "showInscription": true + } + } + "#; + + let request: api::GetAsset = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_asset(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap new file mode 100644 index 000000000..21f2b10c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap @@ -0,0 +1,86 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "total": 1, + "limit": 1000, + "cursor": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "items": [ + { + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap new file mode 100644 index 000000000..21f2b10c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap @@ -0,0 +1,86 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "total": 1, + "limit": 1000, + "cursor": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "items": [ + { + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap new file mode 100644 index 000000000..21f2b10c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap @@ -0,0 +1,86 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "total": 1, + "limit": 1000, + "cursor": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "items": [ + { + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } + } + ] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap new file mode 100644 index 000000000..fc4e0a991 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap @@ -0,0 +1,79 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "ProgrammableNFT", + "id": "Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://nftstorage.link/ipfs/bafkreibmdapcawep5fb77lvcuvoivft3w3wsnc4qworrntjbg6abc5vwti", + "files": [], + "metadata": { + "name": "Claynosaurz #1096", + "symbol": "DINO", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "B7B2g3WbdZMDV3YcDGRGhEt5KyWqDJZFwRR8zpWVEkUF", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "6mszaj17KSfVqADrQj3o4W3zoLMTykgmV37W4QadCczK" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.05, + "basis_points": 500, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "AoebZtN5iKpVyUBc82aouWhugVknLzjUmEEUezxviYNo", + "share": 0, + "verified": true + }, + { + "address": "36tfiBtaDGjAMKd6smPacHQhe4MXycLL6f9ww9CD1naT", + "share": 100, + "verified": false + } + ], + "ownership": { + "frozen": true, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "J1ep1LizHMU3Bf1GKkWePGHU3Qwwzw6FvwW5ySFWdCkn" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "EwSfdqoZPBW6JJN1SBkM2pPgpihDVbByuxKdmiXurxYF", + "freeze_authority": "EwSfdqoZPBW6JJN1SBkM2pPgpihDVbByuxKdmiXurxYF" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap new file mode 100644 index 000000000..9f40a6f5c --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap @@ -0,0 +1,79 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "ProgrammableNFT", + "id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json", + "files": [], + "metadata": { + "name": "Mad Lads #1983", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": true, + "delegated": true, + "delegate": "BPB5idZgbA1DG4XEmnKs62AADRZFf3jY7Kr9mpMGyKPi", + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 1, + "decimals": 0, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip", + "freeze_authority": "FAzbjKo66M3tKhkKqegmWFaYr93FB74B1ChEBdFyKcip" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap new file mode 100644 index 000000000..12b41c50d --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap @@ -0,0 +1,202 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json", + "files": [], + "metadata": { + "name": "PayPal USD", + "symbol": "PYUSD" + }, + "links": {} + }, + "authorities": [], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "mint_extensions": { + "metadata": { + "uri": "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json", + "mint": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "name": "PayPal USD", + "symbol": "PYUSD", + "update_authority": "9nEfZqzTP3dfVWmzQy54TzsZqSQqDFVW4PhXdG9vYCVD", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "metadata_address": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "transfer_fee_config": { + "withheld_amount": 0, + "newer_transfer_fee": { + "epoch": 605, + "maximum_fee": 0, + "transfer_fee_basis_points": 0 + }, + "older_transfer_fee": { + "epoch": 605, + "maximum_fee": 0, + "transfer_fee_basis_points": 0 + }, + "withdraw_withheld_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "transfer_fee_config_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + }, + "confidential_transfer_fee_config": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "withheld_amount": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "harvest_to_mint_enabled": true, + "withdraw_withheld_authority_elgamal_pubkey": [ + 28, + 55, + 230, + 67, + 59, + 115, + 4, + 221, + 130, + 115, + 122, + 228, + 13, + 155, + 139, + 243, + 196, + 159, + 91, + 14, + 108, + 73, + 168, + 213, + 51, + 40, + 179, + 229, + 6, + 144, + 28, + 87 + ] + } + }, + "token_info": { + "supply": 168620476682610, + "decimals": 6, + "token_program": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "mint_authority": "22mKJkKjGEQ3rampp5YKaSsaYZ52BUkcnUN6evXGsXzz", + "freeze_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap new file mode 100644 index 000000000..f4b41d558 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap @@ -0,0 +1,62 @@ +--- +source: integration_tests/tests/integration_tests/show_fungible_flag_tests.rs +expression: response +--- +{ + "interface": "FungibleToken", + "id": "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://storage.googleapis.com/token-metadata/JitoSOL.json", + "files": [], + "metadata": { + "name": "Jito Staked SOL", + "symbol": "JitoSOL", + "token_standard": "Fungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "EDGARWktv3nDxRYjufjdbZmryqGXceaFPoPpbUzdpqED", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": false, + "locked": false + }, + "creators": [], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "token", + "owner": "" + }, + "mutable": true, + "burnt": false, + "token_info": { + "supply": 12585937222711072, + "decimals": 9, + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "mint_authority": "6iQKfEyhr3bZMotVkW6beNZz5CPAkiwvgV2CTje9pVSS" + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap new file mode 100644 index 000000000..62373341d --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap @@ -0,0 +1,78 @@ +--- +source: integration_tests/tests/integration_tests/show_inscription_flag_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "V1_NFT", + "id": "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://arweave.net/qJdjeP8XFfYIG6z5pJ-3RsZR2EcbgILX_ot-b2fEC0g", + "files": [], + "metadata": { + "name": "punk2491", + "symbol": "Symbol", + "token_standard": "NonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "BVk6Bvxa9v6Y32o7KGPhYV4CU9pmG2K7nAYc7mDejsGM", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.08, + "basis_points": 800, + "primary_sale_happened": false, + "locked": false + }, + "creators": [ + { + "address": "BVk6Bvxa9v6Y32o7KGPhYV4CU9pmG2K7nAYc7mDejsGM", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "supply": { + "print_max_supply": 0, + "print_current_supply": 0, + "edition_nonce": 252 + }, + "mutable": true, + "burnt": false, + "inscription": { + "authority": "11111111111111111111111111111111", + "root": "9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ", + "inscription_data": "4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v", + "content": "image/net/riupjyro3lsvkb_listajh0jdsrsjmnyhusxvznycqw", + "encoding": "base64", + "order": 1733, + "size": 202, + "validation_hash": "7fa0041483b92f5a0448067ecef9beca2192b13bfe86fbd53a0024e84fcea652" + } +} diff --git a/migration/src/lib.rs b/migration/src/lib.rs index d2d5c9051..211501cd3 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -44,6 +44,7 @@ mod m20240319_120101_add_mpl_core_enum_vals; mod m20240320_120101_add_mpl_core_info_items; mod m20240520_120101_add_mpl_core_external_plugins_columns; mod m20240718_161232_change_supply_columns_to_numeric; +mod m20241119_060310_add_token_inscription_enum_variant; pub mod model; @@ -97,6 +98,7 @@ impl MigratorTrait for Migrator { Box::new(m20240320_120101_add_mpl_core_info_items::Migration), Box::new(m20240520_120101_add_mpl_core_external_plugins_columns::Migration), Box::new(m20240718_161232_change_supply_columns_to_numeric::Migration), + Box::new(m20241119_060310_add_token_inscription_enum_variant::Migration), ] } } diff --git a/migration/src/m20241119_060310_add_token_inscription_enum_variant.rs b/migration/src/m20241119_060310_add_token_inscription_enum_variant.rs new file mode 100644 index 000000000..1bdd6f8c3 --- /dev/null +++ b/migration/src/m20241119_060310_add_token_inscription_enum_variant.rs @@ -0,0 +1,25 @@ +use sea_orm::{ConnectionTrait, DatabaseBackend, Statement}; +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .get_connection() + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TYPE v1_account_attachments ADD VALUE IF NOT EXISTS 'token_inscription';" + .to_string(), + )) + .await?; + + Ok(()) + } + + async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> { + Ok(()) + } +} diff --git a/program_transformers/src/lib.rs b/program_transformers/src/lib.rs index 32d3aa02f..99a10277b 100644 --- a/program_transformers/src/lib.rs +++ b/program_transformers/src/lib.rs @@ -4,6 +4,7 @@ use { error::{ProgramTransformerError, ProgramTransformerResult}, mpl_core_program::handle_mpl_core_account, token::handle_token_program_account, + token_inscription::handle_token_inscription_program_update, token_metadata::handle_token_metadata_account, }, blockbuster::{ @@ -12,7 +13,8 @@ use { programs::{ bubblegum::BubblegumParser, mpl_core_program::MplCoreParser, token_account::TokenAccountParser, token_extensions::Token2022AccountParser, - token_metadata::TokenMetadataParser, ProgramParseResult, + token_inscriptions::TokenInscriptionParser, token_metadata::TokenMetadataParser, + ProgramParseResult, }, }, futures::future::BoxFuture, @@ -36,6 +38,7 @@ pub mod error; mod mpl_core_program; mod token; mod token_extensions; +mod token_inscription; mod token_metadata; #[derive(Debug, Clone, PartialEq, Eq)] @@ -91,17 +94,19 @@ pub struct ProgramTransformer { impl ProgramTransformer { pub fn new(pool: PgPool, download_metadata_notifier: DownloadMetadataNotifier) -> Self { - let mut parsers: HashMap> = HashMap::with_capacity(5); + let mut parsers: HashMap> = HashMap::with_capacity(6); let bgum = BubblegumParser {}; let token_metadata = TokenMetadataParser {}; let token = TokenAccountParser {}; let mpl_core = MplCoreParser {}; let token_extensions = Token2022AccountParser {}; + let token_inscription = TokenInscriptionParser {}; parsers.insert(bgum.key(), Box::new(bgum)); parsers.insert(token_metadata.key(), Box::new(token_metadata)); parsers.insert(token.key(), Box::new(token)); parsers.insert(mpl_core.key(), Box::new(mpl_core)); parsers.insert(token_extensions.key(), Box::new(token_extensions)); + parsers.insert(token_inscription.key(), Box::new(token_inscription)); let hs = parsers.iter().fold(HashSet::new(), |mut acc, (k, _)| { acc.insert(*k); acc @@ -253,6 +258,14 @@ impl ProgramTransformer { ) .await } + ProgramParseResult::TokenInscriptionAccount(parsing_result) => { + handle_token_inscription_program_update( + account_info, + parsing_result, + &self.storage, + ) + .await + } _ => Err(ProgramTransformerError::NotImplemented), }?; } diff --git a/program_transformers/src/token_inscription/mod.rs b/program_transformers/src/token_inscription/mod.rs new file mode 100644 index 000000000..957275fc8 --- /dev/null +++ b/program_transformers/src/token_inscription/mod.rs @@ -0,0 +1,59 @@ +use std::str::FromStr; + +use crate::AccountInfo; +use blockbuster::programs::token_inscriptions::TokenInscriptionAccount; +use digital_asset_types::dao::asset_v1_account_attachments; +use digital_asset_types::dao::sea_orm_active_enums::V1AccountAttachments; +use sea_orm::sea_query::OnConflict; +use sea_orm::{ + ActiveValue, ConnectionTrait, DatabaseConnection, DbBackend, EntityTrait, QueryTrait, +}; +use solana_sdk::pubkey::Pubkey; + +use crate::error::{ProgramTransformerError, ProgramTransformerResult}; + +pub async fn handle_token_inscription_program_update<'a, 'b>( + account_info: &AccountInfo, + parsing_result: &'a TokenInscriptionAccount, + db: &'b DatabaseConnection, +) -> ProgramTransformerResult<()> { + let account_key = account_info.pubkey.to_bytes().to_vec(); + + let TokenInscriptionAccount { data } = parsing_result; + + let ser = serde_json::to_value(data) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; + + let asset_id = Pubkey::from_str(&data.root) + .map_err(|e| ProgramTransformerError::ParsingError(e.to_string()))? + .to_bytes() + .to_vec(); + + let model = asset_v1_account_attachments::ActiveModel { + id: ActiveValue::Set(account_key), + asset_id: ActiveValue::Set(Some(asset_id)), + data: ActiveValue::Set(Some(ser)), + slot_updated: ActiveValue::Set(account_info.slot as i64), + initialized: ActiveValue::Set(true), + attachment_type: ActiveValue::Set(V1AccountAttachments::TokenInscription), + }; + + let mut query = asset_v1_account_attachments::Entity::insert(model) + .on_conflict( + OnConflict::columns([asset_v1_account_attachments::Column::Id]) + .update_columns([ + asset_v1_account_attachments::Column::Data, + asset_v1_account_attachments::Column::SlotUpdated, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + + query.sql = format!( + "{} WHERE excluded.slot_updated > asset_v1_account_attachments.slot_updated", + query.sql + ); + db.execute(query).await?; + + Ok(()) +} diff --git a/solana-test-validator-geyser-config/accountsdb-plugin-config.json b/solana-test-validator-geyser-config/accountsdb-plugin-config.json index 3925d085a..34a6adce8 100644 --- a/solana-test-validator-geyser-config/accountsdb-plugin-config.json +++ b/solana-test-validator-geyser-config/accountsdb-plugin-config.json @@ -4,19 +4,18 @@ "metrics_port": 8125, "metrics_uri": "graphite", "env": "dev", - "accounts_selector" : { - "owners" : [ + "accounts_selector": { + "owners": [ "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY", - "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d" + "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d", + "inscokhJarcjaEs59QbQ7hYjrKz25LEPRfCbP8EmdUp" ] }, - "transaction_selector" : { - "mentions" : [ - "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY" - ] + "transaction_selector": { + "mentions": ["BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY"] } }