Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(torii): add support for erc1155 #2955

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/torii/indexer/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use torii_sqlite::{Cursors, Sql};
use tracing::{debug, error, info, trace, warn};

use crate::constants::LOG_TARGET;
use crate::processors::erc1155_transfer_batch::Erc1155TransferBatchProcessor;
use crate::processors::erc1155_transfer_single::Erc1155TransferSingleProcessor;
use crate::processors::erc20_legacy_transfer::Erc20LegacyTransferProcessor;
use crate::processors::erc20_transfer::Erc20TransferProcessor;
use crate::processors::erc721_legacy_transfer::Erc721LegacyTransferProcessor;
Expand Down Expand Up @@ -105,6 +107,13 @@ impl<P: Provider + Send + Sync + std::fmt::Debug + 'static> Processors<P> {
Box::new(Erc721LegacyTransferProcessor) as Box<dyn EventProcessor<P>>,
],
),
(
ContractType::ERC1155,
vec![
Box::new(Erc1155TransferBatchProcessor) as Box<dyn EventProcessor<P>>,
Box::new(Erc1155TransferSingleProcessor) as Box<dyn EventProcessor<P>>,
],
),
];

for (contract_type, processors) in event_processors {
Expand Down
113 changes: 113 additions & 0 deletions crates/torii/indexer/src/processors/erc1155_transfer_batch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use anyhow::Error;
use async_trait::async_trait;
use cainome::cairo_serde::{CairoSerde, U256 as U256Cainome};
use dojo_world::contracts::world::WorldContractReader;
use starknet::core::types::{Event, U256};
use starknet::providers::Provider;
use torii_sqlite::Sql;
use tracing::debug;

use super::{EventProcessor, EventProcessorConfig};
use crate::task_manager::{self, TaskId, TaskPriority};

pub(crate) const LOG_TARGET: &str = "torii_indexer::processors::erc1155_transfer_batch";

#[derive(Default, Debug)]
pub struct Erc1155TransferBatchProcessor;

#[async_trait]
impl<P> EventProcessor<P> for Erc1155TransferBatchProcessor
where
P: Provider + Send + Sync + std::fmt::Debug,
{
fn event_key(&self) -> String {
"TransferBatch".to_string()
}

fn validate(&self, event: &Event) -> bool {
// key: [hash(TransferBatch), operator, from, to]
// data: [ids_len, ids[0].low, ids[0].high, ..., values_len, values[0].low, values[0].high,
// ...]
event.keys.len() == 4 && !event.data.is_empty()
}

Check warning on line 32 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L27-L32

Added lines #L27 - L32 were not covered by tests

fn task_priority(&self) -> TaskPriority {
1
}

Check warning on line 36 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L34-L36

Added lines #L34 - L36 were not covered by tests

fn task_identifier(&self, _event: &Event) -> TaskId {
task_manager::TASK_ID_SEQUENTIAL
}

Check warning on line 40 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L38-L40

Added lines #L38 - L40 were not covered by tests

async fn process(
&self,
_world: &WorldContractReader<P>,
db: &mut Sql,
block_number: u64,
block_timestamp: u64,
event_id: &str,
event: &Event,
_config: &EventProcessorConfig,
) -> Result<(), Error> {
let token_address = event.from_address;
let from = event.keys[2];
let to = event.keys[3];

// ERC1155 TransferBatch event data format:
// - ids_len: felt (first element)
// - ids: U256[] (each element stored as 2 felts: [low, high])
// - values_len: felt
// - values: U256[] (each element stored as 2 felts: [low, high])
// Spec reference: https://eips.ethereum.org/EIPS/eip-1155#transferbatch
let ids_len = event.data[0].try_into().unwrap_or(0u64) as usize;
let mut current_idx = 1;

// First pass: read all token IDs
Larkooo marked this conversation as resolved.
Show resolved Hide resolved
let mut token_ids = Vec::with_capacity(ids_len);
for _ in 0..ids_len {
if current_idx + 1 >= event.data.len() {
break;
}
let token_id = U256Cainome::cairo_deserialize(&event.data, current_idx)?;
token_ids.push(U256::from_words(token_id.low, token_id.high));
current_idx += 2;

Check warning on line 73 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L51-L73

Added lines #L51 - L73 were not covered by tests
}
Comment on lines +62 to +74
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds checking for array access, sensei.

The current implementation might panic if ids_len is larger than the actual data array. Consider adding a bounds check before accessing the array.

Here's a suggested fix:

-        let ids_len = event.data[0].try_into().unwrap_or(0u64) as usize;
+        let ids_len = event.data.get(0)
+            .and_then(|v| v.try_into().ok())
+            .unwrap_or(0u64) as usize;
         let mut current_idx = 1;
 
         // First pass: read all token IDs
         let mut token_ids = Vec::with_capacity(ids_len);
         for _ in 0..ids_len {
             if current_idx + 1 >= event.data.len() {
                 break;
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let ids_len = event.data[0].try_into().unwrap_or(0u64) as usize;
let mut current_idx = 1;
// First pass: read all token IDs
let mut token_ids = Vec::with_capacity(ids_len);
for _ in 0..ids_len {
if current_idx + 1 >= event.data.len() {
break;
}
let token_id = U256Cainome::cairo_deserialize(&event.data, current_idx)?;
token_ids.push(U256::from_words(token_id.low, token_id.high));
current_idx += 2;
}
let ids_len = event.data.get(0)
.and_then(|v| v.try_into().ok())
.unwrap_or(0u64) as usize;
let mut current_idx = 1;
// First pass: read all token IDs
let mut token_ids = Vec::with_capacity(ids_len);
for _ in 0..ids_len {
if current_idx + 1 >= event.data.len() {
break;
}
let token_id = U256Cainome::cairo_deserialize(&event.data, current_idx)?;
token_ids.push(U256::from_words(token_id.low, token_id.high));
current_idx += 2;
}


// Move index to values array
let values_len = event.data[current_idx].try_into().unwrap_or(0u64) as usize;
current_idx += 1;

Check warning on line 78 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L77-L78

Added lines #L77 - L78 were not covered by tests
Comment on lines +76 to +78
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds check for values_len access.

Similar to the ids_len issue, the values_len access should be protected against out-of-bounds access.

Here's a suggested fix:

         // Move index to values array
-        let values_len = event.data[current_idx].try_into().unwrap_or(0u64) as usize;
+        let values_len = event.data.get(current_idx)
+            .and_then(|v| v.try_into().ok())
+            .unwrap_or(0u64) as usize;
         current_idx += 1;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Move index to values array
let values_len = event.data[current_idx].try_into().unwrap_or(0u64) as usize;
current_idx += 1;
// Move index to values array
let values_len = event.data.get(current_idx)
.and_then(|v| v.try_into().ok())
.unwrap_or(0u64) as usize;
current_idx += 1;


// Second pass: read and process amounts
for (idx, token_id) in token_ids.iter().enumerate() {
if idx >= values_len || current_idx + (idx * 2) + 1 >= event.data.len() {
break;
}

Check warning on line 84 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L81-L84

Added lines #L81 - L84 were not covered by tests

let amount = U256Cainome::cairo_deserialize(&event.data, current_idx + (idx * 2))?;
let amount = U256::from_words(amount.low, amount.high);

db.handle_nft_transfer(
token_address,
from,
to,
*token_id,
amount,
block_timestamp,
event_id,
block_number,
)
.await?;

Check warning on line 99 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L86-L99

Added lines #L86 - L99 were not covered by tests

debug!(

Check warning on line 101 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L101

Added line #L101 was not covered by tests
target: LOG_TARGET,
from = ?from,
to = ?to,
token_id = ?token_id,
amount = ?amount,
"ERC1155 TransferBatch"

Check warning on line 107 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L107

Added line #L107 was not covered by tests
);
}

Ok(())
}

Check warning on line 112 in crates/torii/indexer/src/processors/erc1155_transfer_batch.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_batch.rs#L111-L112

Added lines #L111 - L112 were not covered by tests
}
76 changes: 76 additions & 0 deletions crates/torii/indexer/src/processors/erc1155_transfer_single.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use anyhow::Error;
use async_trait::async_trait;
use cainome::cairo_serde::{CairoSerde, U256 as U256Cainome};
use dojo_world::contracts::world::WorldContractReader;
use starknet::core::types::{Event, U256};
use starknet::providers::Provider;
use torii_sqlite::Sql;
use tracing::debug;

use super::{EventProcessor, EventProcessorConfig};
use crate::task_manager::{self, TaskId, TaskPriority};

pub(crate) const LOG_TARGET: &str = "torii_indexer::processors::erc1155_transfer_single";

#[derive(Default, Debug)]
pub struct Erc1155TransferSingleProcessor;

#[async_trait]
impl<P> EventProcessor<P> for Erc1155TransferSingleProcessor
where
P: Provider + Send + Sync + std::fmt::Debug,
{
fn event_key(&self) -> String {
"TransferSingle".to_string()
}

fn validate(&self, event: &Event) -> bool {
// key: [hash(TransferSingle), operator, from, to]
// data: [id.low, id.high, value.low, value.high]
event.keys.len() == 4 && event.data.len() == 4
}

Check warning on line 31 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L27-L31

Added lines #L27 - L31 were not covered by tests

fn task_priority(&self) -> TaskPriority {
1
}

Check warning on line 35 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L33-L35

Added lines #L33 - L35 were not covered by tests

fn task_identifier(&self, _event: &Event) -> TaskId {
task_manager::TASK_ID_SEQUENTIAL
}

Check warning on line 39 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L37-L39

Added lines #L37 - L39 were not covered by tests

async fn process(
&self,
_world: &WorldContractReader<P>,
db: &mut Sql,
block_number: u64,
block_timestamp: u64,
event_id: &str,
event: &Event,
_config: &EventProcessorConfig,
) -> Result<(), Error> {
let token_address = event.from_address;
let from = event.keys[2];
let to = event.keys[3];

Check warning on line 53 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L50-L53

Added lines #L50 - L53 were not covered by tests

let token_id = U256Cainome::cairo_deserialize(&event.data, 0)?;
let token_id = U256::from_words(token_id.low, token_id.high);

Check warning on line 56 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L55-L56

Added lines #L55 - L56 were not covered by tests

let amount = U256Cainome::cairo_deserialize(&event.data, 2)?;
let amount = U256::from_words(amount.low, amount.high);

db.handle_nft_transfer(
token_address,
from,
to,
token_id,
amount,
block_timestamp,
event_id,
block_number,
)
.await?;
debug!(target: LOG_TARGET, from = ?from, to = ?to, token_id = ?token_id, amount = ?amount, "ERC1155 TransferSingle");

Check warning on line 72 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L58-L72

Added lines #L58 - L72 were not covered by tests

Ok(())
}

Check warning on line 75 in crates/torii/indexer/src/processors/erc1155_transfer_single.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc1155_transfer_single.rs#L74-L75

Added lines #L74 - L75 were not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@
let token_id = U256Cainome::cairo_deserialize(&event.data, 2)?;
let token_id = U256::from_words(token_id.low, token_id.high);

db.handle_erc721_transfer(
db.handle_nft_transfer(

Check warning on line 83 in crates/torii/indexer/src/processors/erc721_legacy_transfer.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc721_legacy_transfer.rs#L83

Added line #L83 was not covered by tests
token_address,
from,
to,
token_id,
U256::from(1u8),

Check warning on line 88 in crates/torii/indexer/src/processors/erc721_legacy_transfer.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc721_legacy_transfer.rs#L88

Added line #L88 was not covered by tests
block_timestamp,
event_id,
block_number,
Expand Down
3 changes: 2 additions & 1 deletion crates/torii/indexer/src/processors/erc721_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@
let token_id = U256Cainome::cairo_deserialize(&event.keys, 3)?;
let token_id = U256::from_words(token_id.low, token_id.high);

db.handle_erc721_transfer(
db.handle_nft_transfer(

Check warning on line 83 in crates/torii/indexer/src/processors/erc721_transfer.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc721_transfer.rs#L83

Added line #L83 was not covered by tests
token_address,
from,
to,
token_id,
U256::from(1u8),

Check warning on line 88 in crates/torii/indexer/src/processors/erc721_transfer.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/indexer/src/processors/erc721_transfer.rs#L88

Added line #L88 was not covered by tests
block_timestamp,
event_id,
block_number,
Expand Down
2 changes: 2 additions & 0 deletions crates/torii/indexer/src/processors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use torii_sqlite::Sql;

use crate::task_manager::{TaskId, TaskPriority};

pub mod erc1155_transfer_batch;
pub mod erc1155_transfer_single;
pub mod erc20_legacy_transfer;
pub mod erc20_transfer;
pub mod erc721_legacy_transfer;
Expand Down
18 changes: 9 additions & 9 deletions crates/torii/sqlite/src/erc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
use super::utils::{u256_to_sql_string, I256};
use super::{Sql, SQL_FELT_DELIMITER};
use crate::constants::TOKEN_TRANSFER_TABLE;
use crate::executor::erc::RegisterNftTokenQuery;
use crate::executor::{
ApplyBalanceDiffQuery, Argument, QueryMessage, QueryType, RegisterErc20TokenQuery,
RegisterErc721TokenQuery,
};
use crate::types::ContractType;
use crate::utils::{
Expand Down Expand Up @@ -79,12 +79,13 @@
}

#[allow(clippy::too_many_arguments)]
pub async fn handle_erc721_transfer(
pub async fn handle_nft_transfer(

Check warning on line 82 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L82

Added line #L82 was not covered by tests
&mut self,
contract_address: Felt,
from_address: Felt,
to_address: Felt,
token_id: U256,
amount: U256,

Check warning on line 88 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L88

Added line #L88 was not covered by tests
block_timestamp: u64,
event_id: &str,
block_number: u64,
Expand All @@ -95,15 +96,14 @@
let token_exists: bool = self.local_cache.contains_token_id(&token_id).await;

if !token_exists {
self.register_erc721_token_metadata(contract_address, &token_id, actual_token_id)
.await?;
self.register_nft_token_metadata(contract_address, &token_id, actual_token_id).await?;

Check warning on line 99 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L99

Added line #L99 was not covered by tests
}

self.store_erc_transfer_event(
contract_address,
from_address,
to_address,
U256::from(1u8),
amount,

Check warning on line 106 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L106

Added line #L106 was not covered by tests
&token_id,
block_timestamp,
event_id,
Expand All @@ -120,15 +120,15 @@
);
let from_balance =
erc_cache.entry((ContractType::ERC721, from_balance_id)).or_default();
*from_balance -= I256::from(1u8);
*from_balance -= I256::from(amount);

Check warning on line 123 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L123

Added line #L123 was not covered by tests
}

if to_address != Felt::ZERO {
let to_balance_id =
format!("{}{SQL_FELT_DELIMITER}{}", felt_to_sql_string(&to_address), &token_id);
let to_balance =
erc_cache.entry((ContractType::ERC721, to_balance_id)).or_default();
*to_balance += I256::from(1u8);
*to_balance += I256::from(amount);

Check warning on line 131 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L131

Added line #L131 was not covered by tests
}
}

Expand Down Expand Up @@ -220,7 +220,7 @@
Ok(())
}

async fn register_erc721_token_metadata(
async fn register_nft_token_metadata(

Check warning on line 223 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L223

Added line #L223 was not covered by tests
&mut self,
contract_address: Felt,
token_id: &str,
Expand All @@ -229,7 +229,7 @@
self.executor.send(QueryMessage::new(
"".to_string(),
vec![],
QueryType::RegisterErc721Token(RegisterErc721TokenQuery {
QueryType::RegisterNftToken(RegisterNftTokenQuery {

Check warning on line 232 in crates/torii/sqlite/src/erc.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/sqlite/src/erc.rs#L232

Added line #L232 was not covered by tests
token_id: token_id.to_string(),
contract_address,
actual_token_id,
Expand Down
Loading
Loading