From c2351257e97ec3208ed0bd408726b6fdf61cad02 Mon Sep 17 00:00:00 2001 From: Kyle Espinola Date: Wed, 18 Dec 2024 12:02:45 -0500 Subject: [PATCH 01/10] Respect commitment level set in grpc config (#191) --- grpc-ingest/src/grpc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/grpc-ingest/src/grpc.rs b/grpc-ingest/src/grpc.rs index 480975382..ed6985dfa 100644 --- a/grpc-ingest/src/grpc.rs +++ b/grpc-ingest/src/grpc.rs @@ -140,6 +140,7 @@ impl SubscriptionTask { let request = SubscribeRequest { accounts: req_accounts, transactions: req_transactions, + commitment: Some(config.geyser.commitment.to_proto().into()), ..Default::default() }; From fd9266a71f73221875fe726f1029a171ec25038a Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:23:26 +0530 Subject: [PATCH 02/10] Add getTokenAccount rpc method and implement logic (#213) * feat: add getTokenAccount rpc method and implement logic * add tests for token_accounts indexing * chore: cleanup * chore : add checks for getTokenAccounts and cover more scenarios for tests * show_zero_balance_filter option added (#201) --------- Co-authored-by: Ahzam Akhtar <94620460+AhzamAkhtar@users.noreply.github.com> --- das_api/src/api/api_impl.rs | 35 ++++++- das_api/src/api/mod.rs | 27 ++++- das_api/src/builder.rs | 13 +++ digital_asset_types/src/dao/scopes/asset.rs | 46 ++++++++- digital_asset_types/src/dapi/common/asset.rs | 95 +++++++++++++++++- .../src/dapi/get_token_accounts.rs | 35 +++++++ digital_asset_types/src/dapi/mod.rs | 2 + digital_asset_types/src/rpc/asset.rs | 14 +++ digital_asset_types/src/rpc/options.rs | 2 + digital_asset_types/src/rpc/response.rs | 23 ++++- ...KLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 | Bin 0 -> 312 bytes ...KLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 | Bin 0 -> 312 bytes ...v9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui | Bin 0 -> 312 bytes ...KLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 | Bin 0 -> 312 bytes ...1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS | Bin 0 -> 312 bytes ...qarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a | Bin 0 -> 312 bytes ...1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS | Bin 0 -> 312 bytes ...qarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a | Bin 0 -> 312 bytes .../tests/integration_tests/main.rs | 2 + ...ow_zero_balance_filter_being_disabled.snap | 24 +++++ ...how_zero_balance_filter_being_enabled.snap | 35 +++++++ ...nts_tests__get_token_accounts_by_mint.snap | 24 +++++ ...ts_tests__get_token_accounts_by_owner.snap | 35 +++++++ .../test_show_zero_balance_filter.rs | 83 +++++++++++++++ .../integration_tests/token_accounts_tests.rs | 74 ++++++++++++++ 25 files changed, 556 insertions(+), 13 deletions(-) create mode 100644 digital_asset_types/src/dapi/get_token_accounts.rs create mode 100644 integration_tests/tests/data/accounts/get_token_accounts/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 create mode 100644 integration_tests/tests/data/accounts/get_token_accounts_by_mint/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 create mode 100644 integration_tests/tests/data/accounts/get_token_accounts_by_owner/3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui create mode 100644 integration_tests/tests/data/accounts/get_token_accounts_by_owner/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 create mode 100644 integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS create mode 100644 integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a create mode 100644 integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS create mode 100644 integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap create mode 100644 integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs create mode 100644 integration_tests/tests/integration_tests/token_accounts_tests.rs diff --git a/das_api/src/api/api_impl.rs b/das_api/src/api/api_impl.rs index 4b55f43d8..4f25ed653 100644 --- a/das_api/src/api/api_impl.rs +++ b/das_api/src/api/api_impl.rs @@ -9,13 +9,13 @@ use digital_asset_types::{ dapi::{ get_asset, get_asset_proofs, get_asset_signatures, get_assets, get_assets_by_authority, get_assets_by_creator, get_assets_by_group, get_assets_by_owner, get_proof_for_asset, - search_assets, + get_token_accounts, search_assets, }, rpc::{ filter::{AssetSortBy, SearchConditionType}, - response::GetGroupingResponse, + response::{GetGroupingResponse, TokenAccountList}, + OwnershipModel, RoyaltyModel, }, - rpc::{OwnershipModel, RoyaltyModel}, }; use open_rpc_derive::document_rpc; use sea_orm::{sea_query::ConditionType, ConnectionTrait, DbBackend, Statement}; @@ -516,4 +516,33 @@ impl ApiContract for DasApi { group_size: gs.size, }) } + + async fn get_token_accounts( + self: &DasApi, + payload: GetTokenAccounts, + ) -> Result { + let GetTokenAccounts { + owner_address, + mint_address, + limit, + page, + before, + after, + options, + cursor, + } = payload; + let owner_address = validate_opt_pubkey(&owner_address)?; + let mint_address = validate_opt_pubkey(&mint_address)?; + let options = options.unwrap_or_default(); + let page_options = self.validate_pagination(limit, page, &before, &after, &cursor, None)?; + get_token_accounts( + &self.db_connection, + owner_address, + mint_address, + &page_options, + &options, + ) + .await + .map_err(Into::into) + } } diff --git a/das_api/src/api/mod.rs b/das_api/src/api/mod.rs index 2e1da73ca..ab9da0f9b 100644 --- a/das_api/src/api/mod.rs +++ b/das_api/src/api/mod.rs @@ -2,7 +2,7 @@ use crate::error::DasApiError; use async_trait::async_trait; use digital_asset_types::rpc::filter::{AssetSortDirection, SearchConditionType}; use digital_asset_types::rpc::options::Options; -use digital_asset_types::rpc::response::{AssetList, TransactionSignatureList}; +use digital_asset_types::rpc::response::{AssetList, TokenAccountList, TransactionSignatureList}; use digital_asset_types::rpc::{filter::AssetSorting, response::GetGroupingResponse}; use digital_asset_types::rpc::{Asset, AssetProof, Interface, OwnershipModel, RoyaltyModel}; use open_rpc_derive::{document_rpc, rpc}; @@ -163,6 +163,21 @@ pub struct GetAssetSignatures { pub sort_direction: Option, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct GetTokenAccounts { + pub owner_address: Option, + pub mint_address: Option, + pub limit: Option, + pub page: Option, + pub before: Option, + pub after: Option, + #[serde(default, alias = "displayOptions")] + pub options: Option, + #[serde(default)] + pub cursor: Option, +} + #[document_rpc] #[async_trait] pub trait ApiContract: Send + Sync + 'static { @@ -251,4 +266,14 @@ pub trait ApiContract: Send + Sync + 'static { summary = "Get a list of assets grouped by a specific authority" )] async fn get_grouping(&self, payload: GetGrouping) -> Result; + + #[rpc( + name = "getTokenAccounts", + params = "named", + summary = "Get a list of token accounts by owner or mint" + )] + async fn get_token_accounts( + &self, + payload: GetTokenAccounts, + ) -> Result; } diff --git a/das_api/src/builder.rs b/das_api/src/builder.rs index 25edb0aa2..df5e3bc6c 100644 --- a/das_api/src/builder.rs +++ b/das_api/src/builder.rs @@ -118,6 +118,19 @@ impl RpcApiBuilder { Ok(rpc_context.schema()) })?; + module.register_async_method( + "get_token_accounts", + |rpc_params, rpc_context| async move { + let payload = rpc_params.parse::()?; + rpc_context + .get_token_accounts(payload) + .await + .map_err(Into::into) + }, + )?; + + module.register_alias("getTokenAccounts", "get_token_accounts")?; + Ok(module) } } diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index 58e4cbbaf..42c25adcf 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -4,9 +4,9 @@ use crate::{ asset_authority, asset_creators, asset_data, asset_grouping, cl_audits_v2, extensions::{self, instruction::PascalCase}, sea_orm_active_enums::Instruction, - Cursor, FullAsset, GroupingSize, Pagination, + token_accounts, Cursor, FullAsset, GroupingSize, Pagination, }, - rpc::filter::AssetSortDirection, + rpc::{filter::AssetSortDirection, options::Options}, }; use indexmap::IndexMap; use sea_orm::{entity::*, query::*, ConnectionTrait, DbErr, Order}; @@ -553,3 +553,45 @@ fn filter_out_stale_creators(creators: &mut Vec) { } } } + +pub async fn get_token_accounts( + conn: &impl ConnectionTrait, + owner_address: Option>, + mint_address: Option>, + pagination: &Pagination, + limit: u64, + options: &Options, +) -> Result, DbErr> { + let mut condition = Condition::all(); + + if options.show_zero_balance { + condition = condition.add(token_accounts::Column::Amount.gte(0)); + } else { + condition = condition.add(token_accounts::Column::Amount.gt(0)); + } + + if owner_address.is_none() && mint_address.is_none() { + return Err(DbErr::Custom( + "Either 'owner_address' or 'mint_address' must be provided".to_string(), + )); + } + + if let Some(owner) = owner_address { + condition = condition.add(token_accounts::Column::Owner.eq(owner)); + } + if let Some(mint) = mint_address { + condition = condition.add(token_accounts::Column::Mint.eq(mint)); + } + + let token_accounts = paginate( + pagination, + limit, + token_accounts::Entity::find().filter(condition), + Order::Asc, + token_accounts::Column::Pubkey, + ) + .all(conn) + .await?; + + Ok(token_accounts) +} diff --git a/digital_asset_types/src/dapi/common/asset.rs b/digital_asset_types/src/dapi/common/asset.rs index c9a9dd486..2bafcab3c 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -1,15 +1,18 @@ use crate::dao::sea_orm_active_enums::SpecificationVersions; +use crate::dao::token_accounts; use crate::dao::FullAsset; use crate::dao::PageOptions; use crate::dao::Pagination; use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; use crate::rpc::filter::{AssetSortBy, AssetSortDirection, AssetSorting}; use crate::rpc::options::Options; +use crate::rpc::response::TokenAccountList; use crate::rpc::response::TransactionSignatureList; -use crate::rpc::response::{AssetError, AssetList}; +use crate::rpc::response::{AssetList, DasError}; use crate::rpc::{ Asset as RpcAsset, Authority, Compression, Content, Creator, File, Group, Interface, - MetadataMap, MplCoreInfo, Ownership, Royalty, Scope, Supply, Uses, + MetadataMap, MplCoreInfo, Ownership, Royalty, Scope, Supply, TokenAccount as RpcTokenAccount, + Uses, }; use jsonpath_lib::JsonPathError; use log::warn; @@ -455,14 +458,14 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result, options: &Options, -) -> (Vec, Vec) { +) -> (Vec, Vec) { asset_list .into_iter() .fold((vec![], vec![]), |(mut assets, mut errors), asset| { let id = bs58::encode(asset.asset.id.clone()).into_string(); match asset_to_rpc(asset, options) { Ok(rpc_asset) => assets.push(rpc_asset), - Err(e) => errors.push(AssetError { + Err(e) => errors.push(DasError { id, error: e.to_string(), }), @@ -470,3 +473,87 @@ pub fn asset_list_to_rpc( (assets, errors) }) } + +pub fn token_account_to_rpc( + token_account: token_accounts::Model, + _options: &Options, +) -> Result { + let address = bs58::encode(token_account.pubkey.clone()).into_string(); + let mint = bs58::encode(token_account.mint.clone()).into_string(); + let owner = bs58::encode(token_account.owner.clone()).into_string(); + let delegate = token_account + .delegate + .map(|d| bs58::encode(d).into_string()); + let close_authority = token_account + .close_authority + .map(|d| bs58::encode(d).into_string()); + + Ok(RpcTokenAccount { + address, + mint, + amount: token_account.amount as u64, + owner, + frozen: token_account.frozen, + delegate, + delegated_amount: token_account.delegated_amount as u64, + close_authority, + extensions: None, + }) +} + +pub fn token_account_list_to_rpc( + token_accounts: Vec, + options: &Options, +) -> (Vec, Vec) { + token_accounts.into_iter().fold( + (vec![], vec![]), + |(mut accounts, mut errors), token_account| { + let id = bs58::encode(token_account.pubkey.clone()).into_string(); + match token_account_to_rpc(token_account, options) { + Ok(rpc_token_account) => accounts.push(rpc_token_account), + Err(e) => errors.push(DasError { + id, + error: e.to_string(), + }), + } + (accounts, errors) + }, + ) +} + +pub fn build_token_list_response( + token_accounts: Vec, + limit: u64, + pagination: &Pagination, + options: &Options, +) -> TokenAccountList { + let total = token_accounts.len() as u32; + let (page, before, after, cursor) = match pagination { + Pagination::Keyset { before, after } => { + let bef = before.clone().and_then(|x| String::from_utf8(x).ok()); + let aft = after.clone().and_then(|x| String::from_utf8(x).ok()); + (None, bef, aft, None) + } + Pagination::Page { page } => (Some(*page as u32), None, None, None), + Pagination::Cursor(_) => { + if let Some(last_token_account) = token_accounts.last() { + let cursor_str = bs58::encode(&last_token_account.pubkey.clone()).into_string(); + (None, None, None, Some(cursor_str)) + } else { + (None, None, None, None) + } + } + }; + + let (items, errors) = token_account_list_to_rpc(token_accounts, options); + TokenAccountList { + total, + limit: limit as u32, + page, + before, + after, + token_accounts: items, + cursor, + errors, + } +} diff --git a/digital_asset_types/src/dapi/get_token_accounts.rs b/digital_asset_types/src/dapi/get_token_accounts.rs new file mode 100644 index 000000000..28dcfc503 --- /dev/null +++ b/digital_asset_types/src/dapi/get_token_accounts.rs @@ -0,0 +1,35 @@ +use sea_orm::{DatabaseConnection, DbErr}; + +use crate::{ + dao::PageOptions, + rpc::{options::Options, response::TokenAccountList}, +}; + +use super::common::{build_token_list_response, create_pagination}; + +pub async fn get_token_accounts( + db: &DatabaseConnection, + owner_address: Option>, + mint_address: Option>, + page_options: &PageOptions, + options: &Options, +) -> Result { + let pagination = create_pagination(page_options)?; + + let token_accounts = crate::dao::scopes::asset::get_token_accounts( + db, + owner_address, + mint_address, + &pagination, + page_options.limit, + options, + ) + .await?; + + Ok(build_token_list_response( + token_accounts, + page_options.limit, + &pagination, + options, + )) +} diff --git a/digital_asset_types/src/dapi/mod.rs b/digital_asset_types/src/dapi/mod.rs index e9481169a..488a474bd 100644 --- a/digital_asset_types/src/dapi/mod.rs +++ b/digital_asset_types/src/dapi/mod.rs @@ -5,6 +5,7 @@ mod assets_by_owner; mod change_logs; mod get_asset; mod get_asset_signatures; +mod get_token_accounts; mod search_assets; pub mod common; @@ -16,4 +17,5 @@ pub use assets_by_owner::*; pub use change_logs::*; pub use get_asset::*; pub use get_asset_signatures::*; +pub use get_token_accounts::*; pub use search_assets::*; diff --git a/digital_asset_types/src/rpc/asset.rs b/digital_asset_types/src/rpc/asset.rs index 751ef3841..8b5ccbbae 100644 --- a/digital_asset_types/src/rpc/asset.rs +++ b/digital_asset_types/src/rpc/asset.rs @@ -399,3 +399,17 @@ pub struct Asset { #[serde(skip_serializing_if = "Option::is_none")] pub unknown_external_plugins: Option, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] +pub struct TokenAccount { + pub address: String, + pub mint: String, + pub amount: u64, + pub owner: String, + pub frozen: bool, + pub delegate: Option, + pub delegated_amount: u64, + pub close_authority: Option, + pub extensions: Option, +} diff --git a/digital_asset_types/src/rpc/options.rs b/digital_asset_types/src/rpc/options.rs index a1fafa2c4..219f27c6e 100644 --- a/digital_asset_types/src/rpc/options.rs +++ b/digital_asset_types/src/rpc/options.rs @@ -6,4 +6,6 @@ use serde::{Deserialize, Serialize}; pub struct Options { #[serde(default)] pub show_unverified_collections: bool, + #[serde(default)] + pub show_zero_balance: bool, } diff --git a/digital_asset_types/src/rpc/response.rs b/digital_asset_types/src/rpc/response.rs index 53076c8b2..55277e5d4 100644 --- a/digital_asset_types/src/rpc/response.rs +++ b/digital_asset_types/src/rpc/response.rs @@ -1,12 +1,13 @@ use schemars::JsonSchema; + use { - crate::rpc::Asset, + crate::rpc::{Asset, TokenAccount}, serde::{Deserialize, Serialize}, }; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] #[serde(default)] -pub struct AssetError { +pub struct DasError { pub id: String, pub error: String, } @@ -34,7 +35,7 @@ pub struct AssetList { pub cursor: Option, pub items: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] - pub errors: Vec, + pub errors: Vec, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] @@ -50,3 +51,19 @@ pub struct TransactionSignatureList { pub after: Option, pub items: Vec<(String, String)>, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] +pub struct TokenAccountList { + pub total: u32, + pub limit: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub before: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub after: Option, + pub token_accounts: Vec, + pub cursor: Option, + pub errors: Vec, +} diff --git a/integration_tests/tests/data/accounts/get_token_accounts/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 b/integration_tests/tests/data/accounts/get_token_accounts/jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2 new file mode 100644 index 0000000000000000000000000000000000000000..b4c5bef734a3ca6655c3c0069b96c64fa3c79f6f GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ z7MpUeDPYpQf6Q!mzdgL3x^VK%(+}Owy{=p%v!(TIe6;yQtDdi|b^jSwY5>jnAS(|f z85sUUftier2#Bw^$w+=OBZ$SouoQ@SU(NrM^yBh`sA}UZKlK%jjHhi%8$4Xr2HwqA z_*wjM-&*w(JRgs?dw+j4_0-&qHpkeH1-$R2W<4snqL$^?GIK72z|t!(z@`x z7MpUeDPYpQf6Q!mzdgL3x^VK%(+}Owy{=p%v!(TIe6;yQtDdi|b^jSwY5>jnAS(|f z85sUUL4f{55fGoLYQgNuj35>R!%`sTeKr42(vQm%qNa14Q literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_token_accounts_by_owner/3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui b/integration_tests/tests/data/accounts/get_token_accounts_by_owner/3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui new file mode 100644 index 0000000000000000000000000000000000000000..20e262776be00e5c67e099301e64a96713520101 GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQRGwzrvLn~#=ziXLQ`YuqM%^)ae{$25 zMbC9Jl-|5|zBQHY?ze~6Qx{IYdHSK-x!09zWVW=vjgK~;Xw~zzweCN|N)4bHA7tf$ zBm={LCol-k9^mvpWyj;wB7srqp7FnX0$oRek|a9FE#5?!4 z7MpUeDPYpQf6Q!mzdgL3x^VK%(+}Owy{=p%v!(TIe6;yQtDdi|b^jSwY5>jnAS(|f z85sUUL9qTr5fFcO?Sk2p89^)thNVEv`)dB5q#u_jL{%GS`KhmHWISzC+Th``Ht=q~ z!q4K5`_`(T;Q4s8-TV8asi)>WpV^SGg#(J*?Z;-e2|p~ zk_-(0q2P#rr4We!urZi<3L}Wcz_1jESt@THKO-<+Rs>>VMDtPRO)<-ttkbhqEK`#X<>fut7u;P-}@)jj9X) D3t(3f literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_disabled/CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a new file mode 100644 index 0000000000000000000000000000000000000000..0f73124a761446f143b0b8cf408352fafd5c6d1a GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ+{D0q|DV{w2|K@9p83*vO55k@nI*S= zu=z1fXxh9f_kJzg-ER-Cr!Jg)^YlZvbFVAc$ZTnS8y{^x(W>WbYu$f_l^Q@ZKFG=g zNd|`hP;kV*QV7JiZwh9f!U$q9Ff0Y)L>Y@8N6HOSwpfci5O%vCK{LOPyIqmhJ`rk9Z6Eba|w|o@q;p|9!u~0%AY7_&A-jdk8B1UBY peX%2JoPWN5m_D^rCf_4!jU;2^?==TrC~jf`SqcL%jW9j|8UW0VXj=dP literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS b/integration_tests/tests/data/accounts/show_zero_balance_filter_being_enabled/BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS new file mode 100644 index 0000000000000000000000000000000000000000..a25676b25cdbdef3c22f2337e7405c7fab6a5066 GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQoc?l|>WpV^SGg#(J*?Z;-e2|p~ zk_-(0q2QQ*r4Wdp+!o9{g%QMJU|0&oER{EppAj&)hJ+qwmkt3)`{q z_tr(PjMb&inWbYu$f_l^Q@ZKFG=g zNd|`hP;kt@QV7I9(iY4-g%QMJU|0&oi82;Hj+7guY`G>{a4A{!$om-iHje)*d0u|| z^l{;t)xV|AnvJ?hj8ex0_GypJ=XyyO_ literal 0 HcmV?d00001 diff --git a/integration_tests/tests/integration_tests/main.rs b/integration_tests/tests/integration_tests/main.rs index 7220ca96c..7f88a27a8 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -4,3 +4,5 @@ mod common; mod general_scenario_tests; mod mpl_core_tests; mod regular_nft_tests; +mod test_show_zero_balance_filter; +mod token_accounts_tests; diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap new file mode 100644 index 000000000..f2a5d1a10 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_disabled.snap @@ -0,0 +1,24 @@ +--- +source: integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs +expression: response +snapshot_kind: text +--- +{ + "total": 1, + "limit": 1000, + "token_accounts": [ + { + "address": "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + "mint": "7Y5WQ2e3FummR2DebrqP8caC64QvXzpnhSTNWjNabxWn", + "amount": 1, + "owner": "2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "frozen": true, + "delegate": "D98f1ebFe6kfZTcztLo1iPeKAwogbWHAgXzgSpdRDiu7", + "delegated_amount": 1, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap new file mode 100644 index 000000000..c628cdebb --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_show_zero_balance_filter__show_zero_balance_filter_being_enabled.snap @@ -0,0 +1,35 @@ +--- +source: integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs +expression: response +snapshot_kind: text +--- +{ + "total": 2, + "limit": 1000, + "token_accounts": [ + { + "address": "BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS", + "mint": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", + "amount": 0, + "owner": "2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + }, + { + "address": "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + "mint": "7Y5WQ2e3FummR2DebrqP8caC64QvXzpnhSTNWjNabxWn", + "amount": 1, + "owner": "2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "frozen": true, + "delegate": "D98f1ebFe6kfZTcztLo1iPeKAwogbWHAgXzgSpdRDiu7", + "delegated_amount": 1, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap new file mode 100644 index 000000000..461483ad2 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_mint.snap @@ -0,0 +1,24 @@ +--- +source: integration_tests/tests/integration_tests/token_accounts_tests.rs +expression: response +snapshot_kind: text +--- +{ + "total": 1, + "limit": 1000, + "token_accounts": [ + { + "address": "jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2", + "mint": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1", + "amount": 1000000000000, + "owner": "CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap new file mode 100644 index 000000000..e99e05b5e --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_accounts_tests__get_token_accounts_by_owner.snap @@ -0,0 +1,35 @@ +--- +source: integration_tests/tests/integration_tests/token_accounts_tests.rs +expression: response +snapshot_kind: text +--- +{ + "total": 2, + "limit": 1000, + "token_accounts": [ + { + "address": "jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2", + "mint": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1", + "amount": 1000000000000, + "owner": "CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + }, + { + "address": "3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui", + "mint": "F3D8Priw3BRecH36BuMubQHrTUn1QxmupLHEmmbZ4LXW", + "amount": 10000, + "owner": "CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx", + "frozen": false, + "delegate": null, + "delegated_amount": 0, + "close_authority": null, + "extensions": null + } + ], + "cursor": null, + "errors": [] +} diff --git a/integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs b/integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs new file mode 100644 index 000000000..e19f18d71 --- /dev/null +++ b/integration_tests/tests/integration_tests/test_show_zero_balance_filter.rs @@ -0,0 +1,83 @@ +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_show_zero_balance_filter_being_enabled() { + 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([ + "BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS", + "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress":"2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "displayOptions": { + "showZeroBalance": true + } + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_show_zero_balance_filter_being_disabled() { + 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([ + "BE1CkzRjLTXAWcSVCaqzycwXsZ18Yuk3jMDMnPUoHjjS", + "CyqarC6hyNYvb3EDueyeYrnGeAUjCDtMvWrbtdAnA53a", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress":"2oerfxddTpK5hWAmCMYB6fr9WvNrjEH54CHCWK8sAq7g", + "displayOptions": { + "showZeroBalance": false + } + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/token_accounts_tests.rs b/integration_tests/tests/integration_tests/token_accounts_tests.rs new file mode 100644 index 000000000..8a465e914 --- /dev/null +++ b/integration_tests/tests/integration_tests/token_accounts_tests.rs @@ -0,0 +1,74 @@ +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_token_accounts_by_mint() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "mintAddress":"wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1" + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} + +#[tokio::test] +#[serial] +#[named] +async fn test_get_token_accounts_by_owner() { + let name = trim_test_name(function_name!()); + + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts([ + "jKLTJu7nE1zLmC2J2xjVVBm4G7vJcKGCGQX36Jrsba2", + "3Pv9H5UzU8T9BwgutXrcn2wLohS1JUZuk3x8paiRyzui", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ownerAddress":"CeviT1DTQLuicEB7yLeFkkAGmam5GnJssbGb7CML4Tgx" + } + "#; + + let request: api::GetTokenAccounts = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_token_accounts(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} From d914e4baa931631c9e78f9486977e65dd7ec8daa Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:02:14 +0530 Subject: [PATCH 03/10] Index Token Extensions and Fungible Tokens (#214) * Token extensions Indexing support * fix: register token extensions parser with program transform. add integration test for the token extensions. * WIP : token extensions * chore: cleanup * fix: token extensions integration tests * change is_non_fungible condition checks * chore: cleanup * resolve comments * fix tests and snapshots * add more scenarios * resolve comments and fix fixtures * chore : resolve comments --------- Co-authored-by: niks3089 Co-authored-by: Kyle Espinola --- Cargo.lock | 25 +- .../src/programs/token_extensions/mod.rs | 29 ++ .../src/dao/extensions/asset.rs | 3 +- digital_asset_types/src/dao/full_asset.rs | 2 +- .../src/dao/generated/asset.rs | 3 + .../src/dao/generated/token_accounts.rs | 3 + .../src/dao/generated/tokens.rs | 13 + digital_asset_types/src/dao/scopes/asset.rs | 6 +- digital_asset_types/src/dapi/common/asset.rs | 70 ++--- digital_asset_types/src/rpc/asset.rs | 45 ++- digital_asset_types/tests/common.rs | 1 + ...38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq | Bin 0 -> 824 bytes ...jFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | Bin 0 -> 224 bytes ...38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq | Bin 0 -> 824 bytes ...jFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | Bin 0 -> 224 bytes ...KocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 | Bin 0 -> 224 bytes ...U5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y | Bin 0 -> 496 bytes ...U5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y | Bin 0 -> 496 bytes ...bpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM | Bin 0 -> 760 bytes ...U5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y | Bin 0 -> 496 bytes ...bpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM | Bin 0 -> 760 bytes ...1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo | Bin 0 -> 1008 bytes .../fungibles_and_token_extensions_tests.rs | 162 ++++++++++ .../tests/integration_tests/main.rs | 1 + ...sts__account_updates-metadata-updated.snap | 2 +- ..._tests__account_updates-token-updated.snap | 2 +- ...sts__account_updates-with-all-updates.snap | 2 +- ...account_update_tests__account_updates.snap | 2 +- ...__fungible_token_get_asset_scenario_1.snap | 56 ++++ ...__fungible_token_get_asset_scenario_2.snap | 39 +++ ...token_extensions_get_asset_scenario_1.snap | 63 ++++ ...token_extensions_get_asset_scenario_2.snap | 78 +++++ ...token_extensions_get_asset_scenario_3.snap | 197 ++++++++++++ ...general_scenario_tests__asset_parsing.snap | 2 +- ...l_scenario_tests__creators_reordering.snap | 2 +- ...core_tests__mpl_core_autograph_plugin.snap | 3 +- ..._core_autograph_plugin_with_signature.snap | 3 +- ...s__mpl_core_tests__mpl_core_get_asset.snap | 3 +- ...nary_data_and_owner_is_data_authority.snap | 3 +- ...nd_update_authority_is_data_authority.snap | 3 +- ...ck_data_and_address_is_data_authority.snap | 3 +- ...et_with_data_section_with_binary_data.snap | 3 +- ...sset_with_data_section_with_json_data.snap | 3 +- ..._with_data_section_with_msg_pack_data.snap | 3 +- ...ests__mpl_core_get_asset_with_edition.snap | 3 +- ...ultiple_internal_and_external_plugins.snap | 3 +- ...custom_offset_and_base_address_config.snap | 3 +- ..._oracle_external_plugin_on_collection.snap | 3 +- ...with_oracle_multiple_lifecycle_events.snap | 3 +- ..._core_get_asset_with_oracle_no_offset.snap | 3 +- ...ore_get_asset_with_pubkey_in_rule_set.snap | 3 +- ...sset_with_two_oracle_external_plugins.snap | 3 +- ...sts__mpl_core_get_assets_by_authority.snap | 4 +- ...e_tests__mpl_core_get_assets_by_group.snap | 5 +- ..._with_oracle_and_custom_pda_all_seeds.snap | 3 +- ...e_tests__mpl_core_get_assets_by_owner.snap | 3 +- ...l_core_tests__mpl_core_get_collection.snap | 3 +- ...ry_data_and_address_is_data_authority.snap | 3 +- ...json_data_and_owner_is_data_authority.snap | 3 +- ...nd_update_authority_is_data_authority.snap | 3 +- ...ts__mpl_core_verified_creators_plugin.snap | 3 +- ...rified_creators_plugin_with_signature.snap | 3 +- ...sts__regular_nft_tests__reg_get_asset.snap | 2 +- ...reg_get_asset_batch-2-and-a-missing-1.snap | 3 +- ...eg_get_asset_batch-only-2-different-2.snap | 3 +- ...nft_tests__reg_get_asset_batch-only-2.snap | 3 +- ...lar_nft_tests__reg_get_asset_by_group.snap | 2 +- ..._regular_nft_tests__reg_search_assets.snap | 1 - ..._token_extensions_get_asset_scenario1.snap | 53 ++++ ..._token_extensions_get_asset_scenario2.snap | 68 ++++ migration/src/lib.rs | 2 + .../m20240219_115532_add_extensions_column.rs | 60 ++++ program_transformers/Cargo.toml | 1 + program_transformers/src/asset_upserts.rs | 21 +- program_transformers/src/lib.rs | 46 ++- .../src/mpl_core_program/v1_asset.rs | 4 +- program_transformers/src/token/mod.rs | 94 +++--- .../src/token_extensions/mod.rs | 294 ++++++++++++++++++ .../src/token_metadata/v1_asset.rs | 25 +- 79 files changed, 1364 insertions(+), 210 deletions(-) create mode 100644 integration_tests/tests/data/accounts/fungible_token_get_asset/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq create mode 100644 integration_tests/tests/data/accounts/fungible_token_get_asset/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v create mode 100644 integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq create mode 100644 integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v create mode 100644 integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_2/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 create mode 100644 integration_tests/tests/data/accounts/token_extensions_get_asset/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y create mode 100644 integration_tests/tests/data/accounts/token_extensions_get_asset_scenario1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y create mode 100644 integration_tests/tests/data/accounts/token_extensions_get_asset_scenario2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM create mode 100644 integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_1/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y create mode 100644 integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM create mode 100644 integration_tests/tests/data/accounts/token_extensions_get_asset_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo create mode 100644 integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap create mode 100644 migration/src/m20240219_115532_add_extensions_column.rs create mode 100644 program_transformers/src/token_extensions/mod.rs diff --git a/Cargo.lock b/Cargo.lock index fc7779580..34458c8ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1233,9 +1233,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" [[package]] name = "bv" @@ -1598,9 +1598,9 @@ checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -2070,9 +2070,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" dependencies = [ "serde", ] @@ -2909,9 +2909,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -3753,9 +3753,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -4341,6 +4341,7 @@ dependencies = [ "solana-transaction-status", "spl-account-compression", "spl-token", + "spl-token-2022", "sqlx", "thiserror", "tokio", @@ -7510,9 +7511,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" diff --git a/blockbuster/src/programs/token_extensions/mod.rs b/blockbuster/src/programs/token_extensions/mod.rs index ec1633c9c..d890e5dbc 100644 --- a/blockbuster/src/programs/token_extensions/mod.rs +++ b/blockbuster/src/programs/token_extensions/mod.rs @@ -56,6 +56,26 @@ pub struct MintAccountExtensions { pub token_group_member: Option, } +impl MintAccountExtensions { + pub fn is_some(&self) -> bool { + self.default_account_state.is_some() + || self.confidential_transfer_mint.is_some() + || self.confidential_transfer_account.is_some() + || self.confidential_transfer_fee_config.is_some() + || self.interest_bearing_config.is_some() + || self.transfer_fee_config.is_some() + || self.mint_close_authority.is_some() + || self.permanent_delegate.is_some() + || self.metadata_pointer.is_some() + || self.metadata.is_some() + || self.transfer_hook.is_some() + || self.group_pointer.is_some() + || self.token_group.is_some() + || self.group_member_pointer.is_some() + || self.token_group_member.is_some() + } +} + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct TokenAccountExtensions { pub confidential_transfer: Option, @@ -63,6 +83,15 @@ pub struct TokenAccountExtensions { pub memo_transfer: Option, pub transfer_fee_amount: Option, } + +impl TokenAccountExtensions { + pub fn is_some(&self) -> bool { + self.confidential_transfer.is_some() + || self.cpi_guard.is_some() + || self.memo_transfer.is_some() + || self.transfer_fee_amount.is_some() + } +} #[derive(Debug, PartialEq)] pub struct TokenAccount { pub account: Account, diff --git a/digital_asset_types/src/dao/extensions/asset.rs b/digital_asset_types/src/dao/extensions/asset.rs index 18525e39e..ace692d7e 100644 --- a/digital_asset_types/src/dao/extensions/asset.rs +++ b/digital_asset_types/src/dao/extensions/asset.rs @@ -76,7 +76,7 @@ impl Default for asset::Model { specification_version: None, specification_asset_class: None, owner: None, - owner_type: OwnerType::Single, + owner_type: OwnerType::Unknown, delegate: None, frozen: Default::default(), supply: Default::default(), @@ -103,6 +103,7 @@ impl Default for asset::Model { owner_delegate_seq: None, leaf_seq: None, base_info_seq: None, + mint_extensions: None, mpl_core_plugins: None, mpl_core_unknown_plugins: None, mpl_core_collection_current_size: None, diff --git a/digital_asset_types/src/dao/full_asset.rs b/digital_asset_types/src/dao/full_asset.rs index fda932d21..1d901c2ce 100644 --- a/digital_asset_types/src/dao/full_asset.rs +++ b/digital_asset_types/src/dao/full_asset.rs @@ -3,7 +3,7 @@ use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_group #[derive(Clone, Debug, PartialEq)] pub struct FullAsset { pub asset: asset::Model, - pub data: asset_data::Model, + pub data: Option, pub authorities: Vec, pub creators: Vec, pub groups: Vec, diff --git a/digital_asset_types/src/dao/generated/asset.rs b/digital_asset_types/src/dao/generated/asset.rs index f70e0b383..f2f74d6a9 100644 --- a/digital_asset_types/src/dao/generated/asset.rs +++ b/digital_asset_types/src/dao/generated/asset.rs @@ -50,6 +50,7 @@ pub struct Model { pub owner_delegate_seq: Option, pub leaf_seq: Option, pub base_info_seq: Option, + pub mint_extensions: Option, pub mpl_core_plugins: Option, pub mpl_core_unknown_plugins: Option, pub mpl_core_collection_num_minted: Option, @@ -93,6 +94,7 @@ pub enum Column { OwnerDelegateSeq, LeafSeq, BaseInfoSeq, + MintExtensions, MplCorePlugins, MplCoreUnknownPlugins, MplCoreCollectionNumMinted, @@ -153,6 +155,7 @@ impl ColumnTrait for Column { Self::OwnerDelegateSeq => ColumnType::BigInteger.def().null(), Self::LeafSeq => ColumnType::BigInteger.def().null(), Self::BaseInfoSeq => ColumnType::BigInteger.def().null(), + Self::MintExtensions => ColumnType::JsonBinary.def().null(), Self::MplCorePlugins => ColumnType::JsonBinary.def().null(), Self::MplCoreUnknownPlugins => ColumnType::JsonBinary.def().null(), Self::MplCoreCollectionNumMinted => ColumnType::Integer.def().null(), diff --git a/digital_asset_types/src/dao/generated/token_accounts.rs b/digital_asset_types/src/dao/generated/token_accounts.rs index 380e29b42..d758403b8 100644 --- a/digital_asset_types/src/dao/generated/token_accounts.rs +++ b/digital_asset_types/src/dao/generated/token_accounts.rs @@ -24,6 +24,7 @@ pub struct Model { pub delegated_amount: i64, pub slot_updated: i64, pub token_program: Vec, + pub extensions: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] @@ -38,6 +39,7 @@ pub enum Column { DelegatedAmount, SlotUpdated, TokenProgram, + Extensions, } #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] @@ -69,6 +71,7 @@ impl ColumnTrait for Column { Self::DelegatedAmount => ColumnType::BigInteger.def(), Self::SlotUpdated => ColumnType::BigInteger.def(), Self::TokenProgram => ColumnType::Binary.def(), + Self::Extensions => ColumnType::Json.def().null(), } } } diff --git a/digital_asset_types/src/dao/generated/tokens.rs b/digital_asset_types/src/dao/generated/tokens.rs index 326b8d968..ed1e5598d 100644 --- a/digital_asset_types/src/dao/generated/tokens.rs +++ b/digital_asset_types/src/dao/generated/tokens.rs @@ -23,6 +23,7 @@ pub struct Model { pub close_authority: Option>, pub extension_data: Option>, pub slot_updated: i64, + pub extensions: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] @@ -36,6 +37,7 @@ pub enum Column { CloseAuthority, ExtensionData, SlotUpdated, + Extensions, } #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] @@ -66,6 +68,7 @@ impl ColumnTrait for Column { Self::CloseAuthority => ColumnType::Binary.def().null(), Self::ExtensionData => ColumnType::Binary.def().null(), Self::SlotUpdated => ColumnType::BigInteger.def(), + Self::Extensions => ColumnType::JsonBinary.def().null(), } } } @@ -77,3 +80,13 @@ impl RelationTrait for Relation { } impl ActiveModelBehavior for ActiveModel {} + +pub trait IsNonFungible { + fn is_non_fungible(&self) -> bool; +} + +impl IsNonFungible for Model { + fn is_non_fungible(&self) -> bool { + self.decimals == 0 && self.supply == 1.into() + } +} diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index 42c25adcf..b3c4e0e63 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -274,7 +274,7 @@ pub async fn get_related_for_assets( let id = asset.id.clone(); let fa = FullAsset { asset, - data: ad.clone(), + data: Some(ad.clone()), authorities: vec![], creators: vec![], groups: vec![], @@ -391,9 +391,9 @@ pub async fn get_by_id( if !include_no_supply { asset_data = asset_data.filter(Condition::all().add(asset::Column::Supply.gt(0))); } - let asset_data: (asset::Model, asset_data::Model) = + let asset_data: (asset::Model, Option) = asset_data.one(conn).await.and_then(|o| match o { - Some((a, Some(d))) => Ok((a, d)), + Some((a, d)) => Ok((a, d)), _ => Err(DbErr::RecordNotFound("Asset Not Found".to_string())), })?; diff --git a/digital_asset_types/src/dapi/common/asset.rs b/digital_asset_types/src/dapi/common/asset.rs index 2bafcab3c..6caa1f059 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -1,4 +1,3 @@ -use crate::dao::sea_orm_active_enums::SpecificationVersions; use crate::dao::token_accounts; use crate::dao::FullAsset; use crate::dao::PageOptions; @@ -281,14 +280,8 @@ pub fn v1_content_from_json(asset_data: &asset_data::Model) -> Result Result { - match asset.specification_version { - Some(SpecificationVersions::V1) | Some(SpecificationVersions::V0) => { - v1_content_from_json(data) - } - Some(_) => Err(DbErr::Custom("Version Not Implemented".to_string())), - None => Err(DbErr::Custom("Specification version not found".to_string())), - } +pub fn get_content(data: &asset_data::Model) -> Option { + v1_content_from_json(data).ok() } pub fn to_authority(authority: Vec) -> Vec { @@ -337,10 +330,7 @@ pub fn to_grouping( pub fn get_interface(asset: &asset::Model) -> Result { Ok(Interface::from(( - asset - .specification_version - .as_ref() - .ok_or(DbErr::Custom("Specification version not found".to_string()))?, + asset.specification_version.as_ref(), asset .specification_asset_class .as_ref() @@ -363,14 +353,32 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Some(MplCoreInfo { num_minted: asset.mpl_core_collection_num_minted, @@ -383,9 +391,9 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Result Result Some(Supply { edition_nonce, @@ -436,17 +444,9 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result None, }, - uses: data.chain_data.get("uses").map(|u| Uses { - use_method: u - .get("use_method") - .and_then(|s| s.as_str()) - .unwrap_or("Single") - .to_string() - .into(), - total: u.get("total").and_then(|t| t.as_u64()).unwrap_or(0), - remaining: u.get("remaining").and_then(|t| t.as_u64()).unwrap_or(0), - }), + uses, burnt: asset.burnt, + mint_extensions: asset.mint_extensions, plugins: asset.mpl_core_plugins, unknown_plugins: asset.mpl_core_unknown_plugins, mpl_core_info, diff --git a/digital_asset_types/src/rpc/asset.rs b/digital_asset_types/src/rpc/asset.rs index 8b5ccbbae..f8f7aab98 100644 --- a/digital_asset_types/src/rpc/asset.rs +++ b/digital_asset_types/src/rpc/asset.rs @@ -21,22 +21,22 @@ pub struct AssetProof { pub tree_id: String, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema, Default)] pub enum Interface { #[serde(rename = "V1_NFT")] V1NFT, #[serde(rename = "V1_PRINT")] V1PRINT, - #[serde(rename = "LEGACY_NFT")] + #[serde(rename = "V2_NFT")] + Nft, // TODO: change on version bump + #[serde(rename = "LEGACY_NFT")] #[allow(non_camel_case_types)] LEGACY_NFT, - #[serde(rename = "V2_NFT")] - Nft, #[serde(rename = "FungibleAsset")] FungibleAsset, - #[serde(rename = "Custom")] - Custom, + #[serde(rename = "FungibleToken")] + FungibleToken, #[serde(rename = "Identity")] Identity, #[serde(rename = "Executable")] @@ -47,19 +47,28 @@ pub enum Interface { MplCoreAsset, #[serde(rename = "MplCoreCollection")] MplCoreCollection, + #[default] + #[serde(rename = "Custom")] + Custom, } -impl From<(&SpecificationVersions, &SpecificationAssetClass)> for Interface { - fn from(i: (&SpecificationVersions, &SpecificationAssetClass)) -> Self { +impl From<(Option<&SpecificationVersions>, &SpecificationAssetClass)> for Interface { + fn from(i: (Option<&SpecificationVersions>, &SpecificationAssetClass)) -> Self { match i { - (SpecificationVersions::V1, SpecificationAssetClass::Nft) => Interface::V1NFT, - (SpecificationVersions::V1, SpecificationAssetClass::PrintableNft) => Interface::V1NFT, - (SpecificationVersions::V0, SpecificationAssetClass::Nft) => Interface::LEGACY_NFT, - (SpecificationVersions::V1, SpecificationAssetClass::ProgrammableNft) => { + (Some(SpecificationVersions::V1), SpecificationAssetClass::Nft) => Interface::V1NFT, + (Some(SpecificationVersions::V1), SpecificationAssetClass::PrintableNft) => { + Interface::V1NFT + } + (Some(SpecificationVersions::V0), SpecificationAssetClass::Nft) => { + Interface::LEGACY_NFT + } + (Some(SpecificationVersions::V1), SpecificationAssetClass::ProgrammableNft) => { Interface::ProgrammableNFT } (_, SpecificationAssetClass::MplCoreAsset) => Interface::MplCoreAsset, (_, SpecificationAssetClass::MplCoreCollection) => Interface::MplCoreCollection, + (_, SpecificationAssetClass::FungibleAsset) => Interface::FungibleAsset, + (_, SpecificationAssetClass::FungibleToken) => Interface::FungibleToken, _ => Interface::Custom, } } @@ -87,6 +96,10 @@ impl From for (SpecificationVersions, SpecificationAssetClass) { SpecificationVersions::V1, SpecificationAssetClass::MplCoreCollection, ), + Interface::FungibleToken => ( + SpecificationVersions::V1, + SpecificationAssetClass::FungibleToken, + ), _ => (SpecificationVersions::V1, SpecificationAssetClass::Unknown), } } @@ -366,7 +379,7 @@ pub struct MplCoreInfo { pub plugins_json_version: Option, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] pub struct Asset { pub interface: Interface, pub id: String, @@ -382,13 +395,17 @@ pub struct Asset { pub royalty: Option, #[serde(skip_serializing_if = "Option::is_none")] pub creators: Option>, - pub ownership: Ownership, + #[serde(skip_serializing_if = "Option::is_none")] + pub ownership: Option, #[serde(skip_serializing_if = "Option::is_none")] pub uses: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub supply: Option, pub mutable: bool, pub burnt: bool, #[serde(skip_serializing_if = "Option::is_none")] + pub mint_extensions: 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/tests/common.rs b/digital_asset_types/tests/common.rs index 0dcad71ef..6f40f610e 100644 --- a/digital_asset_types/tests/common.rs +++ b/digital_asset_types/tests/common.rs @@ -169,6 +169,7 @@ pub fn create_asset( mpl_core_plugins_json_version: None, mpl_core_external_plugins: None, mpl_core_unknown_external_plugins: None, + mint_extensions: None, }, ) } diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq b/integration_tests/tests/data/accounts/fungible_token_get_asset/5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq new file mode 100644 index 0000000000000000000000000000000000000000..a6006eeb7b89d1f63e6995e6db760c7d6ecb91e7 GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ^z4vWJ0r8`dyU|(_V&if>DTl+RleQM z&c0lG+(-Xrq7`>R>c+WQpO%-A@i(`u5;S%`zrtSN+&T1)xEp!7d8U`I&h@ eCRV@&6a}hrhDe^ks%Gd4GBW-dx{eHjEUJ*KQ@u=?ze~6Qx{IYdHSK-x!09zWVW=vjgK~;Xw~zzweCN|N|2cuUen%s z0qOrx(4@p61mrR(m4|vxW@KOp0DTl+RleQM z&c0lG+(-Xrq7`>R>c+fzhlO={5cRx9F>Dz-RHOrWoUG-xV6@UhX2D>OY=V#^t enOFfAP!y=f86tTCtD2!J$jJC-=sJF=xEuf~9a#SW literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_1/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v new file mode 100644 index 0000000000000000000000000000000000000000..3ffad1cc084755abda1e5f0355c8e01bd555f2e8 GIT binary patch literal 224 zcmY#jfB*@G3HjEUJ*KQ@u=?ze~6Qx{IYdHSK-x!09zWVW=vjgK~;Xw~zzweCN|N|2cuUen%s z0qOrxu-=$M2*_nvb-BoMG9v>+5ReU|7-sxyd(k`l(W~x*XO^^gYjzxR_^REavVRNT z#YJ~d$4SnfV84C6k`ylk8zV?{Qo*XO$fHwOw*(^b literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_2/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 b/integration_tests/tests/data/accounts/fungible_token_get_asset_scenario_2/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 new file mode 100644 index 0000000000000000000000000000000000000000..49c0c5373e0b96a3d918b6bdfe9bf5e8052fc5cf GIT binary patch literal 224 zcmY#jfB*@G3s-NKbc(mR7`=hC+=4P}x#(pf|eJ?fZQNb0p WEWehSa~TAdUU>mj#|2VM1ONcC%tojH literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y b/integration_tests/tests/data/accounts/token_extensions_get_asset/BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y new file mode 100644 index 0000000000000000000000000000000000000000..357411c17a7feac2d92f747959c0bddb86837e2b GIT binary patch literal 496 zcmY#jfB*@G3eYws-;VJdtF72Lx%-&gT7jO& z(Os`MziO5JSbLf6?ze~UO8f6gIK}RHl5_UfHmN_p%{zJ3|Ly&$absckACQ>|2F5^= zf#E+Ctk*XZ1o597Jm1#G$iR>U#6ZMwCjXK>@AF=dhi4)_{gn4#WYB-_+%C;;rsV|( z_HB87Dj1{&4B!ly0G((?AqEG6t|SyeYws-;VJdtF72Lx%-&gT7jO& z(Os`MziO5JSbLf6?ze~UO8f6gIK}RHl5_UfHmN_p%{zJ3|Ly&$absckACQ>|2F5^= zf#E+CY!8kQ0r90iczR4`WMD`FVjyBTlYhyc_j#|!!!r?|e#-kVGU&f|ZkOga)AE7? z`?fqk6%0}X25<&UfKD`{5Q76jR}u;_VTKZl&0q(5*u^ne!6mhf8N!qTQW+&B1;tkS a`iaT8sd~xzx%w%ox%qlo#rb)V@BsiME20hn literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM b/integration_tests/tests/data/accounts/token_extensions_get_asset_scenario2/HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM new file mode 100644 index 0000000000000000000000000000000000000000..a4c739eb46d55c67f75a738b7e75d6ede7f4579f GIT binary patch literal 760 zcmY#jfB*@G3brr2hCe@8nhgxA&*UjfLHRKxRI$^aqj* z4F93vQ46~eh=1^&yT@ck28Jvq1_mI-;M)DkL?q{AyV+Df2J`$B-r~1wlTVn>PF;ID zx+(wCW6=_26G zP#Z8ZGbj+Wmxl;7EDVkW%^~P`J_ZMh4G|*33_?jjm|-Fbx|XQ$@r=BAb;rX-dm>J=nbeYws-;VJdtF72Lx%-&gT7jO& z(Os`MziO5JSbLf6?ze~UO8f6gIK}RHl5_UfHmN_p%{zJ3|Ly&$absckACQ>|2F5^= zf#E+CY?X)<0rAf_qbrr2hCe@8nhgxA&*UjfLHRKxRI$^aqj* z4F93PN|!?j#J}C2?m3x}fgy{DfdNP{xORUs5y?5(ZZ_4A!8|{OxA^Va( zZpy#(ShPghcycgM9UCJ^wRo#h;`^)}!CL7bg4?3j8fw~~XFo5*wRPvhaJG=%1KL0( z)CP>q3W>`(o9fZu_0{S)}u|mNmKPM-# zhy}brr2hCe@8nhgxA&*UjfLHRKxXb?wBiL) z4F93PRF^{t#1Cmr_ngegz>vhuzyPEe`27=9wcZFbdF!k9`aOFdWW1V7OZg7hT-oAh z5ohM@nq4D$`S`gB3=C|HAl2fnMv3pUb_8pse+X`iT5G6jf1drk5ZBh73&Yt$dJkxW zY@jk=WM)txXfF>DY8V-E2%1Bz|6-ZYeTgZ;!r(~IK6Fzs_yP=#1Pzfff97mm%yPG> zxatY-?C#G;=1248c&@l=tg-ni+XR_#P{dIL_!t}rI)ab|LO?T`N<@v7x6Xc__q``1 zPa(&#qk4*ZZp!-w{T`pMH(9^9K`7k|GpwW73@)HU0}?9(5_1$ngI!obyhtF!AIQik zDJdwn($_D^&rZ$L%}p&yOi3(B)GJ7=$S>AQ&d=2^s4Oi`iHAw(7Zl~E=ojbbB<3Ze NN$F)3=jTB}1OOa}Bjx}A literal 0 HcmV?d00001 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 new file mode 100644 index 000000000..6115b3103 --- /dev/null +++ b/integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs @@ -0,0 +1,162 @@ +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_token_extensions_get_asset_scenario_1() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + "#; + + 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_token_extensions_get_asset_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(["HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" + } + "#; + + 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_token_extensions_get_asset_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" + } + "#; + + 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_fungible_token_get_asset_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([ + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "5x38Kp4hvdomTCnCrAny4UtMUt5rQBdB6px2K1Ui45Wq", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + } + "#; + + 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_fungible_token_get_asset_scenario_2() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new_with_options( + name.clone(), + TestSetupOptions { + network: Some(Network::Devnet), + }, + ) + .await; + + let seeds: Vec = seed_accounts(["wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1"]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "id": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1" + } + "#; + + 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/main.rs b/integration_tests/tests/integration_tests/main.rs index 7f88a27a8..63df2c4a2 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -1,6 +1,7 @@ mod account_update_tests; mod cnft_tests; mod common; +mod fungibles_and_token_extensions_tests; mod general_scenario_tests; mod mpl_core_tests; mod regular_nft_tests; diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap index fe306fab3..7ede46288 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-metadata-updated.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: response_new_slot +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response_new_slot "ownership_model": "single", "owner": "BzbdvwEkQKeghTY53aZxTYjUienhdbkNVkgrLV6cErke" }, - "supply": null, "mutable": false, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap index 5dac7346d..90985a04a 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-token-updated.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: response_new_slot +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response_new_slot "ownership_model": "single", "owner": "1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap index d0978550e..4a314f738 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates-with-all-updates.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: setup.das_api.get_asset(request.clone()).await.unwrap() +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: setup.das_api.get_asset(request.clone()).await.unwrap() "ownership_model": "single", "owner": "1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM" }, - "supply": null, "mutable": false, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap index 2deeb3b43..e37b12286 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__account_update_tests__account_updates.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/account_update_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response "ownership_model": "single", "owner": "BzbdvwEkQKeghTY53aZxTYjUienhdbkNVkgrLV6cErke" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap new file mode 100644 index 000000000..16be64a22 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_1.snap @@ -0,0 +1,56 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "", + "files": [], + "metadata": { + "name": "USD Coin", + "symbol": "USDC" + }, + "links": {} + }, + "authorities": [ + { + "address": "2wmVCSfPxGPjrnMMn7rchp4uaeoTqN39mXFC2zhPdri9", + "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 +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap new file mode 100644 index 000000000..2357ddf57 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__fungible_token_get_asset_scenario_2.snap @@ -0,0 +1,39 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1", + "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": false, + "burnt": false +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap new file mode 100644 index 000000000..edaf78071 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap @@ -0,0 +1,63 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://acme.com/demo.json", + "files": [], + "metadata": { + "name": "DAS Dev", + "symbol": "DAS" + }, + "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://acme.com/demo.json", + "mint": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "name": "DAS Dev", + "symbol": "DAS", + "update_authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "additional_metadata": [] + }, + "metadata_pointer": { + "authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "metadata_address": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap new file mode 100644 index 000000000..3ac4fdcb9 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap @@ -0,0 +1,78 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://token-metadata.paxos.com/usdp_metadata/prod/solana/usdp_metadata.json", + "files": [], + "metadata": { + "name": "Pax Dollar", + "symbol": "USDP" + }, + "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/usdp_metadata/prod/solana/usdp_metadata.json", + "mint": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "name": "Pax Dollar", + "symbol": "USDP", + "update_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "metadata_address": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap new file mode 100644 index 000000000..3484f3ce6 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap @@ -0,0 +1,197 @@ +--- +source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "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": { + "authority": "9nEfZqzTP3dfVWmzQy54TzsZqSQqDFVW4PhXdG9vYCVD", + "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 + ] + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap index 27d495e3a..9843eb6b3 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__asset_parsing.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/general_scenario_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -62,7 +63,6 @@ expression: response "ownership_model": "single", "owner": "BzbdvwEkQKeghTY53aZxTYjUienhdbkNVkgrLV6cErke" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap index 2ed4bf732..7b50b3a93 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__general_scenario_tests__creators_reordering.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/general_scenario_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -77,7 +78,6 @@ expression: response "ownership_model": "single", "owner": "AZgXpkRSetUJHy6C3NBvG6jNe49MpgrkZ2RkdMkjCjkW" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap index 1a8cfd344..552549a56 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 487 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap index 4277369f1..2753ea8e2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_autograph_plugin_with_signature.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 516 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "ACxrDWeCPic6voU6a8vyadpL8nSW15Un5vT76LDpxD4N" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap index 0b9a6039f..a72d1bfc8 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 37 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -62,7 +62,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap index b0d613582..e48244c90 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_binary_data_and_owner_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 603 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap index ba7f6bca3..d7f66a390 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_json_data_and_update_authority_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 633 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap index 2031b1797..38619bc7f 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_app_data_with_msg_pack_data_and_address_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 662 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap index 85e72340b..3fe19c33d 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_binary_data.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 721 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -56,7 +56,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap index 9524d637f..410235155 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_json_data.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 780 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -56,7 +56,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap index 840e75768..0294d65bf 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_data_section_with_msg_pack_data.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 839 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -56,7 +56,6 @@ expression: response "ownership_model": "single", "owner": "HKwwF4sPVYFPgqEgxth4GZRZjJ9o4E3wA8eu2QM5Vt3H" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap index 6333a1260..f86780213 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_edition.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 216 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap index 60e77e446..a941b9087 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_multiple_internal_and_external_plugins.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 458 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -57,7 +57,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap index ac19d3d3a..08ad3c6dd 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_custom_offset_and_base_address_config.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 361 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap index 40802d2a6..520d97d9b 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_external_plugin_on_collection.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 274 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap index f42fdf792..9bf617915 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_multiple_lifecycle_events.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 332 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap index 51d44c25c..fcbfab1d2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_oracle_no_offset.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 390 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap index bfbe6538e..05acfc8ac 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_pubkey_in_rule_set.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 245 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -62,7 +62,6 @@ expression: response "ownership_model": "single", "owner": "8LsUNkb7bLaAcZ7NjRKPuvcyRGTWbm9BxzUpVKjqdV8o" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap index b9d26a436..1da1d1281 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_asset_with_two_oracle_external_plugins.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 274 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap index 5acbdeb5d..53017c8fe 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 108 expression: response +snapshot_kind: text --- { "total": 2, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "GzYvuu9aUYXmnardj4svbAcCNmefiaGu2E3knGw9NJQQ" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, @@ -118,7 +117,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap index 431a64ec4..f338c6fb7 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 149 expression: response +snapshot_kind: text --- { "total": 3, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, @@ -123,7 +122,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, @@ -185,7 +183,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap index f9cea5683..a4236f53d 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_group_with_oracle_and_custom_pda_all_seeds.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 429 expression: response +snapshot_kind: text --- { "total": 1, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "FAe4nM85BQ8b1nWEc5TTeogQGnNDWsuGYU84vuiPc7kE" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap index 7b190b8a9..946d8a9af 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_assets_by_owner.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 187 expression: response +snapshot_kind: text --- { "total": 1, @@ -61,7 +61,6 @@ expression: response "ownership_model": "single", "owner": "7uScVQiT4vArB88dHrZoeVKWbtsRJmNp9r5Gce5VQpXS" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap index 1d854ee47..14a95dbb2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 66 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap index 38d0e3771..4e3b7c0c2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_binary_data_and_address_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 692 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap index b7ef57f77..2ed4afdc1 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_json_data_and_owner_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 751 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap index ff0cfbdbf..526a7a545 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_get_collection_with_linked_app_data_with_msg_pack_data_and_update_authority_is_data_authority.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 810 expression: response +snapshot_kind: text --- { "interface": "MplCoreCollection", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": {}, diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap index 6387d404c..cbd495062 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 545 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "APrZTeVysBJqAznfLXS71NAzjr2fCVTSF1A66MeErzM7" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap index 96428d6a1..36b1cd541 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__mpl_core_tests__mpl_core_verified_creators_plugin_with_signature.snap @@ -1,7 +1,7 @@ --- source: integration_tests/tests/integration_tests/mpl_core_tests.rs -assertion_line: 574 expression: response +snapshot_kind: text --- { "interface": "MplCoreAsset", @@ -51,7 +51,6 @@ expression: response "ownership_model": "single", "owner": "D9SSaw4tz5AGpfWynYJ66jDCVNTsbLBkqT8rxQFenqj4" }, - "supply": null, "mutable": true, "burnt": false, "plugins": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap index c62b3de1c..befc56b19 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- { "interface": "ProgrammableNFT", @@ -67,7 +68,6 @@ expression: response "ownership_model": "single", "owner": "A59E2tNJEqNN9TDnzgGnmLmnTsdRDoPocGx3n1w2dqZw" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap index 0825ec1b2..2f859cbb6 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-2-and-a-missing-1.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- [ { @@ -68,7 +69,6 @@ expression: response "ownership_model": "single", "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" }, - "supply": null, "mutable": true, "burnt": false }, @@ -138,7 +138,6 @@ expression: response "ownership_model": "single", "owner": "3H3d3hfpZVVdVwuFAxDtDSFN2AdR7kwiDA3ynbnbkhc9" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap index 36c9961f9..565f843d4 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2-different-2.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- [ { @@ -68,7 +69,6 @@ expression: response "ownership_model": "single", "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" }, - "supply": null, "mutable": true, "burnt": false }, @@ -137,7 +137,6 @@ expression: response "ownership_model": "single", "owner": "3H3d3hfpZVVdVwuFAxDtDSFN2AdR7kwiDA3ynbnbkhc9" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap index f07052af1..7647ef26c 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_batch-only-2.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- [ { @@ -68,7 +69,6 @@ expression: response "ownership_model": "single", "owner": "BaBQKh34KrqZzd4ifSHQYMf86HiBGASN6TWUi1ZwfyKv" }, - "supply": null, "mutable": true, "burnt": false }, @@ -137,7 +137,6 @@ expression: response "ownership_model": "single", "owner": "9PacVenjPyQYiWBha89UYRM1nn6mf9bGY7vi32zY6DLn" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap index 87920f33b..ce44d4bc4 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_get_asset_by_group.snap @@ -1,6 +1,7 @@ --- source: integration_tests/tests/integration_tests/regular_nft_tests.rs expression: response +snapshot_kind: text --- { "total": 1, @@ -77,7 +78,6 @@ expression: response "ownership_model": "single", "owner": "9qUcfdADyrrTSetFjNjF9Ro7LKAqzJkzZV6WKLHfv5MU" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap index bd321f698..415fdbb43 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__regular_nft_tests__reg_search_assets.snap @@ -72,7 +72,6 @@ expression: response "ownership_model": "single", "owner": "6Cr66AabRYymhZgYQSfTCo6FVpH18wXrMZswAbcErpyX" }, - "supply": null, "mutable": true, "burnt": false } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap new file mode 100644 index 000000000..3e6f1d5a1 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario1.snap @@ -0,0 +1,53 @@ +--- +source: integration_tests/tests/integration_tests/token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "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://acme.com/demo.json", + "mint": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y", + "name": "DAS Dev", + "symbol": "DAS", + "update_authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "additional_metadata": [] + }, + "metadata_pointer": { + "authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", + "metadata_address": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" + } + } +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap new file mode 100644 index 000000000..d56a5b03f --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__token_extensions_tests__token_extensions_get_asset_scenario2.snap @@ -0,0 +1,68 @@ +--- +source: integration_tests/tests/integration_tests/token_extensions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "interface": "FungibleToken", + "id": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "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/usdp_metadata/prod/solana/usdp_metadata.json", + "mint": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", + "name": "Pax Dollar", + "symbol": "USDP", + "update_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "additional_metadata": [] + }, + "transfer_hook": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "program_id": null + }, + "metadata_pointer": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "metadata_address": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" + }, + "permanent_delegate": { + "delegate": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "mint_close_authority": { + "close_authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk" + }, + "confidential_transfer_mint": { + "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", + "auditor_elgamal_pubkey": null, + "auto_approve_new_accounts": false + } + } +} diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 7cee77f5e..d2d5c9051 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -38,6 +38,7 @@ mod m20240116_130744_add_update_metadata_ix; mod m20240117_120101_alter_creator_indices; mod m20240124_173104_add_tree_seq_index_to_cl_audits_v2; mod m20240124_181900_add_slot_updated_column_per_update_type; +mod m20240219_115532_add_extensions_column; mod m20240313_120101_add_mpl_core_plugins_columns; mod m20240319_120101_add_mpl_core_enum_vals; mod m20240320_120101_add_mpl_core_info_items; @@ -90,6 +91,7 @@ impl MigratorTrait for Migrator { Box::new(m20240117_120101_alter_creator_indices::Migration), Box::new(m20240124_173104_add_tree_seq_index_to_cl_audits_v2::Migration), Box::new(m20240124_181900_add_slot_updated_column_per_update_type::Migration), + Box::new(m20240219_115532_add_extensions_column::Migration), Box::new(m20240313_120101_add_mpl_core_plugins_columns::Migration), Box::new(m20240319_120101_add_mpl_core_enum_vals::Migration), Box::new(m20240320_120101_add_mpl_core_info_items::Migration), diff --git a/migration/src/m20240219_115532_add_extensions_column.rs b/migration/src/m20240219_115532_add_extensions_column.rs new file mode 100644 index 000000000..d0414ea18 --- /dev/null +++ b/migration/src/m20240219_115532_add_extensions_column.rs @@ -0,0 +1,60 @@ +use sea_orm_migration::{ + prelude::*, + sea_orm::{ConnectionTrait, DatabaseBackend, Statement}, +}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let connection = manager.get_connection(); + + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE asset ADD COLUMN mint_extensions jsonb;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE tokens ADD COLUMN extensions jsonb;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE token_accounts ADD COLUMN extensions jsonb;".to_string(), + )) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let connection = manager.get_connection(); + + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE asset DROP COLUMN mint_extensions;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE tokens DROP COLUMN extensions;".to_string(), + )) + .await?; + connection + .execute(Statement::from_string( + DatabaseBackend::Postgres, + "ALTER TABLE token_accounts DROP COLUMN extensions;".to_string(), + )) + .await?; + + Ok(()) + } +} diff --git a/program_transformers/Cargo.toml b/program_transformers/Cargo.toml index 35bab7a19..de6eb6cc3 100644 --- a/program_transformers/Cargo.toml +++ b/program_transformers/Cargo.toml @@ -25,6 +25,7 @@ sqlx = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["time"] } tracing = { workspace = true } +spl-token-2022 = {workspace = true, features = ["no-entrypoint"]} [lints] workspace = true diff --git a/program_transformers/src/asset_upserts.rs b/program_transformers/src/asset_upserts.rs index 5561ff5fd..09fe09be0 100644 --- a/program_transformers/src/asset_upserts.rs +++ b/program_transformers/src/asset_upserts.rs @@ -56,8 +56,8 @@ pub async fn upsert_assets_token_account_columns, pub supply: Decimal, - pub supply_mint: Option>, - pub slot_updated_mint_account: u64, + pub slot_updated_mint_account: i64, + pub extensions: Option, } pub async fn upsert_assets_mint_account_columns( @@ -65,10 +65,17 @@ pub async fn upsert_assets_mint_account_columns Result<(), DbErr> { let active_model = asset::ActiveModel { - id: Set(columns.mint), + id: Set(columns.mint.clone()), supply: Set(columns.supply), - supply_mint: Set(columns.supply_mint), - slot_updated_mint_account: Set(Some(columns.slot_updated_mint_account as i64)), + supply_mint: Set(Some(columns.mint.clone())), + slot_updated_mint_account: Set(Some(columns.slot_updated_mint_account)), + slot_updated: Set(Some(columns.slot_updated_mint_account)), + mint_extensions: Set(columns.extensions), + asset_data: Set(Some(columns.mint.clone())), + // assume every token is a fungible token when mint account is created + specification_asset_class: Set(Some(SpecificationAssetClass::FungibleToken)), + // // assume multiple ownership as we set asset class to fungible token + owner_type: Set(OwnerType::Token), ..Default::default() }; let mut query = asset::Entity::insert(active_model) @@ -76,8 +83,10 @@ pub async fn upsert_assets_mint_account_columns Self { - let mut parsers: HashMap> = HashMap::with_capacity(3); + let mut parsers: HashMap> = HashMap::with_capacity(5); let bgum = BubblegumParser {}; let token_metadata = TokenMetadataParser {}; let token = TokenAccountParser {}; let mpl_core = MplCoreParser {}; + let token_extensions = Token2022AccountParser {}; 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)); let hs = parsers.iter().fold(HashSet::new(), |mut acc, (k, _)| { acc.insert(*k); acc @@ -131,7 +136,6 @@ impl ProgramTransformer { &self, tx_info: &TransactionInfo, ) -> ProgramTransformerResult<()> { - info!("Handling Transaction: {:?}", tx_info.signature); let instructions = self.break_transaction(tx_info); let mut not_impl = 0; let ixlen = instructions.len(); @@ -232,6 +236,14 @@ impl ProgramTransformer { ) .await } + ProgramParseResult::TokenExtensionsProgramAccount(parsing_result) => { + handle_token_extensions_program_account( + account_info, + parsing_result, + &self.storage, + ) + .await + } ProgramParseResult::MplCore(parsing_result) => { handle_mpl_core_account( account_info, @@ -280,3 +292,27 @@ fn record_metric(metric_name: &str, success: bool, retries: u32) { cadence_macros::statsd_count!(metric_name, 1, "success" => success, "retry_count" => retry_count); } } + +pub fn filter_non_null_fields(value: Value) -> Option { + match value { + Value::Null => None, + Value::Object(map) => { + if map.values().all(|v| matches!(v, Value::Null)) { + None + } else { + let filtered_map: Map = map + .into_iter() + .filter(|(_k, v)| !matches!(v, Value::Null)) + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + + if filtered_map.is_empty() { + None + } else { + Some(Value::Object(filtered_map)) + } + } + } + _ => Some(value), + } +} diff --git a/program_transformers/src/mpl_core_program/v1_asset.rs b/program_transformers/src/mpl_core_program/v1_asset.rs index 42e449286..a022bc67e 100644 --- a/program_transformers/src/mpl_core_program/v1_asset.rs +++ b/program_transformers/src/mpl_core_program/v1_asset.rs @@ -316,9 +316,9 @@ pub async fn save_v1_asset( upsert_assets_mint_account_columns( AssetMintAccountColumns { mint: id_vec.clone(), - supply_mint: None, supply, - slot_updated_mint_account: slot, + slot_updated_mint_account: slot as i64, + extensions: None, }, &txn, ) diff --git a/program_transformers/src/token/mod.rs b/program_transformers/src/token/mod.rs index d46360317..2abd30666 100644 --- a/program_transformers/src/token/mod.rs +++ b/program_transformers/src/token/mod.rs @@ -8,12 +8,13 @@ use { AccountInfo, DownloadMetadataNotifier, }, blockbuster::programs::token_account::TokenProgramAccount, - digital_asset_types::dao::{asset, sea_orm_active_enums::OwnerType, token_accounts, tokens}, + digital_asset_types::dao::{ + token_accounts, + tokens::{self, IsNonFungible}, + }, sea_orm::{ - entity::{ActiveValue, ColumnTrait}, - query::{QueryFilter, QueryTrait}, - sea_query::query::OnConflict, - ConnectionTrait, DatabaseConnection, DbBackend, EntityTrait, TransactionTrait, + entity::ActiveValue, query::QueryTrait, sea_query::query::OnConflict, ConnectionTrait, + DatabaseConnection, DbBackend, EntityTrait, TransactionTrait, }, solana_sdk::program_option::COption, spl_token::state::AccountState, @@ -27,6 +28,7 @@ pub async fn handle_token_program_account<'a, 'b>( ) -> ProgramTransformerResult<()> { let account_key = account_info.pubkey.to_bytes().to_vec(); let account_owner = account_info.owner.to_bytes().to_vec(); + let slot = account_info.slot as i64; match &parsing_result { TokenProgramAccount::TokenAccount(ta) => { let mint = ta.mint.to_bytes().to_vec(); @@ -44,9 +46,10 @@ pub async fn handle_token_program_account<'a, 'b>( frozen: ActiveValue::Set(frozen), delegated_amount: ActiveValue::Set(ta.delegated_amount as i64), token_program: ActiveValue::Set(account_owner.clone()), - slot_updated: ActiveValue::Set(account_info.slot as i64), + slot_updated: ActiveValue::Set(slot), amount: ActiveValue::Set(ta.amount as i64), close_authority: ActiveValue::Set(None), + extensions: ActiveValue::Set(None), }; let mut query = token_accounts::Entity::insert(model) @@ -71,30 +74,29 @@ pub async fn handle_token_program_account<'a, 'b>( query.sql ); db.execute(query).await?; - let txn = db.begin().await?; - let asset_update: Option = asset::Entity::find_by_id(mint.clone()) - .filter(asset::Column::OwnerType.eq("single")) - .one(&txn) + + let token = tokens::Entity::find_by_id(mint.clone()).one(db).await?; + + let is_non_fungible = token.map(|t| t.is_non_fungible()).unwrap_or(false); + + if is_non_fungible { + let txn = db.begin().await?; + + upsert_assets_token_account_columns( + AssetTokenAccountColumns { + mint: mint.clone(), + owner: Some(owner.clone()), + frozen, + delegate, + slot_updated_token_account: Some(slot), + }, + &txn, + ) .await?; - if let Some(_asset) = asset_update { - // will only update owner if token account balance is non-zero - // since the asset is marked as single then the token account balance can only be 1. Greater implies a fungible token in which case no si - // TODO: this does not guarantee in case when wallet receives an amount of 1 for a token but its supply is more. is unlikely since mints often have a decimal - if ta.amount == 1 { - upsert_assets_token_account_columns( - AssetTokenAccountColumns { - mint: mint.clone(), - owner: Some(owner.clone()), - frozen, - delegate, - slot_updated_token_account: Some(account_info.slot as i64), - }, - &txn, - ) - .await?; - } + + txn.commit().await?; } - txn.commit().await?; + Ok(()) } TokenProgramAccount::Mint(m) => { @@ -116,6 +118,7 @@ pub async fn handle_token_program_account<'a, 'b>( extension_data: ActiveValue::Set(None), mint_authority: ActiveValue::Set(mint_auth), freeze_authority: ActiveValue::Set(freeze_auth), + extensions: ActiveValue::Set(None), }; let mut query = tokens::Entity::insert(model) @@ -140,29 +143,20 @@ pub async fn handle_token_program_account<'a, 'b>( ); db.execute(query).await?; - let asset_update: Option = asset::Entity::find_by_id(account_key.clone()) - .filter( - asset::Column::OwnerType - .eq(OwnerType::Single) - .or(asset::Column::OwnerType - .eq(OwnerType::Unknown) - .and(asset::Column::Supply.eq(1))), - ) - .one(db) - .await?; - if let Some(_asset) = asset_update { - upsert_assets_mint_account_columns( - AssetMintAccountColumns { - mint: account_key.clone(), - supply_mint: Some(account_key), - supply: m.supply.into(), - slot_updated_mint_account: account_info.slot, - }, - db, - ) - .await?; - } + let txn = db.begin().await?; + upsert_assets_mint_account_columns( + AssetMintAccountColumns { + mint: account_key.clone(), + supply: m.supply.into(), + slot_updated_mint_account: slot, + extensions: None, + }, + &txn, + ) + .await?; + + txn.commit().await?; Ok(()) } } diff --git a/program_transformers/src/token_extensions/mod.rs b/program_transformers/src/token_extensions/mod.rs new file mode 100644 index 000000000..8fa8dcdd3 --- /dev/null +++ b/program_transformers/src/token_extensions/mod.rs @@ -0,0 +1,294 @@ +use { + crate::{ + asset_upserts::{ + upsert_assets_mint_account_columns, upsert_assets_token_account_columns, + AssetMintAccountColumns, AssetTokenAccountColumns, + }, + error::{ProgramTransformerError, ProgramTransformerResult}, + filter_non_null_fields, AccountInfo, + }, + blockbuster::programs::token_extensions::{ + extension::ShadowMetadata, MintAccount, TokenAccount, TokenExtensionsProgramAccount, + }, + digital_asset_types::dao::{ + asset, asset_data, + sea_orm_active_enums::ChainMutability, + token_accounts, + tokens::{self, IsNonFungible as IsNonFungibleModel}, + }, + sea_orm::{ + entity::ActiveValue, query::QueryTrait, sea_query::query::OnConflict, ConnectionTrait, + DatabaseConnection, DatabaseTransaction, DbBackend, DbErr, EntityTrait, Set, + TransactionTrait, + }, + serde_json::Value, + solana_sdk::program_option::COption, + spl_token_2022::state::AccountState, +}; + +pub async fn handle_token_extensions_program_account<'a, 'b, 'c>( + account_info: &'a AccountInfo, + parsing_result: &'b TokenExtensionsProgramAccount, + db: &'c DatabaseConnection, +) -> ProgramTransformerResult<()> { + let account_key = account_info.pubkey.to_bytes().to_vec(); + let account_owner = account_info.owner.to_bytes().to_vec(); + let slot = account_info.slot as i64; + match parsing_result { + TokenExtensionsProgramAccount::TokenAccount(ta) => { + let TokenAccount { + account, + extensions, + } = ta; + let ta = account; + + let extensions: Option = if extensions.is_some() { + filter_non_null_fields( + serde_json::to_value(extensions.clone()) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?, + ) + } else { + None + }; + + let mint = ta.mint.to_bytes().to_vec(); + let delegate: Option> = match ta.delegate { + COption::Some(d) => Some(d.to_bytes().to_vec()), + COption::None => None, + }; + let frozen = matches!(ta.state, AccountState::Frozen); + let owner = ta.owner.to_bytes().to_vec(); + let model = token_accounts::ActiveModel { + pubkey: ActiveValue::Set(account_key.clone()), + mint: ActiveValue::Set(mint.clone()), + delegate: ActiveValue::Set(delegate.clone()), + owner: ActiveValue::Set(owner.clone()), + frozen: ActiveValue::Set(frozen), + delegated_amount: ActiveValue::Set(ta.delegated_amount as i64), + token_program: ActiveValue::Set(account_owner.clone()), + slot_updated: ActiveValue::Set(slot), + amount: ActiveValue::Set(ta.amount as i64), + close_authority: ActiveValue::Set(None), + extensions: ActiveValue::Set(extensions.clone()), + }; + + let mut query = token_accounts::Entity::insert(model) + .on_conflict( + OnConflict::columns([token_accounts::Column::Pubkey]) + .update_columns([ + token_accounts::Column::Mint, + token_accounts::Column::DelegatedAmount, + token_accounts::Column::Delegate, + token_accounts::Column::Amount, + token_accounts::Column::Frozen, + token_accounts::Column::TokenProgram, + token_accounts::Column::Owner, + token_accounts::Column::CloseAuthority, + token_accounts::Column::SlotUpdated, + token_accounts::Column::Extensions, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + query.sql = format!( + "{} WHERE excluded.slot_updated > token_accounts.slot_updated", + query.sql + ); + db.execute(query).await?; + + let token = tokens::Entity::find_by_id(mint.clone()).one(db).await?; + + let is_non_fungible = token.map(|t| t.is_non_fungible()).unwrap_or(false); + + if is_non_fungible { + let txn = db.begin().await?; + + upsert_assets_token_account_columns( + AssetTokenAccountColumns { + mint: mint.clone(), + owner: Some(owner.clone()), + frozen, + delegate, + slot_updated_token_account: Some(slot), + }, + &txn, + ) + .await?; + + txn.commit().await?; + } + + Ok(()) + } + TokenExtensionsProgramAccount::MintAccount(m) => { + let MintAccount { + account, + extensions, + } = m; + + let mint_extensions: Option = if extensions.is_some() { + filter_non_null_fields( + serde_json::to_value(extensions.clone()) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?, + ) + } else { + None + }; + + let m = account; + let freeze_auth: Option> = match m.freeze_authority { + COption::Some(d) => Some(d.to_bytes().to_vec()), + COption::None => None, + }; + let mint_auth: Option> = match m.mint_authority { + COption::Some(d) => Some(d.to_bytes().to_vec()), + COption::None => None, + }; + let model = tokens::ActiveModel { + mint: ActiveValue::Set(account_key.clone()), + token_program: ActiveValue::Set(account_owner), + slot_updated: ActiveValue::Set(slot), + supply: ActiveValue::Set(m.supply.into()), + decimals: ActiveValue::Set(m.decimals as i32), + close_authority: ActiveValue::Set(None), + extension_data: ActiveValue::Set(None), + mint_authority: ActiveValue::Set(mint_auth), + freeze_authority: ActiveValue::Set(freeze_auth), + extensions: ActiveValue::Set(mint_extensions.clone()), + }; + + let mut query = tokens::Entity::insert(model) + .on_conflict( + OnConflict::columns([tokens::Column::Mint]) + .update_columns([ + tokens::Column::Supply, + tokens::Column::TokenProgram, + tokens::Column::MintAuthority, + tokens::Column::CloseAuthority, + tokens::Column::ExtensionData, + tokens::Column::SlotUpdated, + tokens::Column::Decimals, + tokens::Column::FreezeAuthority, + tokens::Column::Extensions, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + query.sql = format!( + "{} WHERE excluded.slot_updated >= tokens.slot_updated", + query.sql + ); + db.execute(query).await?; + let txn = db.begin().await?; + + upsert_assets_mint_account_columns( + AssetMintAccountColumns { + mint: account_key.clone(), + supply: m.supply.into(), + slot_updated_mint_account: slot, + extensions: mint_extensions.clone(), + }, + &txn, + ) + .await?; + + txn.commit().await?; + + if let Some(metadata) = &extensions.metadata { + upsert_asset_data(metadata, account_key.clone(), slot, db).await?; + } + + Ok(()) + } + _ => Err(ProgramTransformerError::NotImplemented), + } +} + +async fn upsert_asset_data( + metadata: &ShadowMetadata, + key_bytes: Vec, + slot: i64, + db: &DatabaseConnection, +) -> ProgramTransformerResult<()> { + let metadata_json = serde_json::to_value(metadata.clone()) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; + let asset_data_model = asset_data::ActiveModel { + metadata_url: ActiveValue::Set(metadata.uri.clone()), + metadata: ActiveValue::Set(Value::String("processing".to_string())), + id: ActiveValue::Set(key_bytes.clone()), + chain_data_mutability: ActiveValue::Set(ChainMutability::Mutable), + chain_data: ActiveValue::Set(metadata_json), + slot_updated: ActiveValue::Set(slot), + base_info_seq: ActiveValue::Set(Some(0)), + raw_name: ActiveValue::Set(Some(metadata.name.clone().into_bytes().to_vec())), + raw_symbol: ActiveValue::Set(Some(metadata.symbol.clone().into_bytes().to_vec())), + ..Default::default() + }; + let mut asset_data_query = asset_data::Entity::insert(asset_data_model) + .on_conflict( + OnConflict::columns([asset_data::Column::Id]) + .update_columns([ + asset_data::Column::ChainDataMutability, + asset_data::Column::ChainData, + asset_data::Column::MetadataUrl, + asset_data::Column::SlotUpdated, + asset_data::Column::BaseInfoSeq, + asset_data::Column::RawName, + asset_data::Column::RawSymbol, + ]) + .to_owned(), + ) + .build(DbBackend::Postgres); + asset_data_query.sql = format!( + "{} WHERE excluded.slot_updated >= asset_data.slot_updated", + asset_data_query.sql + ); + db.execute(asset_data_query).await?; + + let txn = db.begin().await?; + upsert_assets_metadata_cols( + AssetMetadataAccountCols { + mint: key_bytes.clone(), + slot_updated_metadata_account: slot, + }, + &txn, + ) + .await?; + + txn.commit().await?; + + Ok(()) +} + +struct AssetMetadataAccountCols { + mint: Vec, + slot_updated_metadata_account: i64, +} + +async fn upsert_assets_metadata_cols( + metadata: AssetMetadataAccountCols, + db: &DatabaseTransaction, +) -> Result<(), DbErr> { + let asset = asset::ActiveModel { + id: ActiveValue::Set(metadata.mint.clone()), + slot_updated_metadata_account: Set(Some(metadata.slot_updated_metadata_account)), + ..Default::default() + }; + + let mut asset_query = asset::Entity::insert(asset) + .on_conflict( + OnConflict::columns([asset::Column::Id]) + .update_columns([asset::Column::SlotUpdatedMetadataAccount]) + .to_owned(), + ) + .build(DbBackend::Postgres); + + asset_query.sql = format!( + "{} WHERE excluded.slot_updated_metadata_account >= asset.slot_updated_metadata_account OR asset.slot_updated_metadata_account IS NULL", + asset_query.sql + ); + + db.execute(asset_query).await?; + + Ok(()) +} diff --git a/program_transformers/src/token_metadata/v1_asset.rs b/program_transformers/src/token_metadata/v1_asset.rs index 7c45b68b7..dd40df218 100644 --- a/program_transformers/src/token_metadata/v1_asset.rs +++ b/program_transformers/src/token_metadata/v1_asset.rs @@ -20,7 +20,8 @@ use { ChainMutability, Mutability, OwnerType, SpecificationAssetClass, SpecificationVersions, V1AccountAttachments, }, - token_accounts, tokens, + token_accounts, + tokens::{self}, }, json::ChainDataV1, }, @@ -84,9 +85,9 @@ pub async fn index_and_fetch_mint_data( upsert_assets_mint_account_columns( AssetMintAccountColumns { mint: mint_pubkey_vec.clone(), - supply_mint: Some(token.mint.clone()), supply: token.supply, - slot_updated_mint_account: token.slot_updated as u64, + slot_updated_mint_account: token.slot_updated, + extensions: token.extensions.clone(), }, conn, ) @@ -109,7 +110,7 @@ async fn index_token_account_data( ) -> ProgramTransformerResult<()> { let token_account: Option = find_model_with_retry( conn, - "owners", + "token_accounts", &token_accounts::Entity::find() .filter(token_accounts::Column::Mint.eq(mint_pubkey_vec.clone())) .filter(token_accounts::Column::Amount.gt(0)) @@ -135,7 +136,7 @@ async fn index_token_account_data( } else { warn!( target: "Account not found", - "Token acc not found in 'owners' table for mint {}", + "Token acc not found in 'token-accounts' table for mint {}", bs58::encode(&mint_pubkey_vec).into_string() ); } @@ -173,10 +174,8 @@ pub async fn save_v1_asset( let mut ownership_type = match class { SpecificationAssetClass::FungibleAsset => OwnerType::Token, SpecificationAssetClass::FungibleToken => OwnerType::Token, - SpecificationAssetClass::Nft | SpecificationAssetClass::ProgrammableNft => { - OwnerType::Single - } - _ => OwnerType::Unknown, + SpecificationAssetClass::Unknown => OwnerType::Unknown, + _ => OwnerType::Single, }; // Wrapped Solana is a special token that has supply 0 (infinite). @@ -200,6 +199,14 @@ pub async fn save_v1_asset( }; }; + //Map specification asset class based on the supply. + if class == SpecificationAssetClass::Unknown { + class = match supply { + s if s > Decimal::from(1) => SpecificationAssetClass::FungibleToken, + _ => SpecificationAssetClass::Unknown, + }; + }; + if (ownership_type == OwnerType::Single) | (ownership_type == OwnerType::Unknown) { index_token_account_data(conn, mint_pubkey_vec.clone()).await?; } From e8aaded8d21aabadc35a9f4189e693b5628b2aa8 Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:34:55 +0530 Subject: [PATCH 04/10] add remaining extensions (#225) --- .../programs/token_extensions/extension.rs | 17 -------- .../src/programs/token_extensions/mod.rs | 42 +++++++++++++++---- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/blockbuster/src/programs/token_extensions/extension.rs b/blockbuster/src/programs/token_extensions/extension.rs index c9a0b9b4a..6dba2b57c 100644 --- a/blockbuster/src/programs/token_extensions/extension.rs +++ b/blockbuster/src/programs/token_extensions/extension.rs @@ -13,7 +13,6 @@ use spl_token_2022::extension::{ default_account_state::DefaultAccountState, group_member_pointer::GroupMemberPointer, group_pointer::GroupPointer, - immutable_owner::ImmutableOwner, interest_bearing_mint::{BasisPoints, InterestBearingConfig}, memo_transfer::MemoTransfer, metadata_pointer::MetadataPointer, @@ -77,9 +76,6 @@ pub struct ShadowDefaultAccountState { pub state: PodAccountState, } -#[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] -pub struct ShadowImmutableOwner; - #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowInterestBearingConfig { pub rate_authority: OptionalNonZeroPubkey, @@ -97,20 +93,16 @@ pub struct ShadowMemoTransfer { #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowMetadataPointer { - pub authority: OptionalNonZeroPubkey, pub metadata_address: OptionalNonZeroPubkey, } #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowGroupMemberPointer { - pub authority: OptionalNonZeroPubkey, pub member_address: OptionalNonZeroPubkey, } #[derive(Clone, Copy, Debug, Default, PartialEq, Zeroable, Serialize, Deserialize)] pub struct ShadowGroupPointer { - /// Authority that can set the group address - pub authority: OptionalNonZeroPubkey, /// Account address that holds the group pub group_address: OptionalNonZeroPubkey, } @@ -348,12 +340,6 @@ impl From for ShadowDefaultAccountState { } } -impl From for ShadowImmutableOwner { - fn from(_: ImmutableOwner) -> Self { - ShadowImmutableOwner - } -} - impl From for ShadowConfidentialTransferFeeAmount { fn from(original: ConfidentialTransferFeeAmount) -> Self { ShadowConfidentialTransferFeeAmount { @@ -381,7 +367,6 @@ impl From for ShadowMemoTransfer { impl From for ShadowMetadataPointer { fn from(original: MetadataPointer) -> Self { ShadowMetadataPointer { - authority: original.authority, metadata_address: original.metadata_address, } } @@ -390,7 +375,6 @@ impl From for ShadowMetadataPointer { impl From for ShadowGroupPointer { fn from(original: GroupPointer) -> Self { ShadowGroupPointer { - authority: original.authority, group_address: original.group_address, } } @@ -420,7 +404,6 @@ impl From for ShadowTokenGroupMember { impl From for ShadowGroupMemberPointer { fn from(original: GroupMemberPointer) -> Self { ShadowGroupMemberPointer { - authority: original.authority, member_address: original.member_address, } } diff --git a/blockbuster/src/programs/token_extensions/mod.rs b/blockbuster/src/programs/token_extensions/mod.rs index d890e5dbc..faf12b719 100644 --- a/blockbuster/src/programs/token_extensions/mod.rs +++ b/blockbuster/src/programs/token_extensions/mod.rs @@ -4,6 +4,7 @@ use crate::{ program_handler::{ParseResult, ProgramParser}, programs::ProgramParseResult, }; + use serde::{Deserialize, Serialize}; use solana_sdk::{pubkey::Pubkey, pubkeys}; use spl_token_2022::{ @@ -14,10 +15,12 @@ use spl_token_2022::{ default_account_state::DefaultAccountState, group_member_pointer::GroupMemberPointer, group_pointer::GroupPointer, + immutable_owner::ImmutableOwner, interest_bearing_mint::InterestBearingConfig, memo_transfer::MemoTransfer, metadata_pointer::MetadataPointer, mint_close_authority::MintCloseAuthority, + non_transferable::{NonTransferable, NonTransferableAccount}, permanent_delegate::PermanentDelegate, transfer_fee::{TransferFeeAmount, TransferFeeConfig}, transfer_hook::TransferHook, @@ -41,7 +44,6 @@ use self::extension::{ pub struct MintAccountExtensions { pub default_account_state: Option, pub confidential_transfer_mint: Option, - pub confidential_transfer_account: Option, pub confidential_transfer_fee_config: Option, pub interest_bearing_config: Option, pub transfer_fee_config: Option, @@ -54,6 +56,10 @@ pub struct MintAccountExtensions { pub token_group: Option, pub group_member_pointer: Option, pub token_group_member: Option, + // TODO : add this when spl-token-2022 is updated + // pub scaled_ui_amount: Option, + pub non_transferable: Option, + pub immutable_owner: Option, } impl MintAccountExtensions { @@ -82,6 +88,8 @@ pub struct TokenAccountExtensions { pub cpi_guard: Option, pub memo_transfer: Option, pub transfer_fee_amount: Option, + pub immutable_owner: Option, + pub non_transferable_account: Option, } impl TokenAccountExtensions { @@ -163,6 +171,16 @@ impl ProgramParser for Token2022AccountParser { let cpi_guard = account.get_extension::().ok().copied(); let memo_transfer = account.get_extension::().ok().copied(); let transfer_fee_amount = account.get_extension::().ok().copied(); + let immutable_owner = account + .get_extension::() + .ok() + .copied() + .map(|_| true); + let non_transferable_account = account + .get_extension::() + .ok() + .copied() + .map(|_| true); // Create a structured account with extensions let structured_account = TokenAccount { @@ -173,6 +191,8 @@ impl ProgramParser for Token2022AccountParser { cpi_guard: cpi_guard.map(ShadowCpiGuard::from), memo_transfer: memo_transfer.map(ShadowMemoTransfer::from), transfer_fee_amount: transfer_fee_amount.map(ShadowTransferFeeAmount::from), + immutable_owner, + non_transferable_account, }, }; @@ -182,10 +202,7 @@ impl ProgramParser for Token2022AccountParser { .get_extension::() .ok() .copied(); - let confidential_transfer_account = mint - .get_extension::() - .ok() - .copied(); + let confidential_transfer_fee_config = mint .get_extension::() .ok() @@ -203,14 +220,23 @@ impl ProgramParser for Token2022AccountParser { let group_member_pointer = mint.get_extension::().ok().copied(); let token_group_member = mint.get_extension::().ok().copied(); let transfer_hook = mint.get_extension::().ok().copied(); + let non_transferable = mint + .get_extension::() + .ok() + .copied() + .map(|_| true); + + let immutable_owner = mint + .get_extension::() + .ok() + .copied() + .map(|_| true); let structured_mint = MintAccount { account: mint.base, extensions: MintAccountExtensions { confidential_transfer_mint: confidential_transfer_mint .map(ShadowConfidentialTransferMint::from), - confidential_transfer_account: confidential_transfer_account - .map(ShadowConfidentialTransferAccount::from), confidential_transfer_fee_config: confidential_transfer_fee_config .map(ShadowConfidentialTransferFeeConfig::from), default_account_state: default_account_state @@ -227,6 +253,8 @@ impl ProgramParser for Token2022AccountParser { token_group: token_group.map(ShadowTokenGroup::from), group_member_pointer: group_member_pointer.map(ShadowGroupMemberPointer::from), token_group_member: token_group_member.map(ShadowTokenGroupMember::from), + non_transferable, + immutable_owner, }, }; result = TokenExtensionsProgramAccount::MintAccount(structured_mint); From 9654780ad3f376cbcc4efff60599977eed10b4fc Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:27:33 +0530 Subject: [PATCH 05/10] Query NFT Editions (#216) Associate editions to the nft mint of the master edition. Add new method to API get_nft_editions to find editions for the mint. --- Cargo.lock | 1 + .../src/programs/token_extensions/mod.rs | 1 - das_api/src/api/api_impl.rs | 35 ++++- das_api/src/api/mod.rs | 22 +++- das_api/src/builder.rs | 10 +- digital_asset_types/Cargo.toml | 1 + digital_asset_types/src/dao/scopes/asset.rs | 124 +++++++++++++++++- digital_asset_types/src/rpc/response.rs | 29 ++++ ...9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez | Bin 0 -> 824 bytes ...HfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ | Bin 0 -> 424 bytes ...mY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz | Bin 0 -> 824 bytes ...QecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz | Bin 0 -> 224 bytes ...xgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV | Bin 0 -> 384 bytes ...2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL | Bin 0 -> 224 bytes ...vFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw | Bin 0 -> 224 bytes ...iWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg | Bin 0 -> 384 bytes .../tests/integration_tests/main.rs | 1 + .../integration_tests/nft_editions_tests.rs | 50 +++++++ ...token_extensions_get_asset_scenario_1.snap | 2 - ...token_extensions_get_asset_scenario_2.snap | 2 - ...token_extensions_get_asset_scenario_3.snap | 2 - ..._nft_editions_tests__get_nft_editions.snap | 24 ++++ .../src/token_metadata/master_edition.rs | 73 +++++++---- .../src/token_metadata/mod.rs | 29 +++- .../src/token_metadata/v1_asset.rs | 35 +++++ 25 files changed, 401 insertions(+), 40 deletions(-) create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw create mode 100644 integration_tests/tests/data/accounts/get_nft_editions/giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg create mode 100644 integration_tests/tests/integration_tests/nft_editions_tests.rs create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap diff --git a/Cargo.lock b/Cargo.lock index 34458c8ad..2e8cba4d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1946,6 +1946,7 @@ dependencies = [ "jsonpath_lib", "log", "mime_guess", + "mpl-token-metadata", "num-derive 0.3.3", "num-traits", "schemars", diff --git a/blockbuster/src/programs/token_extensions/mod.rs b/blockbuster/src/programs/token_extensions/mod.rs index faf12b719..6272f96b5 100644 --- a/blockbuster/src/programs/token_extensions/mod.rs +++ b/blockbuster/src/programs/token_extensions/mod.rs @@ -66,7 +66,6 @@ impl MintAccountExtensions { pub fn is_some(&self) -> bool { self.default_account_state.is_some() || self.confidential_transfer_mint.is_some() - || self.confidential_transfer_account.is_some() || self.confidential_transfer_fee_config.is_some() || self.interest_bearing_config.is_some() || self.transfer_fee_config.is_some() diff --git a/das_api/src/api/api_impl.rs b/das_api/src/api/api_impl.rs index 4f25ed653..9aac04b2c 100644 --- a/das_api/src/api/api_impl.rs +++ b/das_api/src/api/api_impl.rs @@ -1,15 +1,15 @@ use digital_asset_types::{ dao::{ - scopes::asset::get_grouping, + scopes::asset::{get_grouping, get_nft_editions}, sea_orm_active_enums::{ OwnerType, RoyaltyTargetType, SpecificationAssetClass, SpecificationVersions, }, Cursor, PageOptions, SearchAssetsQuery, }, dapi::{ - get_asset, get_asset_proofs, get_asset_signatures, get_assets, get_assets_by_authority, - get_assets_by_creator, get_assets_by_group, get_assets_by_owner, get_proof_for_asset, - get_token_accounts, search_assets, + common::create_pagination, get_asset, get_asset_proofs, get_asset_signatures, get_assets, + get_assets_by_authority, get_assets_by_creator, get_assets_by_group, get_assets_by_owner, + get_proof_for_asset, get_token_accounts, search_assets, }, rpc::{ filter::{AssetSortBy, SearchConditionType}, @@ -501,6 +501,7 @@ impl ApiContract for DasApi { .await .map_err(Into::into) } + async fn get_grouping( self: &DasApi, payload: GetGrouping, @@ -545,4 +546,30 @@ impl ApiContract for DasApi { .await .map_err(Into::into) } + + async fn get_nft_editions( + self: &DasApi, + payload: GetNftEditions, + ) -> Result { + let GetNftEditions { + mint_address, + page, + limit, + before, + after, + cursor, + } = payload; + + let page_options = self.validate_pagination(limit, page, &before, &after, &cursor, None)?; + let mint_address = validate_pubkey(mint_address.clone())?; + let pagination = create_pagination(&page_options)?; + get_nft_editions( + &self.db_connection, + mint_address, + &pagination, + page_options.limit, + ) + .await + .map_err(Into::into) + } } diff --git a/das_api/src/api/mod.rs b/das_api/src/api/mod.rs index ab9da0f9b..b98fe2cbe 100644 --- a/das_api/src/api/mod.rs +++ b/das_api/src/api/mod.rs @@ -2,7 +2,9 @@ use crate::error::DasApiError; use async_trait::async_trait; use digital_asset_types::rpc::filter::{AssetSortDirection, SearchConditionType}; use digital_asset_types::rpc::options::Options; -use digital_asset_types::rpc::response::{AssetList, TokenAccountList, TransactionSignatureList}; +use digital_asset_types::rpc::response::{ + AssetList, NftEditions, TokenAccountList, TransactionSignatureList, +}; use digital_asset_types::rpc::{filter::AssetSorting, response::GetGroupingResponse}; use digital_asset_types::rpc::{Asset, AssetProof, Interface, OwnershipModel, RoyaltyModel}; use open_rpc_derive::{document_rpc, rpc}; @@ -147,6 +149,18 @@ pub struct GetGrouping { pub group_value: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct GetNftEditions { + pub mint_address: String, + pub page: Option, + pub limit: Option, + pub before: Option, + pub after: Option, + #[serde(default)] + pub cursor: Option, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct GetAssetSignatures { @@ -276,4 +290,10 @@ pub trait ApiContract: Send + Sync + 'static { &self, payload: GetTokenAccounts, ) -> Result; + #[rpc( + name = "getNftEditions", + params = "named", + summary = "Get all printable editions for a master edition NFT mint" + )] + async fn get_nft_editions(&self, payload: GetNftEditions) -> Result; } diff --git a/das_api/src/builder.rs b/das_api/src/builder.rs index df5e3bc6c..d048dfc5a 100644 --- a/das_api/src/builder.rs +++ b/das_api/src/builder.rs @@ -128,9 +128,17 @@ impl RpcApiBuilder { .map_err(Into::into) }, )?; - module.register_alias("getTokenAccounts", "get_token_accounts")?; + module.register_async_method("get_nft_editions", |rpc_params, rpc_context| async move { + let payload = rpc_params.parse::()?; + rpc_context + .get_nft_editions(payload) + .await + .map_err(Into::into) + })?; + module.register_alias("getNftEditions", "get_nft_editions")?; + Ok(module) } } diff --git a/digital_asset_types/Cargo.toml b/digital_asset_types/Cargo.toml index 449f6e78d..53d7bc0d5 100644 --- a/digital_asset_types/Cargo.toml +++ b/digital_asset_types/Cargo.toml @@ -29,6 +29,7 @@ spl-concurrent-merkle-tree = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["macros"] } url = { workspace = true } +mpl-token-metadata = { workspace = true } [features] default = ["json_types", "sql_types"] diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index b3c4e0e63..b6530e3dc 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -1,15 +1,24 @@ use crate::{ dao::{ asset::{self}, - asset_authority, asset_creators, asset_data, asset_grouping, cl_audits_v2, + asset_authority, asset_creators, asset_data, asset_grouping, asset_v1_account_attachments, + cl_audits_v2, extensions::{self, instruction::PascalCase}, - sea_orm_active_enums::Instruction, + sea_orm_active_enums::{Instruction, V1AccountAttachments}, token_accounts, Cursor, FullAsset, GroupingSize, Pagination, }, - rpc::{filter::AssetSortDirection, options::Options}, + rpc::{ + filter::AssetSortDirection, + options::Options, + response::{NftEdition, NftEditions}, + }, }; use indexmap::IndexMap; -use sea_orm::{entity::*, query::*, ConnectionTrait, DbErr, Order}; +use mpl_token_metadata::accounts::{Edition, MasterEdition}; +use sea_orm::{entity::*, query::*, sea_query::Expr, ConnectionTrait, DbErr, Order}; +use serde::de::DeserializeOwned; +use serde_json::Value; +use solana_sdk::pubkey::Pubkey; use std::collections::HashMap; pub fn paginate( @@ -595,3 +604,110 @@ pub async fn get_token_accounts( Ok(token_accounts) } + +pub 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( + attachment: asset_v1_account_attachments::Model, +) -> Result { + let data: Edition = attachment + .data + .clone() + .ok_or(DbErr::RecordNotFound("Edition data not found".to_string())) + .map(get_edition_data_from_json)??; + + Ok(NftEdition { + mint_address: attachment + .asset_id + .clone() + .map(|id| bs58::encode(id).into_string()) + .unwrap_or("".to_string()), + edition_number: data.edition, + edition_address: bs58::encode(attachment.id.clone()).into_string(), + }) +} + +pub async fn get_nft_editions( + conn: &impl ConnectionTrait, + mint_address: Pubkey, + pagination: &Pagination, + limit: u64, +) -> Result { + let master_edition_pubkey = MasterEdition::find_pda(&mint_address).0; + + // to fetch nft editions associated with a mint we need to fetch the master edition first + let master_edition = + asset_v1_account_attachments::Entity::find_by_id(master_edition_pubkey.to_bytes().to_vec()) + .one(conn) + .await? + .ok_or(DbErr::RecordNotFound( + "Master Edition not found".to_string(), + ))?; + + let master_edition_data: MasterEdition = master_edition + .data + .clone() + .ok_or(DbErr::RecordNotFound( + "Master Edition data not found".to_string(), + )) + .map(get_edition_data_from_json)??; + + let mut stmt = asset_v1_account_attachments::Entity::find(); + + stmt = stmt.filter( + asset_v1_account_attachments::Column::AttachmentType + .eq(V1AccountAttachments::Edition) + // The data field is a JSON field that contains the edition data. + .and(asset_v1_account_attachments::Column::Data.is_not_null()) + // The parent field is a string field that contains the master edition pubkey ( mapping edition to master edition ) + .and(Expr::cust(&format!( + "data->>'parent' = '{}'", + master_edition_pubkey + ))), + ); + + let nft_editions = paginate( + pagination, + limit, + stmt, + Order::Asc, + asset_v1_account_attachments::Column::Id, + ) + .all(conn) + .await? + .into_iter() + .map(attachment_to_nft_edition) + .collect::, _>>()?; + + let (page, before, after, cursor) = match pagination { + Pagination::Keyset { before, after } => { + let bef = before.clone().and_then(|x| String::from_utf8(x).ok()); + let aft = after.clone().and_then(|x| String::from_utf8(x).ok()); + (None, bef, aft, None) + } + Pagination::Page { page } => (Some(*page as u32), None, None, None), + Pagination::Cursor(_) => { + if let Some(last_asset) = nft_editions.last() { + let cursor_str = bs58::encode(last_asset.edition_address.clone()).into_string(); + (None, None, None, Some(cursor_str)) + } else { + (None, None, None, None) + } + } + }; + + Ok(NftEditions { + total: nft_editions.len() as u32, + master_edition_address: master_edition_pubkey.to_string(), + supply: master_edition_data.supply, + max_supply: master_edition_data.max_supply, + editions: nft_editions, + limit: limit as u32, + page, + before, + after, + cursor, + }) +} diff --git a/digital_asset_types/src/rpc/response.rs b/digital_asset_types/src/rpc/response.rs index 55277e5d4..641f39640 100644 --- a/digital_asset_types/src/rpc/response.rs +++ b/digital_asset_types/src/rpc/response.rs @@ -67,3 +67,32 @@ pub struct TokenAccountList { pub cursor: Option, pub errors: Vec, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] + +pub struct NftEdition { + pub mint_address: String, + pub edition_address: String, + pub edition_number: u64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] +#[serde(default)] +pub struct NftEditions { + pub total: u32, + pub limit: u32, + pub master_edition_address: String, + pub supply: u64, + pub max_supply: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub editions: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub before: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub after: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cursor: Option, +} diff --git a/integration_tests/tests/data/accounts/get_nft_editions/4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez b/integration_tests/tests/data/accounts/get_nft_editions/4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez new file mode 100644 index 0000000000000000000000000000000000000000..558488282bb1232cbef1991e25eb372f8473ce9f GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQG(K_lWXjskoNd)X_I{Zze`gf+PCoH% z@fSt|aOz@1>11JugYT z_mSK!Q@-GfH$WJyZDv2)&3att&^>xigF>fF_FPIrYfk`M38G&KK_?MA+Ae}n2 G?FImpjDPq5 literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_nft_editions/8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ b/integration_tests/tests/data/accounts/get_nft_editions/8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ new file mode 100644 index 0000000000000000000000000000000000000000..10eae717ab2f9945cb62f4d2f931cc1b942ef8f3 GIT binary patch literal 424 zcmY#jfB*@G33~?>c+riUHLezOgX*(c4xf$Kky9+S;O|z?Z5Q`2|Il>P0GYWZ6i6~K z{D%Ttt)GG*{)e?&^d~cdSPTrynHU&Y_!DxbG_F>dc;G%0L#o8PlBlb^mp0Dyyd?47 zM{>7JRpR5a#EUx(tW9UOh(F|)+}kSf>A(Nt3!k|%18+~0sI;G>05m8#GpDpDGcivg zAhjqnKLunq3gANFodEJON=gcft@QO1i^@|I%To37QcLs;OHzzN(^9;%lXHvHb-i5O z^L*l?y!?v%i-QgFlZ}c>;){YptHMfsU9(Zl8%WLzW=2q85(-2{V3;ueV`Lskrw(nq E0r3NTfdBvi literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_nft_editions/9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz b/integration_tests/tests/data/accounts/get_nft_editions/9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz new file mode 100644 index 0000000000000000000000000000000000000000..75d190842d3f68f6177bc5db184233e9750c912a GIT binary patch literal 224 zcmY#jfB*@G30V6T085jUQ3Q-vV literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_nft_editions/AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV b/integration_tests/tests/data/accounts/get_nft_editions/AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV new file mode 100644 index 0000000000000000000000000000000000000000..31abb9fb66ffa18d1f0277f89d9b97146391fd2b GIT binary patch literal 384 zcmY#jfB*@G3vkVkY}&(Jkh<~l#Tr+Oxk2^WEQilV?8qsWTJU$P=eCP`n15)xfy^{8PzRC> z4F93PPV1*2i2rrn7X8VL3=AKEY{tAA-T1}tt9nnz+SKH*cxR_w`f>KSR{h%Aq(z-) Ma?E!D)eScQ0QjpyO#lD@ literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_nft_editions/Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL b/integration_tests/tests/data/accounts/get_nft_editions/Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL new file mode 100644 index 0000000000000000000000000000000000000000..cf8f217c81e16d808c5fa972c134551cf80544ce GIT binary patch literal 224 zcmY#jfB*@G3`7X8VL3=Ba)HjrY-tI>^L{JyI9bgWHH4vTko+NB?7k89Phtxa0g Sc_znv7f1~lFcPDhfdK%>SyNR2 literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_nft_editions/GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw b/integration_tests/tests/data/accounts/get_nft_editions/GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw new file mode 100644 index 0000000000000000000000000000000000000000..80ea336fbe2765a4003cf9286d1359b196ba6b4e GIT binary patch literal 224 zcmY#jfB*@G3C6`Khy0RzTLnJ-_g{SB zGgoHd?P(H~_H)?oetUR5b>ZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW1euxOD+VMP z82&?noz_o55P#yjE&7ug85n|qY#_xj@xVRZ7eDqlPh7KV_QjGo& T?LMy9vb+~&A literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_nft_editions/giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg b/integration_tests/tests/data/accounts/get_nft_editions/giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg new file mode 100644 index 0000000000000000000000000000000000000000..a63f177f011d22c824c8e4997f9296abebb840b6 GIT binary patch literal 384 zcmY#jfB*@G3vvn_(rH@{pYrcQ-whe-O)Yyu zD&O-3EqXFP=^=MP>c+OCE6Q = seed_accounts([ + "Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL", + "9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz", + "8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ", + "GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw", + "9ZmY7qCaq7WbrR7RZdHWCNS9FrFRPwRqU84wzWfmqLDz", + "AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV", + "9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz", + "4V9QuYLpiMu4ZQmhdEHmgATdgiHkDeJfvZi84BfkYcez", + "giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "mintAddress": "Ey2Qb8kLctbchQsMnhZs5DjY32To2QtPuXNwWvk4NosL", + "limit":10 + } + "#; + + let request: api::GetNftEditions = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_nft_editions(request).await.unwrap(); + + insta::assert_json_snapshot!(name, response); +} diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap index edaf78071..52b0da861 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_1.snap @@ -1,7 +1,6 @@ --- source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs expression: response -snapshot_kind: text --- { "interface": "FungibleToken", @@ -56,7 +55,6 @@ snapshot_kind: text "additional_metadata": [] }, "metadata_pointer": { - "authority": "Em34oqDQYQZ9b6ycPHD28K47mttrRsdNu1S1pgK6NtPL", "metadata_address": "BPU5vrAHafRuVeK33CgfdwTKSsmC4p6t3aqyav3cFF7Y" } } diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap index 3ac4fdcb9..31d33d4d2 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_2.snap @@ -1,7 +1,6 @@ --- source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs expression: response -snapshot_kind: text --- { "interface": "FungibleToken", @@ -60,7 +59,6 @@ snapshot_kind: text "program_id": null }, "metadata_pointer": { - "authority": "2apBGMsS6ti9RyF5TwQTDswXBWskiJP2LD4cUEDqYJjk", "metadata_address": "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM" }, "permanent_delegate": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap index 3484f3ce6..75c59b24b 100644 --- a/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__fungibles_and_token_extensions_tests__token_extensions_get_asset_scenario_3.snap @@ -1,7 +1,6 @@ --- source: integration_tests/tests/integration_tests/fungibles_and_token_extensions_tests.rs expression: response -snapshot_kind: text --- { "interface": "FungibleToken", @@ -60,7 +59,6 @@ snapshot_kind: text "program_id": null }, "metadata_pointer": { - "authority": "9nEfZqzTP3dfVWmzQy54TzsZqSQqDFVW4PhXdG9vYCVD", "metadata_address": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo" }, "permanent_delegate": { diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap new file mode 100644 index 000000000..d67779b90 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__nft_editions_tests__get_nft_editions.snap @@ -0,0 +1,24 @@ +--- +source: integration_tests/tests/integration_tests/nft_editions_tests.rs +expression: response +snapshot_kind: text +--- +{ + "total": 2, + "limit": 10, + "master_edition_address": "8SHfqzJYABeGfiG1apwiEYt6TvfGQiL1pdwEjvTKsyiZ", + "supply": 60, + "max_supply": 69, + "editions": [ + { + "mint_address": "GJvFDcBWf6aDncd1TBzx2ou1rgLFYaMBdbYLBa9oTAEw", + "edition_address": "AoxgzXKEsJmUyF5pBb3djn9cJFA26zh2SQHvd9EYijZV", + "edition_number": 1 + }, + { + "mint_address": "9yQecKKYSHxez7fFjJkUvkz42TLmkoXzhyZxEf2pw8pz", + "edition_address": "giWoA4jqHFkodPJgtbRYRcYtiXbsVytnxnEao3QT2gg", + "edition_number": 2 + } + ] +} diff --git a/program_transformers/src/token_metadata/master_edition.rs b/program_transformers/src/token_metadata/master_edition.rs index 791368af6..73eeb1fcc 100644 --- a/program_transformers/src/token_metadata/master_edition.rs +++ b/program_transformers/src/token_metadata/master_edition.rs @@ -1,17 +1,15 @@ use { crate::error::{ProgramTransformerError, ProgramTransformerResult}, blockbuster::token_metadata::{ - accounts::{DeprecatedMasterEditionV1, MasterEdition}, + accounts::{DeprecatedMasterEditionV1, Edition, MasterEdition}, types::Key, }, digital_asset_types::dao::{ - asset, asset_v1_account_attachments, extensions, - sea_orm_active_enums::{SpecificationAssetClass, V1AccountAttachments}, + asset_v1_account_attachments, sea_orm_active_enums::V1AccountAttachments, }, sea_orm::{ - entity::{ActiveModelTrait, ActiveValue, EntityTrait, RelationTrait}, - prelude::*, - query::{JoinType, QuerySelect, QueryTrait}, + entity::{ActiveValue, EntityTrait}, + query::QueryTrait, sea_query::query::OnConflict, ConnectionTrait, DatabaseTransaction, DbBackend, }, @@ -65,15 +63,7 @@ pub async fn save_master_edition( txn: &DatabaseTransaction, ) -> ProgramTransformerResult<()> { let id_bytes = id.to_bytes().to_vec(); - let master_edition: Option<(asset_v1_account_attachments::Model, Option)> = - asset_v1_account_attachments::Entity::find_by_id(id.to_bytes().to_vec()) - .find_also_related(asset::Entity) - .join( - JoinType::InnerJoin, - extensions::asset::Relation::AssetData.def(), - ) - .one(txn) - .await?; + let ser = serde_json::to_value(me_data) .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; @@ -85,14 +75,47 @@ pub async fn save_master_edition( ..Default::default() }; - if let Some((_me, Some(asset))) = master_edition { - let mut updatable: asset::ActiveModel = asset.into(); - updatable.supply = ActiveValue::Set(Decimal::from(1)); - updatable.specification_asset_class = ActiveValue::Set(Some(SpecificationAssetClass::Nft)); - updatable.update(txn).await?; - } + 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::AttachmentType, + 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 + ); + + txn.execute(query).await?; + Ok(()) +} - let query = asset_v1_account_attachments::Entity::insert(model) +pub async fn save_edition( + id: Pubkey, + slot: u64, + e_data: &Edition, + txn: &DatabaseTransaction, +) -> ProgramTransformerResult<()> { + let id_bytes = id.to_bytes().to_vec(); + + let ser = serde_json::to_value(e_data) + .map_err(|e| ProgramTransformerError::SerializatonError(e.to_string()))?; + + let model = asset_v1_account_attachments::ActiveModel { + id: ActiveValue::Set(id_bytes), + attachment_type: ActiveValue::Set(V1AccountAttachments::Edition), + data: ActiveValue::Set(Some(ser)), + slot_updated: ActiveValue::Set(slot as i64), + ..Default::default() + }; + + let mut query = asset_v1_account_attachments::Entity::insert(model) .on_conflict( OnConflict::columns([asset_v1_account_attachments::Column::Id]) .update_columns([ @@ -103,6 +126,12 @@ pub async fn save_master_edition( .to_owned(), ) .build(DbBackend::Postgres); + + query.sql = format!( + "{} WHERE excluded.slot_updated >= asset_v1_account_attachments.slot_updated", + query.sql + ); + txn.execute(query).await?; Ok(()) } diff --git a/program_transformers/src/token_metadata/mod.rs b/program_transformers/src/token_metadata/mod.rs index cbeb94171..0080303ed 100644 --- a/program_transformers/src/token_metadata/mod.rs +++ b/program_transformers/src/token_metadata/mod.rs @@ -7,7 +7,11 @@ use { }, AccountInfo, DownloadMetadataNotifier, }, - blockbuster::programs::token_metadata::{TokenMetadataAccountData, TokenMetadataAccountState}, + blockbuster::{ + programs::token_metadata::{TokenMetadataAccountData, TokenMetadataAccountState}, + token_metadata::types::TokenStandard, + }, + master_edition::save_edition, sea_orm::{DatabaseConnection, TransactionTrait}, }; @@ -45,9 +49,32 @@ pub async fn handle_token_metadata_account<'a, 'b>( txn.commit().await?; Ok(()) } + TokenMetadataAccountData::EditionV1(e) => { + let txn = db.begin().await?; + save_edition(account_info.pubkey, account_info.slot, e, &txn).await?; + txn.commit().await?; + Ok(()) + } + // TokenMetadataAccountData::EditionMarker(_) => {} // TokenMetadataAccountData::UseAuthorityRecord(_) => {} // TokenMetadataAccountData::CollectionAuthorityRecord(_) => {} _ => Err(ProgramTransformerError::NotImplemented), } } + +pub trait IsNonFungibe { + fn is_non_fungible(&self) -> bool; +} + +impl IsNonFungibe for TokenStandard { + fn is_non_fungible(&self) -> bool { + matches!( + self, + TokenStandard::NonFungible + | TokenStandard::NonFungibleEdition + | TokenStandard::ProgrammableNonFungible + | TokenStandard::ProgrammableNonFungibleEdition + ) + } +} diff --git a/program_transformers/src/token_metadata/v1_asset.rs b/program_transformers/src/token_metadata/v1_asset.rs index dd40df218..70634f5bc 100644 --- a/program_transformers/src/token_metadata/v1_asset.rs +++ b/program_transformers/src/token_metadata/v1_asset.rs @@ -1,4 +1,5 @@ use { + super::IsNonFungibe, crate::{ asset_upserts::{ upsert_assets_metadata_account_columns, upsert_assets_mint_account_columns, @@ -32,6 +33,7 @@ use { ConnectionTrait, DbBackend, DbErr, TransactionTrait, }, solana_sdk::pubkey, + solana_sdk::pubkey::Pubkey, sqlx::types::Decimal, tracing::warn, }; @@ -408,6 +410,11 @@ pub async fn save_v1_asset( } txn.commit().await?; + // If the asset is a non-fungible token, then we need to insert to the asset_v1_account_attachments table + if let Some(true) = metadata.token_standard.map(|t| t.is_non_fungible()) { + upsert_asset_v1_account_attachments(conn, &mint_pubkey, slot).await?; + } + if uri.is_empty() { warn!( "URI is empty for mint {}. Skipping background task.", @@ -418,3 +425,31 @@ pub async fn save_v1_asset( Ok(Some(DownloadMetadataInfo::new(mint_pubkey_vec, uri))) } + +async fn upsert_asset_v1_account_attachments( + conn: &T, + mint_pubkey: &Pubkey, + slot: u64, +) -> ProgramTransformerResult<()> { + let edition_pubkey = MasterEdition::find_pda(mint_pubkey).0; + let mint_pubkey_vec = mint_pubkey.to_bytes().to_vec(); + let attachment = asset_v1_account_attachments::ActiveModel { + id: ActiveValue::Set(edition_pubkey.to_bytes().to_vec()), + asset_id: ActiveValue::Set(Some(mint_pubkey_vec.clone())), + slot_updated: ActiveValue::Set(slot as i64), + // by default, the attachment type is MasterEditionV2 + attachment_type: ActiveValue::Set(V1AccountAttachments::MasterEditionV2), + ..Default::default() + }; + let query = asset_v1_account_attachments::Entity::insert(attachment) + .on_conflict( + OnConflict::columns([asset_v1_account_attachments::Column::Id]) + .update_columns([asset_v1_account_attachments::Column::AssetId]) + .to_owned(), + ) + .build(DbBackend::Postgres); + + conn.execute(query).await?; + + Ok(()) +} From 313cb5ffd753734759c2a1e504a8e105acdfcfa3 Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:09:24 +0530 Subject: [PATCH 06/10] Index Token Inscriptions (#222) Program parser for inscriptions for blockbluster. Create and register program parser with program tranformer. Add show_instrcitions flag to the API to view the inscription information. --- blockbuster/src/programs/mod.rs | 3 + .../src/programs/token_inscriptions/mod.rs | 141 ++++++++++++++++++ digital_asset_types/src/dao/full_asset.rs | 3 + .../src/dao/generated/sea_orm_active_enums.rs | 2 + digital_asset_types/src/dao/scopes/asset.rs | 84 ++++++++--- .../src/dapi/assets_by_authority.rs | 2 +- .../src/dapi/assets_by_creator.rs | 2 +- .../src/dapi/assets_by_group.rs | 2 +- .../src/dapi/assets_by_owner.rs | 2 +- digital_asset_types/src/dapi/common/asset.rs | 29 ++++ digital_asset_types/src/dapi/get_asset.rs | 4 +- digital_asset_types/src/dapi/search_assets.rs | 2 +- digital_asset_types/src/rpc/asset.rs | 14 ++ digital_asset_types/src/rpc/options.rs | 2 + ...18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v | Bin 0 -> 344 bytes ...kS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ | Bin 0 -> 224 bytes ...o9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 | Bin 0 -> 224 bytes ...rH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 | Bin 0 -> 400 bytes ...ixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP | Bin 0 -> 824 bytes .../tests/integration_tests/main.rs | 1 + .../show_inscription_flag_tests.rs | 46 ++++++ ...sset_with_show_inscription_scenario_1.snap | 78 ++++++++++ migration/src/lib.rs | 2 + ...0310_add_token_inscription_enum_variant.rs | 25 ++++ program_transformers/src/lib.rs | 17 ++- .../src/token_inscription/mod.rs | 59 ++++++++ .../accountsdb-plugin-config.json | 13 +- 27 files changed, 498 insertions(+), 35 deletions(-) create mode 100644 blockbuster/src/programs/token_inscriptions/mod.rs create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/4Q18N6XrfJHgDbRTaHJR328jN9dixCLQAQhDsTsRzg3v create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/9FkS3kZV4MoGps14tUSp7iVnizGbxcK4bDEhSoF5oYAZ create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/AKo9P7S8FE9NYeAcrtZEpimwQAXJMp8Lrt8p4dMkHkY2 create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/DarH4z6SmdVzPrt8krAygpLodhdjvNAstP3taj2tysN2 create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_inscription_scenario_1/HMixBLSkuhiGgVbcGhqJar476xzu1bC8wM7yHsc1iXwP create mode 100644 integration_tests/tests/integration_tests/show_inscription_flag_tests.rs create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_inscription_flag_tests__get_asset_with_show_inscription_scenario_1.snap create mode 100644 migration/src/m20241119_060310_add_token_inscription_enum_variant.rs create mode 100644 program_transformers/src/token_inscription/mod.rs 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/full_asset.rs b/digital_asset_types/src/dao/full_asset.rs index 1d901c2ce..9e5fab427 100644 --- a/digital_asset_types/src/dao/full_asset.rs +++ b/digital_asset_types/src/dao/full_asset.rs @@ -1,5 +1,7 @@ use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; +use super::asset_v1_account_attachments; + #[derive(Clone, Debug, PartialEq)] pub struct FullAsset { pub asset: asset::Model, @@ -7,6 +9,7 @@ pub struct FullAsset { 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..ea3fdc8a5 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -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::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| { @@ -287,6 +290,7 @@ pub async fn get_related_for_assets( authorities: vec![], creators: vec![], groups: vec![], + inscription: None, }; acc.insert(id, fa); }; @@ -296,7 +300,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 +338,7 @@ pub async fn get_related_for_assets( } } - let cond = if show_unverified_collections { + let cond = if options.show_unverified_collections { Condition::all() } else { Condition::any() @@ -351,6 +355,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 +387,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 +403,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,12 +411,20 @@ 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 inscription = if options.show_inscription { + get_inscription_by_mint(conn, asset_id.clone()).await.ok() + } else { + None + }; + let asset_data: (asset::Model, Option) = asset_data.one(conn).await.and_then(|o| match o { Some((a, d)) => Ok((a, d)), @@ -439,6 +464,7 @@ pub async fn get_by_id( authorities, creators, groups: grouping, + inscription, }) } @@ -711,3 +737,23 @@ pub async fn get_nft_editions( cursor, }) } +pub 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())), + }) +} 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..72d58ef04 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -8,11 +8,13 @@ use crate::rpc::options::Options; use crate::rpc::response::TokenAccountList; use crate::rpc::response::TransactionSignatureList; use crate::rpc::response::{AssetList, DasError}; +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,6 +350,7 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result 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 + }; + Ok(RpcAsset { interface: interface.clone(), id: bs58::encode(asset.id).into_string(), @@ -447,6 +475,7 @@ 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..5c333cf71 100644 --- a/digital_asset_types/src/rpc/asset.rs +++ b/digital_asset_types/src/rpc/asset.rs @@ -379,6 +379,18 @@ 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, Default)] pub struct Asset { pub interface: Interface, @@ -406,6 +418,8 @@ 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 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..af3520fc3 100644 --- a/digital_asset_types/src/rpc/options.rs +++ b/digital_asset_types/src/rpc/options.rs @@ -8,4 +8,6 @@ pub struct Options { pub show_unverified_collections: bool, #[serde(default)] pub show_zero_balance: bool, + #[serde(default)] + pub show_inscription: bool, } 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 0000000000000000000000000000000000000000..7443f1f4604775e965ce30b1ced2063442ed3dfa GIT binary patch literal 344 zcmY#jfB*@G3fHs`ZBd z_a&V))_lV7%$IA+x;lINxstcfw=EKp`|)Uw%85S@@AI#ju=}2++D?(TATt?e8?%6@ z|4<;=%PR!po4u+Gn#{<+a02cT!Mf!$a)D5HX!++-1Y~Mn&|1` z7*cU7IYEN8S1Iyxc1Sv5 za#iQRQ{h!A!A=KVHnN^L*wnW&i_uqEa=Pjii9W%T7N?ZivZ5VA9YnbqR2Ej|?4RCt S1L!ygPgg&ebxsLQ3=9DBh;%6c literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1478546cdd8b73455762623c7528974d1d668c99 GIT binary patch literal 224 zcmY#jfB*@G3LaA z|0L^IeGS$&+P{tM?ze~6Qx{IYdHSK-x!09zWVW=vjgK~;Xw~zzweCN|N|2cezG6U< zf#E+C9PH*50`Z+Tmj_K|WMBvavVj!CUgzbO3X+rJS2 zOe^}xu=-I4mowYlZx64hE}VSx^h39EuPfKcY-xQPA8kI-s^@EK-G7FaATtwu#egIO z!+$7XYUdRK^H=5vO=e_Z2m*3|6a%C3^OlL(EsG>e3psWFX&g%OQe5{)sP38R=8C`S Ood1-GEWj(^>Bq| z@|>o^ViDCD*P^+$tgExPpDTI$eA^-sxgU?_sGRuo@IL>V3A^uEs_hhc3o_H-u?CQ2 zVE7LOsXe?xApZCL6+x3385kIW_(jE8{h9BdJ^+d11651*SbtvO^;qNEns+9<9b4CX zZC`a%?Vn`*s;|M?M*FuJl~0gmWXQ{3!fhhqy*;^FCdF*kdc*(wl1>_HK4Ey~dz1}g z`Y9k~3dEVYiRr2Od8sA(MVX}qS(Qck#yQ1h*-7y^nZ+fESs4adDaA#_S-E+Y8KuP) zWmS2V$%W-0Gm;XEQ_V~m9e_;pv_t~~6GIaV<0MO?G}A-_6B7#qGxOBs)HKVa)Z|1X mLrbG1L*t~hR134Tq!d$QpmHOVR11@|c+$zkEL!@0 z-}H=#HIJ&6?6Lm5!t1fdw>9rfc00DN_u9VdsM~Qve!NP@0!*WMXLu zG87x&0tyCK<|gImfMibqaYji=L9vy-eqvF1YGPTcUS4X6exX-NR%(Dngj-sqr@L8| zX@QrnaZqtokdbS0Qo5&4M0|dUZjw=&tFu8m$e2L^6qp%7fk`M38B-V-8UH|oeNZ@j Hs5t@vd6|Gg literal 0 HcmV?d00001 diff --git a/integration_tests/tests/integration_tests/main.rs b/integration_tests/tests/integration_tests/main.rs index 62eafb887..01142197a 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -6,5 +6,6 @@ mod general_scenario_tests; mod mpl_core_tests; mod nft_editions_tests; mod regular_nft_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_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_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"] } } From 715ca79e380639d76b56bf5f6305b5c3371308cd Mon Sep 17 00:00:00 2001 From: Ahzam Akhtar <94620460+AhzamAkhtar@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:44:28 +0530 Subject: [PATCH 07/10] Filter duplicate asset id filters (#227) Users can pass in duplicate asset ids when calling getAssets. This remove the duplicates before processing the request. Co-authored-by: Kyle Espinola --- das_api/src/api/api_impl.rs | 4 + ...cGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj | Bin 0 -> 824 bytes ...AZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng | Bin 0 -> 824 bytes ...Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk | Bin 0 -> 224 bytes ...KKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X | Bin 0 -> 224 bytes .../tests/integration_tests/main.rs | 1 + ...ds__get_assets_with_multiple_same_ids.snap | 138 ++++++++++++++++++ .../test_get_assets_with_multiple_same_ids.rs | 42 ++++++ 8 files changed, 185 insertions(+) create mode 100644 integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj create mode 100644 integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng create mode 100644 integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk create mode 100644 integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap create mode 100644 integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs diff --git a/das_api/src/api/api_impl.rs b/das_api/src/api/api_impl.rs index 9aac04b2c..43bc68db7 100644 --- a/das_api/src/api/api_impl.rs +++ b/das_api/src/api/api_impl.rs @@ -218,6 +218,10 @@ impl ApiContract for DasApi { ) -> Result>, DasApiError> { let GetAssets { ids, options } = payload; + let mut ids: Vec = ids.into_iter().collect(); + ids.sort(); + ids.dedup(); + let batch_size = ids.len(); if batch_size > 1000 { return Err(DasApiError::BatchSizeExceededError); diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj new file mode 100644 index 0000000000000000000000000000000000000000..1ae41d2d6fd0535e0c662f950f965d0a9f065345 GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQl&IsMs?YYJjnSgZG5l-M)pn+sHn%t0 z_P;MWoys>@d5XIrb>riUHLezOgX*(c4xf$Kky9+S;O|z?Z5Q`2|Il>P0GYWZ6i6~K z{D%Sw8C4+=|9-c2)nrBxi-BP|69WUw->D5PF_(f?v9D|4c*Ike5+XQc!HAub-DzQe2W>l$f5Xmy?;7t)E$tR;-_tn3i3XnwgSbmQ`+) zSdyDvZj@D(ot>6xYL-}(R#cUknOu~bR8?+TQJ$7vWt?PYo>pj@o>Z1ol#11QnhG+4 z0+Uc6GNv#xF#ZK%hOKHQ4;vh$5AGMASa`SNMBk0Dbxi#SZCpfn9);w-`XoS8w+>;$ E0sCH>PXGV_ literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng new file mode 100644 index 0000000000000000000000000000000000000000..3e817c9dc76e79d97e41f6cd5f0e75fc406e9c67 GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ+|{|zujMrdr@*@x2mGX7M#Z$}G4!20 zI4ASn*)P)W={LCxQa3)nSmSCjH>f_F45zMt`HWP34f!H}Ekx zeqGD%o_R^_%&XV49_%_)7B!RI`rqd09ba$Uvt0g1MC~=tr~k`VDgX`gO-xbnNlYnL zP_{5JG5{Hk3b=q=Uq_H9LEr=sXOxr_6kF-*=O(7)05$3r8|#%8>z1b$m*^VlCFUkp z<>w`q7waYG=jvw_=jZ8z%+&+2z$OeHVhJN7Fc2A>qn;#qg(O-WI_YWjX=kxrbGNI> zomFiIEh4Y>N_V<4GBOYfX2ujoM#g`PER2kQHcR{ee3nuD+Vpfy>}tu&y6MaM!jt?? zALie@Mkal zF8?E<_L}F@|79!L?tXiCJ$2#ao2MVToqJumMrKRv+xTeniB>&dTkHNatOS{v;420s z85sUUfrN~z5Qy*IrCl|dk%1uy$OcjjY?q8#_x(C{{Iu|Uqe!-E^=Z1dPgzXjdXn{l T$?!|&X-$wCFkmD`H3I_x<*rn| literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X new file mode 100644 index 0000000000000000000000000000000000000000..eee1c395303ef302b64595898fa274079985de18 GIT binary patch literal 224 zcmY#jfB*@G3cYu4Pd{`!_quY8%$C--@zLfJt$Mz;*8OK#2{JRmR}4sk z%z^+38C4+=zoJ{aYBD1OLlBS+q!_Mu>s(#FkV)?GTe%h9lb&Cm6UTREqO6&1iG!B? RvfT$Kg4BQkBQdHO7y$L = seed_accounts([ + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj", + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ids": [ + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X" + ] + } + "#; + + let request: api::GetAssets = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets(request).await.unwrap(); + insta::assert_json_snapshot!(name, response); +} From 20c73f390906a16787bd90e8a40705e26ef86939 Mon Sep 17 00:00:00 2001 From: Ahzam Akhtar <94620460+AhzamAkhtar@users.noreply.github.com> Date: Tue, 7 Jan 2025 21:53:17 +0530 Subject: [PATCH 08/10] no pubkey validation for group_key bug fixed (#228) --- das_api/src/api/api_impl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/das_api/src/api/api_impl.rs b/das_api/src/api/api_impl.rs index 43bc68db7..99bc99eae 100644 --- a/das_api/src/api/api_impl.rs +++ b/das_api/src/api/api_impl.rs @@ -288,6 +288,7 @@ impl ApiContract for DasApi { options, cursor, } = payload; + validate_pubkey(group_value.clone())?; let before: Option = before.filter(|before| !before.is_empty()); let after: Option = after.filter(|after| !after.is_empty()); let sort_by = sort_by.unwrap_or_default(); From a3ac6e6e52f3b0b56a28fdd69debfcc52926f4ae Mon Sep 17 00:00:00 2001 From: Ahzam Akhtar <94620460+AhzamAkhtar@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:10:26 +0530 Subject: [PATCH 09/10] Optionally display collection metadata of groups (#217) When caller includes display option of show_collection_metadata asset data info will be included in the group area of the response. --- .../src/dao/extensions/asset_grouping.rs | 13 +++- digital_asset_types/src/dao/full_asset.rs | 15 +++- digital_asset_types/src/dao/scopes/asset.rs | 50 ++++++++---- digital_asset_types/src/dapi/common/asset.rs | 39 ++++++++-- digital_asset_types/src/rpc/asset.rs | 2 + digital_asset_types/src/rpc/options.rs | 2 + ...XKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK | Bin 0 -> 824 bytes ...v3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti | Bin 0 -> 312 bytes ...6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM | Bin 0 -> 224 bytes .../tests/integration_tests/main.rs | 1 + .../show_collection_metadata_option_tests.rs | 50 ++++++++++++ ..._with_show_collection_metadata_option.snap | 73 ++++++++++++++++++ 12 files changed, 222 insertions(+), 23 deletions(-) create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM create mode 100644 integration_tests/tests/integration_tests/show_collection_metadata_option_tests.rs create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap diff --git a/digital_asset_types/src/dao/extensions/asset_grouping.rs b/digital_asset_types/src/dao/extensions/asset_grouping.rs index 49b091efb..d474c63bb 100644 --- a/digital_asset_types/src/dao/extensions/asset_grouping.rs +++ b/digital_asset_types/src/dao/extensions/asset_grouping.rs @@ -1,11 +1,12 @@ use sea_orm::{EntityTrait, EnumIter, Related, RelationDef, RelationTrait}; -use crate::dao::{asset, asset_authority, asset_grouping}; +use crate::dao::{asset, asset_authority, asset_data, asset_grouping}; #[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation { Asset, AssetAuthority, + AssetData, } impl RelationTrait for Relation { @@ -19,6 +20,10 @@ impl RelationTrait for Relation { .from(asset_grouping::Column::AssetId) .to(asset_authority::Column::Id) .into(), + Self::AssetData => asset_grouping::Entity::belongs_to(asset_data::Entity) + .from(asset_grouping::Column::AssetId) + .to(asset_data::Column::Id) + .into(), } } } @@ -34,3 +39,9 @@ impl Related for asset_grouping::Entity { Relation::AssetAuthority.def() } } + +impl Related for asset_grouping::Entity { + fn to() -> RelationDef { + Relation::AssetData.def() + } +} diff --git a/digital_asset_types/src/dao/full_asset.rs b/digital_asset_types/src/dao/full_asset.rs index 9e5fab427..a75e50f8e 100644 --- a/digital_asset_types/src/dao/full_asset.rs +++ b/digital_asset_types/src/dao/full_asset.rs @@ -1,6 +1,16 @@ +use super::asset_v1_account_attachments; use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; -use super::asset_v1_account_attachments; +pub struct FullAssetGroup { + pub id: i64, + pub asset_id: Vec, + pub group_key: String, + pub group_value: Option, + pub seq: Option, + pub slot_updated: Option, + pub verified: bool, + pub group_info_seq: Option, +} #[derive(Clone, Debug, PartialEq)] pub struct FullAsset { @@ -8,9 +18,10 @@ pub struct FullAsset { pub data: Option, pub authorities: Vec, pub creators: Vec, - pub groups: Vec, pub inscription: Option, + pub groups: Vec<(asset_grouping::Model, Option)>, } + #[derive(Clone, Debug, PartialEq)] pub struct AssetRelated { pub authorities: Vec, diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index ea3fdc8a5..eff0b62fd 100644 --- a/digital_asset_types/src/dao/scopes/asset.rs +++ b/digital_asset_types/src/dao/scopes/asset.rs @@ -348,13 +348,11 @@ pub async fn get_related_for_assets( .add(asset_grouping::Column::Verified.is_null()) }; - let grouping = asset_grouping::Entity::find() + let grouping_base_query = asset_grouping::Entity::find() .filter(asset_grouping::Column::AssetId.is_in(ids.clone())) .filter(asset_grouping::Column::GroupValue.is_not_null()) .filter(cond) - .order_by_asc(asset_grouping::Column::AssetId) - .all(conn) - .await?; + .order_by_asc(asset_grouping::Column::AssetId); if options.show_inscription { let attachments = asset_v1_account_attachments::Entity::find() @@ -369,11 +367,24 @@ pub async fn get_related_for_assets( } } - for g in grouping.into_iter() { - if let Some(asset) = assets_map.get_mut(&g.asset_id) { - asset.groups.push(g); + if options.show_collection_metadata { + let combined_group_query = grouping_base_query + .find_also_related(asset_data::Entity) + .all(conn) + .await?; + for (g, a) in combined_group_query.into_iter() { + if let Some(asset) = assets_map.get_mut(&g.asset_id) { + asset.groups.push((g, a)); + } } - } + } else { + let single_group_query = grouping_base_query.all(conn).await?; + for g in single_group_query.into_iter() { + if let Some(asset) = assets_map.get_mut(&g.asset_id) { + asset.groups.push((g, None)); + } + } + }; Ok(assets_map.into_iter().map(|(_, v)| v).collect()) } @@ -445,7 +456,7 @@ pub async fn get_by_id( filter_out_stale_creators(&mut creators); - let grouping: Vec = asset_grouping::Entity::find() + let grouping_query = asset_grouping::Entity::find() .filter(asset_grouping::Column::AssetId.eq(asset.id.clone())) .filter(asset_grouping::Column::GroupValue.is_not_null()) .filter( @@ -455,16 +466,29 @@ pub async fn get_by_id( // Therefore if verified is null, we can assume that the group is verified. .add(asset_grouping::Column::Verified.is_null()), ) - .order_by_asc(asset_grouping::Column::AssetId) - .all(conn) - .await?; + .order_by_asc(asset_grouping::Column::AssetId); + + let groups = if options.show_collection_metadata { + grouping_query + .find_also_related(asset_data::Entity) + .all(conn) + .await? + } else { + grouping_query + .all(conn) + .await? + .into_iter() + .map(|g| (g, None)) + .collect::>() + }; + Ok(FullAsset { asset, data, authorities, creators, - groups: grouping, inscription, + groups, }) } diff --git a/digital_asset_types/src/dapi/common/asset.rs b/digital_asset_types/src/dapi/common/asset.rs index 72d58ef04..a3716635a 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -308,25 +308,50 @@ pub fn to_creators(creators: Vec) -> Vec { } pub fn to_grouping( - groups: Vec, + groups: Vec<(asset_grouping::Model, Option)>, options: &Options, ) -> Result, DbErr> { let result: Vec = groups .iter() - .filter_map(|model| { + .filter_map(|(asset_group, asset_data)| { let verified = match options.show_unverified_collections { // Null verified indicates legacy data, meaning it is verified. - true => Some(model.verified), + true => Some(asset_group.verified), false => None, }; // Filter out items where group_value is None. - model.group_value.clone().map(|group_value| Group { - group_key: model.group_key.clone(), - group_value: Some(group_value), - verified, + asset_group.group_value.clone().map(|group_value| { + let collection_metadata = asset_data.as_ref().map(|data| { + let mut metadata_selector_fn = jsonpath_lib::selector(&data.metadata); + let metadata_selector = &mut metadata_selector_fn; + let mut meta: MetadataMap = MetadataMap::new(); + + if let Some(name) = safe_select(metadata_selector, "$.name") { + meta.set_item("name", name.clone()); + } + if let Some(symbol) = safe_select(metadata_selector, "$.symbol") { + meta.set_item("symbol", symbol.clone()); + } + if let Some(image) = safe_select(metadata_selector, "$.image") { + meta.set_item("image", image.clone()); + } + if let Some(external_url) = safe_select(metadata_selector, "$.external_url") { + meta.set_item("external_url", external_url.clone()); + } + + meta + }); + + Group { + group_key: asset_group.group_key.clone(), + group_value: Some(group_value), + verified, + collection_metadata, + } }) }) .collect(); + Ok(result) } diff --git a/digital_asset_types/src/rpc/asset.rs b/digital_asset_types/src/rpc/asset.rs index 5c333cf71..4521cf7e1 100644 --- a/digital_asset_types/src/rpc/asset.rs +++ b/digital_asset_types/src/rpc/asset.rs @@ -232,6 +232,8 @@ pub struct Group { pub group_value: Option, #[serde(skip_serializing_if = "Option::is_none")] pub verified: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub collection_metadata: Option, } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] diff --git a/digital_asset_types/src/rpc/options.rs b/digital_asset_types/src/rpc/options.rs index af3520fc3..23c4b9f0b 100644 --- a/digital_asset_types/src/rpc/options.rs +++ b/digital_asset_types/src/rpc/options.rs @@ -7,6 +7,8 @@ pub struct Options { #[serde(default)] pub show_unverified_collections: bool, #[serde(default)] + pub show_collection_metadata: bool, + #[serde(default)] pub show_zero_balance: bool, #[serde(default)] pub show_inscription: bool, diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK new file mode 100644 index 0000000000000000000000000000000000000000..3d17826b4be4eedb2a8d76807b9103247cb8f8ce GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQOlGO` z@E;0(HP06U@pn#ZbeO^jVlgl*XJTMr5w)Jl@wvwOQNfY|nfd2cjQ&P#o5~@dZ{TBY z{JNIiJ+t$skp$x=8HdDx9ak=~ZeQ-d^zF{p)~N7=P1j}AO&?n+01fg@Oi}PjOet1S zHng-b1{sYCxPV+=N028$-~y;MkmZuh%=o;xI<|bC< z=OvaG>m}#s>Sq<_=jnsY)dR7>CJY{82_qvg5E-1Ko+NmMBw8Fg>1p(7XR%##x2wsW zRc!|?BCqyJce*k%G7t)8#uP?I#{Y~gjEsLaOZ)$PmQnrM^mI+^YRSvG>C5`Ull)E} z=HI+VCWa%9fq{{Mk+V&y^>HEBgsZai+?nppntS!--xF&ZFI`%5@!{PQD*uiQo)Dsv FO914prz`*f literal 0 HcmV?d00001 diff --git a/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti b/integration_tests/tests/data/accounts/get_asset_with_show_collection_metadata_option/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti new file mode 100644 index 0000000000000000000000000000000000000000..5079e439756af63f8316b9d97dc346b562e7c4cc GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ%ztaDbFVke^QO+S9HEuhcs#SZ6V6T8 z_;3DN6VAv9PV?FBetUR5b>ZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW)Bu|CK~^3} zGBEsyf?v(^g+Tn!>5UFk7(pxshNVEG{ta0$PuA=FQnBAhY28uei5wpZ>jk T;n|bjouwc(V8BR>Y6b=X = 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" : { + "showCollectionMetadata": 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_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap new file mode 100644 index 000000000..4fd990230 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__show_collection_metadata_option_tests__get_asset_with_show_collection_metadata_option.snap @@ -0,0 +1,73 @@ +--- +source: integration_tests/tests/integration_tests/show_collection_metadata_option_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", + "collection_metadata": {} + } + ], + "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": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9" + }, + "mutable": true, + "burnt": false +} From 3e4fb88be7bc310fc402b4d58cd55183af42aa1f Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:02:44 +0530 Subject: [PATCH 10/10] Show Fungibles Filter (#215) --- .../src/dao/extensions/asset.rs | 12 + digital_asset_types/src/dao/extensions/mod.rs | 1 + .../src/dao/extensions/token_accounts.rs | 25 ++ digital_asset_types/src/dao/full_asset.rs | 6 +- digital_asset_types/src/dao/scopes/asset.rs | 51 +++- digital_asset_types/src/dapi/common/asset.rs | 62 ++-- digital_asset_types/src/rpc/asset.rs | 13 + digital_asset_types/src/rpc/options.rs | 2 + ...XKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK | Bin 0 -> 824 bytes ...v3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti | Bin 0 -> 312 bytes ...6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM | Bin 0 -> 224 bytes ...XKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK | Bin 0 -> 824 bytes ...v3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti | Bin 0 -> 312 bytes ...6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM | Bin 0 -> 224 bytes ...XKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK | Bin 0 -> 824 bytes ...v3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti | Bin 0 -> 312 bytes ...6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM | Bin 0 -> 224 bytes ...RNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx | Bin 0 -> 312 bytes ...ajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 | Bin 0 -> 824 bytes ...YnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs | Bin 0 -> 224 bytes ...6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 | Bin 0 -> 824 bytes ...yaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC | Bin 0 -> 312 bytes ...84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw | Bin 0 -> 224 bytes ...XKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK | Bin 0 -> 824 bytes ...v3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti | Bin 0 -> 312 bytes ...6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM | Bin 0 -> 224 bytes ...1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo | Bin 0 -> 1008 bytes ...n5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX | Bin 0 -> 824 bytes ...toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn | Bin 0 -> 224 bytes ...KocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 | Bin 0 -> 224 bytes ...KocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 | Bin 0 -> 224 bytes .../fungibles_and_token_extensions_tests.rs | 17 +- .../tests/integration_tests/main.rs | 1 + .../show_fungible_flag_tests.rs | 269 ++++++++++++++++++ ...asset_by_authority_with_show_fungible.snap | 86 ++++++ ...t_asset_by_creator_with_show_fungible.snap | 86 ++++++ ...get_asset_by_owner_with_show_fungible.snap | 86 ++++++ ...t_asset_with_show_fungible_scenario_1.snap | 79 +++++ ...t_asset_with_show_fungible_scenario_2.snap | 79 +++++ ...t_asset_with_show_fungible_scenario_3.snap | 202 +++++++++++++ ...t_asset_with_show_fungible_scenario_4.snap | 62 ++++ 41 files changed, 1095 insertions(+), 44 deletions(-) create mode 100644 digital_asset_types/src/dao/extensions/token_accounts.rs create mode 100644 integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK create mode 100644 integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti create mode 100644 integration_tests/tests/data/accounts/get_asset_by_authority_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM create mode 100644 integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK create mode 100644 integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti create mode 100644 integration_tests/tests/data/accounts/get_asset_by_creator_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM create mode 100644 integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK create mode 100644 integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti create mode 100644 integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM create mode 100644 integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/6BRNfDfdq1nKyU1TQiCEQLWyPtD8EwUH9Kt2ahsbidUx create mode 100644 integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7BajpcYgnxmWK91RhrfsdB3Tm83PcDwPvMC8ZinvtTY6 create mode 100644 integration_tests/tests/data/accounts/get_asset_by_owner_with_show_fungible_scenario/7EYnhQoR9YM3N7UoaKRoA44Uy8JeaZV3qyouov87awMs create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/7z6b5TE4WX4mgcQjuNBTDxK4SE75sbgEg5WWJwoUeie8 create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/8myaCN6KcKVkMqroXuLJq6QsqRcPbvme4wV5Ubfr5mDC create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_1/Ca84nWhQu41DMRnjdhRrLZty1i9txepMhAhz5qLLGcBw create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_2/AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_3/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/8yn5oqFMwYA8SgGqWwKq1Hia8aM5gh1DWmHEL34hMqBX create mode 100644 integration_tests/tests/data/accounts/get_asset_with_show_fungible_scenario_4/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn create mode 100644 integration_tests/tests/data/accounts/token_extension_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 create mode 100644 integration_tests/tests/data/accounts/token_keg_fungible_with_no_metadata/wKocBVvHQoVaiwWoCs9JYSVye4YZRrv5Cucf7fDqnz1 create mode 100644 integration_tests/tests/integration_tests/show_fungible_flag_tests.rs create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_authority_with_show_fungible.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_creator_with_show_fungible.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_by_owner_with_show_fungible.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_1.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_2.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_3.snap create mode 100644 integration_tests/tests/integration_tests/snapshots/integration_tests__show_fungible_flag_tests__get_asset_with_show_fungible_scenario_4.snap 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 a75e50f8e..b80a1f1e6 100644 --- a/digital_asset_types/src/dao/full_asset.rs +++ b/digital_asset_types/src/dao/full_asset.rs @@ -1,6 +1,7 @@ -use super::asset_v1_account_attachments; +use super::{asset_v1_account_attachments, tokens}; use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping}; +#[derive(Clone, Debug, PartialEq)] pub struct FullAssetGroup { pub id: i64, pub asset_id: Vec, @@ -15,7 +16,8 @@ pub struct FullAssetGroup { #[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 inscription: Option, diff --git a/digital_asset_types/src/dao/scopes/asset.rs b/digital_asset_types/src/dao/scopes/asset.rs index eff0b62fd..c241c4c4e 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, @@ -161,7 +161,7 @@ pub async fn get_assets_by_owner( 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( @@ -286,11 +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); }; @@ -338,6 +339,17 @@ pub async fn get_related_for_assets( } } + 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 { @@ -436,13 +448,21 @@ pub async fn get_by_id( None }; - let asset_data: (asset::Model, Option) = + 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) @@ -489,6 +509,7 @@ pub async fn get_by_id( creators, inscription, groups, + token_info, }) } @@ -655,11 +676,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 @@ -761,7 +782,8 @@ pub async fn get_nft_editions( cursor, }) } -pub async fn get_inscription_by_mint( + +async fn get_inscription_by_mint( conn: &impl ConnectionTrait, mint: Vec, ) -> Result { @@ -781,3 +803,14 @@ pub async fn get_inscription_by_mint( _ => 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/common/asset.rs b/digital_asset_types/src/dapi/common/asset.rs index a3716635a..328970492 100644 --- a/digital_asset_types/src/dapi/common/asset.rs +++ b/digital_asset_types/src/dapi/common/asset.rs @@ -8,6 +8,7 @@ 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, @@ -376,36 +377,32 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Some(MplCoreInfo { @@ -441,6 +438,22 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result Result, } +#[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, @@ -422,6 +433,8 @@ pub struct Asset { #[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 23c4b9f0b..e1a57f9ce 100644 --- a/digital_asset_types/src/rpc/options.rs +++ b/digital_asset_types/src/rpc/options.rs @@ -12,4 +12,6 @@ pub struct Options { 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 0000000000000000000000000000000000000000..a4776424f52d8ccd1eef3760f0a628e44823a6f9 GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQOlGO` z@E;1qG%JNbd?6W8<|&LI76Zd_CI$u;QR|r;pKGii6)Y)`nSWlz=x@}vsT}h820rG- zuWQ-eGdph@Nic4baYzi^s$u!&>-K$6a}Belwt*C zLrV)|kkP1s3&{0#1bGq!P5^O6Nl8JmmA-y%VoDBBqh7JGUTLvzd1`Tqu9041Zemq_ zUSfH%UUGh}epYdQo<7K2JrE0Q!r&p6FfsxIk-<6YNrG2MqQ#+;o<^T`7TYy^J-VmRU$7#JBCIop(49~W{>xGFo(o$21JxmREQJ+Y?o(xo*QAKpEo^6$vt2_Y)E F1OPU)qy7K@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1470bf74a12e69e60a77de1400c6c7b3974f3b97 GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ%ztaDbFVke^QO+S9HEuhcs#SZ6V6T8 z_;3DN6VAv9PV?FBetUR5b>ZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW)Bu|CK~^3} zGBEsy0twAZArOD5j41OIMi2|g2I9_}MiPvhWE>I$c3io{x_!C-(ziQXTcg4gHeHud zH+^jN+U4VNv6pXaHQL$Jv=Wzp3RY3t$ELbSxZ~^VHlyXDLVx7%&o}nt=fT4WL>V literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..eee1c6f44f9f406ba88a89b65180507df0639266 GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQOlGO` z@E;0nv?_(b0>8zWr!az949l4q7+6HDXL5Y5v3^vrq(El=c@?9-QQM|+$mbjQm>a*Y zWp~f)ylEuCxJkw#F<{4)ORU?M`!9XFv$ZuUJYmyy8FkagRti9~d=pa?d=gWN6_gDv zEsPVpHI5)pg1`wN&L}Ayig(vx)KFq&) zjZ6$j90LO*10!dfQtRVFt_fFV=eaZ8n>F|9%fBbqG+w&2=HkPZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW)Bu|CK~^3} zGBEsy0voMLArOD*Z!zX6j35?}4aA){jU*U1$v7ki?6`7?b^CJvrEhn(wnl{~Y`QL^ zZu;2jwadrlVlUs;YP7SbX(cZI6s)4Ok4<%vaL3ozZARtgT~MPKK=dqU?$Z(G3kr|O mO` z@E-~+wJL=`{KsF#n5Qs;SPTrynHU&YM6G9Xe6F#6RIsE#X8w5&+NQuB*C~z#vw6a$CXR0+n4(-@H7Yz|({&kj)5lf{K!bb}Qxtp>Q;HRo z4J|E<*CIbx<-15xrtTz zd5Pu4ddc~@`dP*KdHNu8^*}7J34@1N!pH~=LZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW)Bu|CK~^3} zGBEsy0xPXbArL?Ln;7#HMi7gEVJQ%I-ZYY6+$7_W7_j5YCD!fB{g=Mo+1eTvp0Me< zjJoM#tJf|cmy5l8TdUE|o~D(!{8O-s(mpoTMZz6lU$+^Rmv=#pVgS*zoVia&m@gXDLVx7%&o}nt=fTT?1O` literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..035a8fc7eb66e42ec8ed229a9c6ca806c5590f51 GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ^!a+$_0g5xjz_QDue^WuW!dxf{hF%2 zHoA{B?^T}N_F*mC-ER-Cr!Jg)^YlZvbFVAc$ZTnS8y{^x(W>WbYu$f_l^Q@ZKFG=g zNd|`hP@tw&ECk}$TIK$n%m`vJFf0Y)n0a&K7jw3Govm$@xU6PqbFaGXzQn`nlf0NE z^8d}Zt&%!#nz%OPZ=RdVX|D&>|DO4skZJq8<)c^+XGh|Tg%a8f-wNc!z@`xEX`>c+bCn552sJ^VwTANH{Z5O0ccQgW=>9}f=f|i zdVU_rV06F*6bSb8@reXWoB)y;B_##LR{Hwsi6yD!iIsYpMU};R6_r)`0TvNvCI#-L zC6y7Tsg)rGfw_fIMaCW`@jitaxdxt*6-B1Ol~rzTeyQ2$whR!Tk&*EqBQPXK0WCuS E0EE78Hvj+t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..07a90f4b0710003caf51f95ec025031b1460aa24 GIT binary patch literal 224 zcmY#jfB*@G34$FTURSP>+0yzpKH7YuRnOPfy8jF-L1zB1-nCp4 zL_>k9RP=7D W*V*pzo2|MdAvog!FDFnBF#rG!K}l%< literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7670328beb682cce5d5e745d6c627eb805b56488 GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQOh4AW?Bj_UyN@oez3Sn(xp3iv=D@nr zpR>-ZXf-(K{!HaANZt7OVvVcC+@ShwmcwTwcH|UGE%>|DbKAu|%s({UG(cu<2?df2 z4F91(vX@H;#6Pz#(|0l>h{eFLoQZ*fWt!&3**w;(8efq4Gk>KK&B%BE+E^*)6X9wbOK0al#~<{Tj}fPrIi$y4ATmr`lX2;R`Rbj#F-Go%^90UX%&p${r&8HYBx{mHu#JNX zBZIu3JE0-!qv56~+g%mtXC@z<6%{$NU!u7#J8C7&+UNS|1m3O}HvM&zcYu4Pd{`!_quY8%$C--@zLfJt$Mz;*8OK#sR1X CX;jDn literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f273bd7c1b556bfff93a99d3feeb2d4a848aa3db GIT binary patch literal 224 zcmY#jfB*@G3WO7wsC`8y?J81W4$FTURSP>+0yzpKH7YuRnOPfy8jF-L1rfSiUCOm zhW}6?*~=va;?G@|={uQ`fguRU22u>?r2?9-F)8FvbgH=ay6k~u;a-7vHiwE$!`_^@ SrEorX2S^PVFcPDhfdK#+8B?49 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..de4e24d36a9d687132dd70bda212e8b157db469f GIT binary patch literal 824 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQOlGO` z@E;1Kdbxx^{P4}0zLOb2ECz<AH-%>0>Jeph3QgDGEM`Da8uP zhL#q_Afr(M7m(}g2=XKdoB-mCl9GaAD}DXk#FQMMM!jNVz0zXc^3>uIT_e53+{CK< zyu|Wiz2y8{{jB2rJbjS4dLS0qguz2BVPpgbB7<|(lLW7jM2kZwJ&iu?EVgUzb~U-P zs_mdfcbFaSqdty!FrAuorKD>KE<=>IP6GBvS F2>>GrrEmZM literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3ffe5ac43105262bc1150bf5b3c911ed3ad51384 GIT binary patch literal 312 zcmY#jfB*@G90nE!4+a$=H-NzfNJap00)qsQ%ztaDbFVke^QO+S9HEuhcs#SZ6V6T8 z_;3DN6VAv9PV?FBetUR5b>ZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW)Bu|CK~^3} zGBEsyf|b|hg+Tn5!Lfyt89^)thNVE)Cj;lzOB@gH%w+;u3Ii~WFg^hq09DUno&W#< literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4922084e34595c8e872697eecac9e2a03394cf46 GIT binary patch literal 224 zcmY#jfB*@G3XDLVx7%&o}nt=fTL1brr2hCe@8nhgxA&*UjfLHRKxXcE^~(!L zG5m)DiMBaHApXUM9oADA85ok785n>R1HXTQs@5A}CU1T9UcYD0gN#>mX(`{~nk!rU zEaJ?(U9*b<=Q794WME)p1gREpHA;M+wIf(7{X=kD)LKJL`}6GQg}AovTo}$4(tAJ~ zWCN7}BQt{nL3??KP{YWOL(m*z{TIuG?n_J&76wOx_Mw}C!53g?Bxs0?`7>whVwSs2 z#Z^yuXLo--GC!Ix$8*J1V~x#E*(S(@gCdS1z{lV~&=G_z5CWRfR3d7uymj{byzf0B zc?vm>9o19Rb5q_g==b=1y~+B;4MOQ&m|-2oW^e%=8jx5SkeH(o8tlRf;za@({y;`X zNl8JmmA-ySes*e}ZfriUHLezOgX*(c4xf$Kky9+S;O|z?Z5Q`2|Il>P0GYWZ6i6~K z{D*>=pc)|%pG{DiV+tdP#lWzfiGhLTh!3}OQ^5Vrx3iX;zFYi-BZOJxs;(gmyXu7xl*A1 zsZZjryshVdtJFkW=rea_-ONqRy|)^>;5yW1euxJ5Mu|V z82&?nN821B5Wo7=4(ln53=Ba)HjrWnc{)$!K;VjnUe$8br+;{;bAP!flUSs-NKbc(mR7`=hC+=4P}x#(pf|eJ?fZQNb0p WEWehSa~TAdUU>mj#|2VM1ONc8Ax4Y< literal 0 HcmV?d00001 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 0311e6bcb..2e66f7e9e 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -7,6 +7,7 @@ mod mpl_core_tests; mod nft_editions_tests; mod regular_nft_tests; mod show_collection_metadata_option_tests; +mod show_fungible_flag_tests; mod show_inscription_flag_tests; mod test_get_assets_with_multiple_same_ids; mod test_show_zero_balance_filter; 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/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" + } +}