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-graphql): add erc1155 to union #3057

Merged
merged 2 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions crates/torii/graphql/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub const TOKEN_UNION_TYPE_NAME: &str = "ERC__Token";

pub const ERC20_TYPE_NAME: &str = "ERC20__Token";
pub const ERC721_TYPE_NAME: &str = "ERC721__Token";
pub const ERC1155_TYPE_NAME: &str = "ERC1155__Token";

// objects' single and plural names
pub const ENTITY_NAMES: (&str, &str) = ("entity", "entities");
Expand All @@ -55,6 +56,7 @@ pub const PAGE_INFO_NAMES: (&str, &str) = ("pageInfo", "");

pub const ERC20_TOKEN_NAME: (&str, &str) = ("erc20Token", "");
pub const ERC721_TOKEN_NAME: (&str, &str) = ("erc721Token", "");
pub const ERC1155_TOKEN_NAME: (&str, &str) = ("erc1155Token", "");

pub const TOKEN_BALANCE_NAME: (&str, &str) = ("", "tokenBalances");
pub const TOKEN_TRANSFER_NAME: (&str, &str) = ("", "tokenTransfers");
Expand Down
13 changes: 13 additions & 0 deletions crates/torii/graphql/src/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,19 @@ lazy_static! {
(Name::new("imagePath"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
]);

pub static ref ERC1155_TOKEN_TYPE_MAPPING: TypeMapping = IndexMap::from([
(Name::new("name"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("symbol"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("tokenId"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("contractAddress"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("amount"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("metadata"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("metadataName"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("metadataDescription"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("metadataAttributes"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("imagePath"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
]);

pub static ref EMPTY_MAPPING: TypeMapping = IndexMap::from([
(Name::new("id"), TypeData::Simple(TypeRef::named(TypeRef::ID))),
]);
Expand Down
174 changes: 169 additions & 5 deletions crates/torii/graphql/src/object/erc/erc_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
use tokio_stream::StreamExt;
use torii_sqlite::simple_broker::SimpleBroker;
use torii_sqlite::types::Token;
use tracing::warn;

use super::handle_cursor;
use crate::constants::{
DEFAULT_LIMIT, ERC20_TOKEN_NAME, ERC20_TYPE_NAME, ERC721_TOKEN_NAME, ERC721_TYPE_NAME,
ID_COLUMN,
DEFAULT_LIMIT, ERC1155_TOKEN_NAME, ERC1155_TYPE_NAME, ERC20_TOKEN_NAME, ERC20_TYPE_NAME,
ERC721_TOKEN_NAME, ERC721_TYPE_NAME, ID_COLUMN,
};
use crate::mapping::{
ERC1155_TOKEN_TYPE_MAPPING, ERC20_TOKEN_TYPE_MAPPING, ERC721_TOKEN_TYPE_MAPPING,
TOKEN_TYPE_MAPPING,
};
use crate::mapping::{ERC20_TOKEN_TYPE_MAPPING, ERC721_TOKEN_TYPE_MAPPING, TOKEN_TYPE_MAPPING};
use crate::object::connection::page_info::PageInfoObject;
use crate::object::connection::{
connection_arguments, cursor, parse_connection_arguments, ConnectionArguments,
Expand Down Expand Up @@ -58,10 +62,28 @@
}
}

#[derive(Debug)]
pub struct Erc1155TokenObject;

impl BasicObject for Erc1155TokenObject {
fn name(&self) -> (&str, &str) {
ERC1155_TOKEN_NAME
}

Check warning on line 71 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L69-L71

Added lines #L69 - L71 were not covered by tests

fn type_name(&self) -> &str {
ERC1155_TYPE_NAME
}

Check warning on line 75 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L73-L75

Added lines #L73 - L75 were not covered by tests

fn type_mapping(&self) -> &TypeMapping {
&ERC1155_TOKEN_TYPE_MAPPING
}

Check warning on line 79 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L77-L79

Added lines #L77 - L79 were not covered by tests
}

#[derive(Debug, Clone)]
pub enum ErcTokenType {
Erc20(Erc20Token),
Erc721(Erc721Token),
Erc1155(Erc1155Token),
}

#[derive(Debug, Clone)]
Expand All @@ -86,6 +108,20 @@
pub image_path: String,
}

#[derive(Debug, Clone)]
pub struct Erc1155Token {
pub name: String,
pub symbol: String,
pub token_id: String,
pub contract_address: String,
pub amount: String,
pub metadata: String,
pub metadata_name: Option<String>,
pub metadata_description: Option<String>,
pub metadata_attributes: Option<String>,
pub image_path: String,
}

impl ErcTokenType {
pub fn to_field_value<'a>(self) -> FieldValue<'a> {
match self {
Expand Down Expand Up @@ -122,6 +158,30 @@
]))),
ERC721_TYPE_NAME.to_string(),
),
ErcTokenType::Erc1155(token) => FieldValue::with_type(
FieldValue::value(Value::Object(ValueMapping::from([
(Name::new("name"), Value::String(token.name)),
(Name::new("symbol"), Value::String(token.symbol)),
(Name::new("tokenId"), Value::String(token.token_id)),
(Name::new("contractAddress"), Value::String(token.contract_address)),
(Name::new("amount"), Value::String(token.amount)),
(Name::new("metadata"), Value::String(token.metadata)),
(
Name::new("metadataName"),
token.metadata_name.map(Value::String).unwrap_or(Value::Null),
),
(
Name::new("metadataDescription"),
token.metadata_description.map(Value::String).unwrap_or(Value::Null),
),
(
Name::new("metadataAttributes"),
token.metadata_attributes.map(Value::String).unwrap_or(Value::Null),
),
(Name::new("imagePath"), Value::String(token.image_path)),
]))),
ERC1155_TYPE_NAME.to_string(),
),

Check warning on line 184 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L161-L184

Added lines #L161 - L184 were not covered by tests
}
}
}
Expand Down Expand Up @@ -446,7 +506,65 @@
};
ErcTokenType::Erc721(token)
}
_ => return None,
"erc1155" => {
let id = row.get::<String, _>("id");
let token_id =
id.split(':').collect::<Vec<&str>>()[1].to_string();

let metadata_str: String = row.get("metadata");

Check warning on line 514 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L509-L514

Added lines #L509 - L514 were not covered by tests
let (
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
) = if metadata_str.is_empty() {
(String::new(), None, None, None, String::new())

Check warning on line 522 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L516-L522

Added lines #L516 - L522 were not covered by tests
} else {
let metadata: serde_json::Value =
serde_json::from_str(&metadata_str)
.expect("metadata is always json");
let metadata_name = metadata.get("name").map(|v| {
v.to_string().trim_matches('"').to_string()
});
let metadata_description =
metadata.get("description").map(|v| {
v.to_string().trim_matches('"').to_string()
});
let metadata_attributes =
metadata.get("attributes").map(|v| {
v.to_string().trim_matches('"').to_string()
});

let image_path =
format!("{}/image", id.replace(":", "/"));
(
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
)

Check warning on line 547 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L524-L547

Added lines #L524 - L547 were not covered by tests
};

let token = Erc1155Token {
name: row.get("name"),
metadata: metadata_str,
contract_address: row.get("contract_address"),
symbol: row.get("symbol"),
token_id,
amount: "0".to_string(),
metadata_name,
metadata_description,
metadata_attributes,
image_path,
};
ErcTokenType::Erc1155(token)

Check warning on line 562 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L550-L562

Added lines #L550 - L562 were not covered by tests
}
_ => {
warn!("Unknown contract type: {}", contract_type);
return None;

Check warning on line 566 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L565-L566

Added lines #L565 - L566 were not covered by tests
}
};

Some(Ok(FieldValue::owned_any(token_metadata)))
Expand Down Expand Up @@ -516,6 +634,52 @@
};
ErcTokenType::Erc721(token)
}
_ => return Err(sqlx::Error::RowNotFound),
"erc1155" => {

Check warning on line 637 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L637

Added line #L637 was not covered by tests
// contract_address:token_id
let id = row.get::<String, _>("id");
let token_id = id.split(':').collect::<Vec<&str>>()[1].to_string();

let metadata_str: String = row.get("metadata");

Check warning on line 642 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L639-L642

Added lines #L639 - L642 were not covered by tests
let (
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
) = if metadata_str.is_empty() {
(String::new(), None, None, None, String::new())

Check warning on line 650 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L644-L650

Added lines #L644 - L650 were not covered by tests
} else {
let metadata: serde_json::Value =
serde_json::from_str(&metadata_str).expect("metadata is always json");
let metadata_name =
metadata.get("name").map(|v| v.to_string().trim_matches('"').to_string());
let metadata_description = metadata
.get("description")
.map(|v| v.to_string().trim_matches('"').to_string());
let metadata_attributes =
metadata.get("attributes").map(|v| v.to_string().trim_matches('"').to_string());

let image_path = format!("{}/image", id.replace(":", "/"));
(metadata_str, metadata_name, metadata_description, metadata_attributes, image_path)

Check warning on line 663 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L652-L663

Added lines #L652 - L663 were not covered by tests
};

let token = Erc1155Token {
name: row.get("name"),
metadata: metadata_str,
contract_address: row.get("contract_address"),
symbol: row.get("symbol"),
token_id,
amount: "0".to_string(),
metadata_name,
metadata_description,
metadata_attributes,
image_path,
};
ErcTokenType::Erc1155(token)

Check warning on line 678 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L666-L678

Added lines #L666 - L678 were not covered by tests
}
_ => {
warn!("Unknown contract type: {}", contract_type);
return Err(sqlx::Error::RowNotFound);

Check warning on line 682 in crates/torii/graphql/src/object/erc/erc_token.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/erc_token.rs#L681-L682

Added lines #L681 - L682 were not covered by tests
}
})
}
115 changes: 113 additions & 2 deletions crates/torii/graphql/src/object/erc/token_balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
use crate::object::connection::{
connection_arguments, cursor, parse_connection_arguments, ConnectionArguments,
};
use crate::object::erc::erc_token::Erc721Token;
use crate::object::erc::erc_token::{Erc1155Token, Erc721Token};
use crate::object::{BasicObject, ResolvableObject};
use crate::query::data::count_rows;
use crate::query::filter::{Comparator, Filter, FilterValue};
Expand Down Expand Up @@ -213,7 +213,66 @@
};
ErcTokenType::Erc721(token_metadata)
}
_ => return None,
"erc1155" => {
let token_id =
row.token_id.split(':').collect::<Vec<&str>>();
assert!(token_id.len() == 2);

Check warning on line 219 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L216-L219

Added lines #L216 - L219 were not covered by tests
Comment on lines +217 to +219
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validate token ID format before splitting.

Ohayo sensei! The token ID splitting logic assumes a specific format without validation. Consider adding proper validation before splitting.

Here's a suggested implementation:

fn validate_and_split_token_id(token_id: &str) -> Result<Vec<&str>, sqlx::Error> {
    let parts: Vec<&str> = token_id.split(':').collect();
    if parts.len() != 2 {
        return Err(sqlx::Error::Protocol(
            format!("Invalid token ID format: {}. Expected format: contract_address:token_id", token_id)
        ));
    }
    Ok(parts)
}

Then use it in the code:

- let token_id = row.token_id.split(':').collect::<Vec<&str>>();
- assert!(token_id.len() == 2);
+ let token_id = validate_and_split_token_id(&row.token_id)?;

Also applies to: 490-492


let metadata_str = row.metadata;

Check warning on line 221 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L221

Added line #L221 was not covered by tests
let (
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
) = if metadata_str.is_empty() {
(String::new(), None, None, None, String::new())

Check warning on line 229 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L223-L229

Added lines #L223 - L229 were not covered by tests
} else {
let metadata: serde_json::Value =
serde_json::from_str(&metadata_str)
.expect("metadata is always json");
Comment on lines +232 to +233
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace expect() with proper error handling.

Ohayo sensei! Using expect() for JSON parsing could cause panics in production. Consider using proper error handling instead.

Here's a suggested implementation:

- serde_json::from_str(&metadata_str).expect("metadata is always json");
+ serde_json::from_str(&metadata_str).map_err(|e| {
+     sqlx::Error::Protocol(format!("Failed to parse metadata JSON: {}", e))
+ })?;

Also applies to: 505-506

let metadata_name = metadata.get("name").map(|v| {
v.to_string().trim_matches('"').to_string()
});
let metadata_description =
metadata.get("description").map(|v| {
v.to_string().trim_matches('"').to_string()
});
let metadata_attributes =
metadata.get("attributes").map(|v| {
v.to_string().trim_matches('"').to_string()
});

let image_path =
format!("{}/{}", token_id.join("/"), "image");

(
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
)

Check warning on line 255 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L231-L255

Added lines #L231 - L255 were not covered by tests
};

let token_metadata = Erc1155Token {
name: row.name,
metadata: metadata_str,
contract_address: row.contract_address,
symbol: row.symbol,
token_id: token_id[1].to_string(),
amount: row.balance,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
};
ErcTokenType::Erc1155(token_metadata)

Check warning on line 270 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L258-L270

Added lines #L258 - L270 were not covered by tests
}
_ => {
warn!("Unknown contract type: {}", row.contract_type);
return None;

Check warning on line 274 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L273-L274

Added lines #L273 - L274 were not covered by tests
}
};

Some(Ok(FieldValue::owned_any(balance_value)))
Expand Down Expand Up @@ -427,6 +486,58 @@

ErcTokenType::Erc721(token_metadata)
}
"erc1155" => {

Check warning on line 489 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L489

Added line #L489 was not covered by tests
// contract_address:token_id
let token_id = row.token_id.split(':').collect::<Vec<&str>>();
assert!(token_id.len() == 2);

Check warning on line 492 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L491-L492

Added lines #L491 - L492 were not covered by tests

let metadata_str = row.metadata;

Check warning on line 494 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L494

Added line #L494 was not covered by tests
let (
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
) = if metadata_str.is_empty() {
(String::new(), None, None, None, String::new())

Check warning on line 502 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L496-L502

Added lines #L496 - L502 were not covered by tests
} else {
let metadata: serde_json::Value =
serde_json::from_str(&metadata_str).expect("metadata is always json");
let metadata_name =
metadata.get("name").map(|v| v.to_string().trim_matches('"').to_string());
let metadata_description = metadata
.get("description")
.map(|v| v.to_string().trim_matches('"').to_string());
let metadata_attributes = metadata
.get("attributes")
.map(|v| v.to_string().trim_matches('"').to_string());

let image_path = format!("{}/{}", token_id.join("/"), "image");

(
metadata_str,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
)

Check warning on line 523 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L504-L523

Added lines #L504 - L523 were not covered by tests
};

let token_metadata = Erc1155Token {
name: row.name,
metadata: metadata_str.to_owned(),
contract_address: row.contract_address,
symbol: row.symbol,
token_id: token_id[1].to_string(),
amount: row.balance,
metadata_name,
metadata_description,
metadata_attributes,
image_path,
};

ErcTokenType::Erc1155(token_metadata)

Check warning on line 539 in crates/torii/graphql/src/object/erc/token_balance.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/graphql/src/object/erc/token_balance.rs#L526-L539

Added lines #L526 - L539 were not covered by tests
}
_ => {
warn!("Unknown contract type: {}", row.contract_type);
continue;
Expand Down
Loading