Skip to content

Commit

Permalink
Index Token Extensions and Fungible Tokens (#214)
Browse files Browse the repository at this point in the history
* 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 <niks3089@gmail.com>
Co-authored-by: Kyle Espinola <kyle.s.espinola@gmail.com>
  • Loading branch information
3 people authored Jan 6, 2025
1 parent fd9266a commit d914e4b
Show file tree
Hide file tree
Showing 79 changed files with 1,364 additions and 210 deletions.
25 changes: 13 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions blockbuster/src/programs/token_extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,42 @@ pub struct MintAccountExtensions {
pub token_group_member: Option<ShadowTokenGroupMember>,
}

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<ShadowConfidentialTransferAccount>,
pub cpi_guard: Option<ShadowCpiGuard>,
pub memo_transfer: Option<ShadowMemoTransfer>,
pub transfer_fee_amount: Option<ShadowTransferFeeAmount>,
}

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,
Expand Down
3 changes: 2 additions & 1 deletion digital_asset_types/src/dao/extensions/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion digital_asset_types/src/dao/full_asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<asset_data::Model>,
pub authorities: Vec<asset_authority::Model>,
pub creators: Vec<asset_creators::Model>,
pub groups: Vec<asset_grouping::Model>,
Expand Down
3 changes: 3 additions & 0 deletions digital_asset_types/src/dao/generated/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct Model {
pub owner_delegate_seq: Option<i64>,
pub leaf_seq: Option<i64>,
pub base_info_seq: Option<i64>,
pub mint_extensions: Option<Json>,
pub mpl_core_plugins: Option<Json>,
pub mpl_core_unknown_plugins: Option<Json>,
pub mpl_core_collection_num_minted: Option<i32>,
Expand Down Expand Up @@ -93,6 +94,7 @@ pub enum Column {
OwnerDelegateSeq,
LeafSeq,
BaseInfoSeq,
MintExtensions,
MplCorePlugins,
MplCoreUnknownPlugins,
MplCoreCollectionNumMinted,
Expand Down Expand Up @@ -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(),
Expand Down
3 changes: 3 additions & 0 deletions digital_asset_types/src/dao/generated/token_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct Model {
pub delegated_amount: i64,
pub slot_updated: i64,
pub token_program: Vec<u8>,
pub extensions: Option<Json>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
Expand All @@ -38,6 +39,7 @@ pub enum Column {
DelegatedAmount,
SlotUpdated,
TokenProgram,
Extensions,
}

#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
Expand Down Expand Up @@ -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(),
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions digital_asset_types/src/dao/generated/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct Model {
pub close_authority: Option<Vec<u8>>,
pub extension_data: Option<Vec<u8>>,
pub slot_updated: i64,
pub extensions: Option<Json>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
Expand All @@ -36,6 +37,7 @@ pub enum Column {
CloseAuthority,
ExtensionData,
SlotUpdated,
Extensions,
}

#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
Expand Down Expand Up @@ -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(),
}
}
}
Expand All @@ -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()
}
}
6 changes: 3 additions & 3 deletions digital_asset_types/src/dao/scopes/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![],
Expand Down Expand Up @@ -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::Model>) =
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())),
})?;

Expand Down
70 changes: 35 additions & 35 deletions digital_asset_types/src/dapi/common/asset.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -281,14 +280,8 @@ pub fn v1_content_from_json(asset_data: &asset_data::Model) -> Result<Content, D
})
}

pub fn get_content(asset: &asset::Model, data: &asset_data::Model) -> Result<Content, DbErr> {
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<Content> {
v1_content_from_json(data).ok()
}

pub fn to_authority(authority: Vec<asset_authority::Model>) -> Vec<Authority> {
Expand Down Expand Up @@ -337,10 +330,7 @@ pub fn to_grouping(

pub fn get_interface(asset: &asset::Model) -> Result<Interface, DbErr> {
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()
Expand All @@ -363,14 +353,32 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result<RpcAsset, DbE
let rpc_creators = to_creators(creators);
let rpc_groups = to_grouping(groups, options)?;
let interface = get_interface(&asset)?;
let content = get_content(&asset, &data)?;
let mut chain_data_selector_fn = jsonpath_lib::selector(&data.chain_data);
let chain_data_selector = &mut chain_data_selector_fn;
let basis_points = safe_select(chain_data_selector, "$.primary_sale_happened")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let edition_nonce =
safe_select(chain_data_selector, "$.edition_nonce").and_then(|v| v.as_u64());

let (content, edition_nonce, basis_points, mutable, uses) = if let Some(data) = &data {
let mut chain_data_selector_fn = jsonpath_lib::selector(&data.chain_data);
let chain_data_selector = &mut chain_data_selector_fn;
(
get_content(data),
safe_select(chain_data_selector, "$.edition_nonce").and_then(|v| v.as_u64()),
safe_select(chain_data_selector, "$.primary_sale_happened")
.and_then(|v| v.as_bool())
.unwrap_or(false),
data.chain_data_mutability.clone().into(),
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),
}),
)
} else {
(None, None, false, false, None)
};

let mpl_core_info = match interface {
Interface::MplCoreAsset | Interface::MplCoreCollection => Some(MplCoreInfo {
num_minted: asset.mpl_core_collection_num_minted,
Expand All @@ -383,9 +391,9 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result<RpcAsset, DbE
Ok(RpcAsset {
interface: interface.clone(),
id: bs58::encode(asset.id).into_string(),
content: Some(content),
content,
authorities: Some(rpc_authorities),
mutable: data.chain_data_mutability.into(),
mutable,
compression: Some(Compression {
eligible: asset.compressible,
compressed: asset.compressed,
Expand Down Expand Up @@ -418,7 +426,7 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result<RpcAsset, DbE
locked: false,
}),
creators: Some(rpc_creators),
ownership: Ownership {
ownership: Some(Ownership {
frozen: asset.frozen,
delegated: asset.delegate.is_some(),
delegate: asset.delegate.map(|s| bs58::encode(s).into_string()),
Expand All @@ -427,7 +435,7 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result<RpcAsset, DbE
.owner
.map(|o| bs58::encode(o).into_string())
.unwrap_or("".to_string()),
},
}),
supply: match interface {
Interface::V1NFT => Some(Supply {
edition_nonce,
Expand All @@ -436,17 +444,9 @@ pub fn asset_to_rpc(asset: FullAsset, options: &Options) -> Result<RpcAsset, DbE
}),
_ => 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,
Expand Down
Loading

0 comments on commit d914e4b

Please sign in to comment.