Skip to content

Commit ee05799

Browse files
authored
add missing indexing delete reousrces (#475)
1 parent 6b0c7ce commit ee05799

File tree

2 files changed

+399
-49
lines changed

2 files changed

+399
-49
lines changed

rust/processor/src/db/common/models/token_v2_models/parquet_v2_token_ownerships.rs

Lines changed: 279 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ use crate::{
99
bq_analytics::generic_parquet_processor::{GetTimeStamp, HasVersion, NamedTable},
1010
db::common::models::{
1111
fungible_asset_models::parquet_v2_fungible_asset_balances::DEFAULT_AMOUNT_VALUE,
12-
object_models::v2_object_utils::ObjectAggregatedDataMapping,
12+
object_models::v2_object_utils::{ObjectAggregatedDataMapping, ObjectWithMetadata},
1313
token_models::{token_utils::TokenWriteSet, tokens::TableHandleToOwner},
1414
token_v2_models::{
15-
parquet_v2_token_datas::TokenDataV2, v2_token_ownerships::CurrentTokenOwnershipV2,
16-
v2_token_utils::TokenStandard,
15+
parquet_v2_token_datas::TokenDataV2,
16+
v2_token_ownerships::{CurrentTokenOwnershipV2, NFTOwnershipV2},
17+
v2_token_utils::{TokenStandard, TokenV2Burned, DEFAULT_OWNER_ADDRESS},
1718
},
1819
},
1920
utils::util::{ensure_not_negative, standardize_address},
2021
};
22+
use ahash::AHashMap;
2123
use allocative_derive::Allocative;
2224
use anyhow::Context;
23-
use aptos_protos::transaction::v1::{DeleteTableItem, WriteTableItem};
25+
use aptos_protos::transaction::v1::{
26+
DeleteResource, DeleteTableItem, WriteResource, WriteTableItem,
27+
};
2428
use bigdecimal::{BigDecimal, ToPrimitive, Zero};
2529
use field_count::FieldCount;
2630
use parquet_derive::ParquetRecordWriter;
@@ -72,7 +76,6 @@ impl TokenOwnershipV2 {
7276
object_metadatas: &ObjectAggregatedDataMapping,
7377
) -> anyhow::Result<Vec<Self>> {
7478
let mut ownerships = vec![];
75-
// let mut current_ownerships = AHashMap::new();
7679

7780
let object_data = object_metadatas
7881
.get(&token_data.token_data_id)
@@ -136,14 +139,76 @@ impl TokenOwnershipV2 {
136139
Ok(ownerships)
137140
}
138141

142+
async fn get_burned_nft_v2_helper(
143+
token_address: &str,
144+
txn_version: i64,
145+
write_set_change_index: i64,
146+
txn_timestamp: chrono::NaiveDateTime,
147+
prior_nft_ownership: &AHashMap<String, NFTOwnershipV2>,
148+
tokens_burned: &TokenV2Burned,
149+
) -> anyhow::Result<Option<(Self, CurrentTokenOwnershipV2)>> {
150+
let token_address = standardize_address(token_address);
151+
if let Some(burn_event) = tokens_burned.get(&token_address) {
152+
// 1. Try to lookup token address in burn event mapping
153+
let previous_owner =
154+
if let Some(previous_owner) = burn_event.get_previous_owner_address() {
155+
previous_owner
156+
} else {
157+
// 2. If it doesn't exist in burn event mapping, then it must be an old burn event that doesn't contain previous_owner.
158+
// Do a lookup to get previous owner. This is necessary because previous owner is part of current token ownerships primary key.
159+
match prior_nft_ownership.get(&token_address) {
160+
Some(inner) => inner.owner_address.clone(),
161+
None => DEFAULT_OWNER_ADDRESS.to_string(),
162+
}
163+
};
164+
165+
let token_data_id = token_address.clone();
166+
let storage_id = token_data_id.clone();
167+
168+
return Ok(Some((
169+
Self {
170+
txn_version,
171+
write_set_change_index,
172+
token_data_id: token_data_id.clone(),
173+
property_version_v1: LEGACY_DEFAULT_PROPERTY_VERSION,
174+
owner_address: Some(previous_owner.clone()),
175+
storage_id: storage_id.clone(),
176+
amount: DEFAULT_AMOUNT_VALUE.clone(),
177+
table_type_v1: None,
178+
token_properties_mutated_v1: None,
179+
is_soulbound_v2: None, // default
180+
token_standard: TokenStandard::V2.to_string(),
181+
block_timestamp: txn_timestamp,
182+
non_transferrable_by_owner: None, // default
183+
},
184+
CurrentTokenOwnershipV2 {
185+
token_data_id,
186+
property_version_v1: BigDecimal::zero(),
187+
owner_address: previous_owner,
188+
storage_id,
189+
amount: BigDecimal::zero(),
190+
table_type_v1: None,
191+
token_properties_mutated_v1: None,
192+
is_soulbound_v2: None, // default
193+
token_standard: TokenStandard::V2.to_string(),
194+
is_fungible_v2: None, // default
195+
last_transaction_version: txn_version,
196+
last_transaction_timestamp: txn_timestamp,
197+
non_transferrable_by_owner: None, // default
198+
},
199+
)));
200+
}
201+
Ok(None)
202+
}
203+
139204
/// We want to track tokens in any offer/claims and tokenstore
140205
pub fn get_v1_from_delete_table_item(
141206
table_item: &DeleteTableItem,
142207
txn_version: i64,
143208
write_set_change_index: i64,
144209
txn_timestamp: chrono::NaiveDateTime,
145210
table_handle_to_owner: &TableHandleToOwner,
146-
) -> anyhow::Result<Option<Self>> {
211+
) -> anyhow::Result<Option<(Self, Option<CurrentTokenOwnershipV2>)>> {
147212
let table_item_data = table_item.data.as_ref().unwrap();
148213

149214
let maybe_token_id = match TokenWriteSet::from_table_item_type(
@@ -161,7 +226,7 @@ impl TokenOwnershipV2 {
161226
let token_data_id = token_data_id_struct.to_id();
162227

163228
let maybe_table_metadata = table_handle_to_owner.get(&table_handle);
164-
let (_, owner_address, table_type) = match maybe_table_metadata {
229+
let (curr_token_ownership, owner_address, table_type) = match maybe_table_metadata {
165230
Some(tm) => {
166231
if tm.table_type != "0x3::token::TokenStore" {
167232
return Ok(None);
@@ -190,21 +255,24 @@ impl TokenOwnershipV2 {
190255
None => (None, None, None),
191256
};
192257

193-
Ok(Some(Self {
194-
txn_version,
195-
write_set_change_index,
196-
token_data_id,
197-
property_version_v1: token_id_struct.property_version.to_u64().unwrap(),
198-
owner_address,
199-
storage_id: table_handle,
200-
amount: DEFAULT_AMOUNT_VALUE.clone(),
201-
table_type_v1: table_type,
202-
token_properties_mutated_v1: None,
203-
is_soulbound_v2: None,
204-
token_standard: TokenStandard::V1.to_string(),
205-
block_timestamp: txn_timestamp,
206-
non_transferrable_by_owner: None,
207-
}))
258+
Ok(Some((
259+
Self {
260+
txn_version,
261+
write_set_change_index,
262+
token_data_id,
263+
property_version_v1: token_id_struct.property_version.to_u64().unwrap(),
264+
owner_address,
265+
storage_id: table_handle,
266+
amount: DEFAULT_AMOUNT_VALUE.clone(),
267+
table_type_v1: table_type,
268+
token_properties_mutated_v1: None,
269+
is_soulbound_v2: None,
270+
token_standard: TokenStandard::V1.to_string(),
271+
block_timestamp: txn_timestamp,
272+
non_transferrable_by_owner: None,
273+
},
274+
curr_token_ownership,
275+
)))
208276
} else {
209277
Ok(None)
210278
}
@@ -217,7 +285,7 @@ impl TokenOwnershipV2 {
217285
write_set_change_index: i64,
218286
txn_timestamp: chrono::NaiveDateTime,
219287
table_handle_to_owner: &TableHandleToOwner,
220-
) -> anyhow::Result<Option<Self>> {
288+
) -> anyhow::Result<Option<(Self, Option<CurrentTokenOwnershipV2>)>> {
221289
let table_item_data = table_item.data.as_ref().unwrap();
222290

223291
let maybe_token = match TokenWriteSet::from_table_item_type(
@@ -237,36 +305,204 @@ impl TokenOwnershipV2 {
237305
let token_data_id = token_data_id_struct.to_id();
238306

239307
let maybe_table_metadata = table_handle_to_owner.get(&table_handle);
240-
let (owner_address, table_type) = match maybe_table_metadata {
308+
let (curr_token_ownership, owner_address, table_type) = match maybe_table_metadata {
241309
Some(tm) => {
242310
if tm.table_type != "0x3::token::TokenStore" {
243311
return Ok(None);
244312
}
245313
let owner_address = tm.get_owner_address();
246-
(Some(owner_address), Some(tm.table_type.clone()))
314+
(
315+
Some(CurrentTokenOwnershipV2 {
316+
token_data_id: token_data_id.clone(),
317+
property_version_v1: token_id_struct.property_version.clone(),
318+
owner_address: owner_address.clone(),
319+
storage_id: table_handle.clone(),
320+
amount: amount.clone(),
321+
table_type_v1: Some(tm.table_type.clone()),
322+
token_properties_mutated_v1: Some(token.token_properties.clone()),
323+
is_soulbound_v2: None,
324+
token_standard: TokenStandard::V1.to_string(),
325+
is_fungible_v2: None,
326+
last_transaction_version: txn_version,
327+
last_transaction_timestamp: txn_timestamp,
328+
non_transferrable_by_owner: None,
329+
}),
330+
Some(owner_address),
331+
Some(tm.table_type.clone()),
332+
)
247333
},
248-
None => (None, None),
334+
None => (None, None, None),
249335
};
250336

251-
Ok(Some(Self {
252-
txn_version,
253-
write_set_change_index,
254-
token_data_id,
255-
property_version_v1: token_id_struct.property_version.to_u64().unwrap(),
256-
owner_address,
257-
storage_id: table_handle,
258-
amount: amount.to_string(),
259-
table_type_v1: table_type,
260-
token_properties_mutated_v1: Some(
261-
canonical_json::to_string(&token.token_properties).unwrap(),
262-
),
263-
is_soulbound_v2: None,
264-
token_standard: TokenStandard::V1.to_string(),
265-
block_timestamp: txn_timestamp,
266-
non_transferrable_by_owner: None,
267-
}))
337+
Ok(Some((
338+
Self {
339+
txn_version,
340+
write_set_change_index,
341+
token_data_id,
342+
property_version_v1: token_id_struct.property_version.to_u64().unwrap(),
343+
owner_address,
344+
storage_id: table_handle,
345+
amount: amount.to_string(),
346+
table_type_v1: table_type,
347+
token_properties_mutated_v1: Some(
348+
canonical_json::to_string(&token.token_properties).unwrap(),
349+
),
350+
is_soulbound_v2: None,
351+
token_standard: TokenStandard::V1.to_string(),
352+
block_timestamp: txn_timestamp,
353+
non_transferrable_by_owner: None,
354+
},
355+
curr_token_ownership,
356+
)))
268357
} else {
269358
Ok(None)
270359
}
271360
}
361+
362+
pub async fn get_burned_nft_v2_from_write_resource(
363+
write_resource: &WriteResource,
364+
txn_version: i64,
365+
write_set_change_index: i64,
366+
txn_timestamp: chrono::NaiveDateTime,
367+
prior_nft_ownership: &AHashMap<String, NFTOwnershipV2>,
368+
tokens_burned: &TokenV2Burned,
369+
object_metadatas: &ObjectAggregatedDataMapping,
370+
) -> anyhow::Result<Option<(Self, CurrentTokenOwnershipV2)>> {
371+
let token_data_id = standardize_address(&write_resource.address.to_string());
372+
if tokens_burned
373+
.get(&standardize_address(&token_data_id))
374+
.is_some()
375+
{
376+
if let Some(object) =
377+
&ObjectWithMetadata::from_write_resource(write_resource, txn_version)?
378+
{
379+
let object_core = &object.object_core;
380+
let owner_address = object_core.get_owner_address();
381+
let storage_id = token_data_id.clone();
382+
383+
// is_soulbound currently means if an object is completely untransferrable
384+
// OR if only admin can transfer. Only the former is true soulbound but
385+
// people might already be using it with the latter meaning so let's include both.
386+
let is_soulbound = if object_metadatas
387+
.get(&token_data_id)
388+
.map(|obj| obj.untransferable.as_ref())
389+
.is_some()
390+
{
391+
true
392+
} else {
393+
!object_core.allow_ungated_transfer
394+
};
395+
let non_transferrable_by_owner = !object_core.allow_ungated_transfer;
396+
397+
return Ok(Some((
398+
Self {
399+
txn_version,
400+
write_set_change_index,
401+
token_data_id: token_data_id.clone(),
402+
property_version_v1: LEGACY_DEFAULT_PROPERTY_VERSION,
403+
owner_address: Some(owner_address.clone()),
404+
storage_id: storage_id.clone(),
405+
amount: DEFAULT_AMOUNT_VALUE.clone(),
406+
table_type_v1: None,
407+
token_properties_mutated_v1: None,
408+
is_soulbound_v2: Some(is_soulbound),
409+
token_standard: TokenStandard::V2.to_string(),
410+
block_timestamp: txn_timestamp,
411+
non_transferrable_by_owner: Some(non_transferrable_by_owner),
412+
},
413+
CurrentTokenOwnershipV2 {
414+
token_data_id,
415+
property_version_v1: BigDecimal::zero(),
416+
owner_address,
417+
storage_id,
418+
amount: BigDecimal::zero(),
419+
table_type_v1: None,
420+
token_properties_mutated_v1: None,
421+
is_soulbound_v2: Some(is_soulbound),
422+
token_standard: TokenStandard::V2.to_string(),
423+
is_fungible_v2: Some(false),
424+
last_transaction_version: txn_version,
425+
last_transaction_timestamp: txn_timestamp,
426+
non_transferrable_by_owner: Some(non_transferrable_by_owner),
427+
},
428+
)));
429+
} else {
430+
return Self::get_burned_nft_v2_helper(
431+
&token_data_id,
432+
txn_version,
433+
write_set_change_index,
434+
txn_timestamp,
435+
prior_nft_ownership,
436+
tokens_burned,
437+
)
438+
.await;
439+
}
440+
}
441+
Ok(None)
442+
}
443+
444+
pub fn get_burned_nft_v2_from_delete_resource(
445+
delete_resource: &DeleteResource,
446+
txn_version: i64,
447+
write_set_change_index: i64,
448+
txn_timestamp: chrono::NaiveDateTime,
449+
prior_nft_ownership: &AHashMap<String, NFTOwnershipV2>,
450+
tokens_burned: &TokenV2Burned,
451+
) -> anyhow::Result<Option<(Self, CurrentTokenOwnershipV2)>> {
452+
let token_address = standardize_address(&delete_resource.address.to_string());
453+
let token_address = standardize_address(&token_address);
454+
if let Some(burn_event) = tokens_burned.get(&token_address) {
455+
// 1. Try to lookup token address in burn event mapping
456+
let previous_owner =
457+
if let Some(previous_owner) = burn_event.get_previous_owner_address() {
458+
previous_owner
459+
} else {
460+
// 2. If it doesn't exist in burn event mapping, then it must be an old burn event that doesn't contain previous_owner.
461+
// Do a lookup to get previous owner. This is necessary because previous owner is part of current token ownerships primary key.
462+
match prior_nft_ownership.get(&token_address) {
463+
Some(inner) => inner.owner_address.clone(),
464+
None => {
465+
DEFAULT_OWNER_ADDRESS.to_string() // we don't want to query db to get the previous owner for parquet.
466+
},
467+
}
468+
};
469+
470+
let token_data_id = token_address.clone();
471+
let storage_id = token_data_id.clone();
472+
473+
return Ok(Some((
474+
Self {
475+
txn_version,
476+
write_set_change_index,
477+
token_data_id: token_data_id.clone(),
478+
property_version_v1: LEGACY_DEFAULT_PROPERTY_VERSION,
479+
owner_address: Some(previous_owner.clone()),
480+
storage_id: storage_id.clone(),
481+
amount: DEFAULT_AMOUNT_VALUE.clone(),
482+
table_type_v1: None,
483+
token_properties_mutated_v1: None,
484+
is_soulbound_v2: None, // default
485+
token_standard: TokenStandard::V2.to_string(),
486+
block_timestamp: txn_timestamp,
487+
non_transferrable_by_owner: None, // default
488+
},
489+
CurrentTokenOwnershipV2 {
490+
token_data_id,
491+
property_version_v1: BigDecimal::zero(),
492+
owner_address: previous_owner,
493+
storage_id,
494+
amount: BigDecimal::zero(),
495+
table_type_v1: None,
496+
token_properties_mutated_v1: None,
497+
is_soulbound_v2: None, // default
498+
token_standard: TokenStandard::V2.to_string(),
499+
is_fungible_v2: None, // default
500+
last_transaction_version: txn_version,
501+
last_transaction_timestamp: txn_timestamp,
502+
non_transferrable_by_owner: None, // default
503+
},
504+
)));
505+
}
506+
Ok(None)
507+
}
272508
}

0 commit comments

Comments
 (0)