diff --git a/Cargo.lock b/Cargo.lock index a4df79109..28794132f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2525,6 +2525,7 @@ dependencies = [ "fuels-abigen-macro", "fuels-core", "futures", + "hex", "itertools", "rand 0.8.5", "reqwest", @@ -2548,6 +2549,7 @@ dependencies = [ "fuel-tx", "fuel-types", "fuels-core", + "fuels-types 0.30.0", "serde", ] diff --git a/docs/src/examples/block-explorer.md b/docs/src/examples/block-explorer.md index 20e29f194..773045485 100644 --- a/docs/src/examples/block-explorer.md +++ b/docs/src/examples/block-explorer.md @@ -265,7 +265,7 @@ mod explorer_index { id: tx.id, value: tx_amount, status: tx.status.clone().into(), - tokens_transferred: Jsonb( + tokens_transferred: Json( serde_json::to_value(tokens_transferred) .unwrap() .to_string(), diff --git a/docs/src/examples/hello-world.md b/docs/src/examples/hello-world.md index 0da9ecfd9..8944efe33 100644 --- a/docs/src/examples/hello-world.md +++ b/docs/src/examples/hello-world.md @@ -72,7 +72,7 @@ mod hello_world_index { } None => { // If we did not already have this Saluation stored in the database. Here we - // show how you can use the String255 type to store strings with length <= 255 + // show how you can use the Charfield type to store strings with length <= 255 let message = format!("{} 👋, my name is {}", &event.greeting, &event.person.name); diff --git a/docs/src/quickstart/index.md b/docs/src/quickstart/index.md index c0978ad0f..6f829a7f8 100644 --- a/docs/src/quickstart/index.md +++ b/docs/src/quickstart/index.md @@ -94,7 +94,7 @@ type QueryRoot { # Calling this `Greeter` so as to not clash with `Person` in the contract type Greeter { id: ID! - name: String255! + name: Charfield! first_seen: UInt8! last_seen: UInt8! } @@ -103,7 +103,7 @@ type Greeter { type Salutation { id: ID! message_hash: Bytes32! - message: String255! + message: Charfield! greeter: Greeter! first_seen: UInt8! last_seen: UInt8! @@ -201,7 +201,7 @@ mod hello_world_index { } None => { // If we did not already have this Saluation stored in the database. Here we - // show how you can use the String255 type to store strings with length <= 255 + // show how you can use the Charfield type to store strings with length <= 255 let message = format!("{} 👋, my name is {}", &event.greeting, &event.person.name); diff --git a/examples/block-explorer/explorer-index/src/lib.rs b/examples/block-explorer/explorer-index/src/lib.rs index aa5049b3a..568086985 100644 --- a/examples/block-explorer/explorer-index/src/lib.rs +++ b/examples/block-explorer/explorer-index/src/lib.rs @@ -274,7 +274,7 @@ mod explorer_index { id: tx.id, value: tx_amount, status: tx.status.clone().into(), - tokens_transferred: Jsonb( + tokens_transferred: Json( serde_json::to_value(tokens_transferred) .unwrap() .to_string(), diff --git a/examples/block-explorer/schema/explorer_index.schema.graphql b/examples/block-explorer/schema/explorer_index.schema.graphql index dc32bc4ec..004dd27b4 100644 --- a/examples/block-explorer/schema/explorer_index.schema.graphql +++ b/examples/block-explorer/schema/explorer_index.schema.graphql @@ -22,9 +22,9 @@ type Tx { id: Bytes32! @unique block: Block! timestamp: Int8! - status: Jsonb! + status: Json! value: UInt8! - tokens_transferred: Jsonb! + tokens_transferred: Json! } type Account { diff --git a/examples/hello-world/hello-index/src/lib.rs b/examples/hello-world/hello-index/src/lib.rs index 4de210469..124797038 100644 --- a/examples/hello-world/hello-index/src/lib.rs +++ b/examples/hello-world/hello-index/src/lib.rs @@ -28,7 +28,7 @@ extern crate alloc; use fuel_indexer_macros::indexer; -use fuel_indexer_plugin::{types::Bytes32, utils::sha256_digest}; +use fuel_indexer_plugin::prelude::*; // A utility function used to convert an arbitrarily sized string into Bytes32 // using the first 32 bytes of the String. This might be provided by a standard-ish @@ -66,7 +66,7 @@ mod hello_world_index { } None => { // If we did not already have this Saluation stored in the database. Here we - // show how you can use the String255 type to store strings with length <= 255 + // show how you can use the Charfield type to store strings with length <= 255 let message = format!("{} 👋, my name is {}", &event.greeting, &event.person.name); diff --git a/examples/hello-world/schema/hello_index.schema.graphql b/examples/hello-world/schema/hello_index.schema.graphql index 30564b231..bef54dfd8 100644 --- a/examples/hello-world/schema/hello_index.schema.graphql +++ b/examples/hello-world/schema/hello_index.schema.graphql @@ -10,7 +10,7 @@ type QueryRoot { # Calling this `Greeter` so as to not clash with `Person` in the contract type Greeter { id: ID! - name: String255! + name: Charfield! first_seen: UInt8! last_seen: UInt8! } @@ -19,7 +19,7 @@ type Greeter { type Salutation { id: ID! message_hash: Bytes32! - message: String255! + message: Charfield! greeter: Greeter! first_seen: UInt8! last_seen: UInt8! diff --git a/packages/fuel-indexer-database/database-types/src/lib.rs b/packages/fuel-indexer-database/database-types/src/lib.rs index 67b7f5379..18db629db 100644 --- a/packages/fuel-indexer-database/database-types/src/lib.rs +++ b/packages/fuel-indexer-database/database-types/src/lib.rs @@ -109,9 +109,10 @@ impl NewColumn { ColumnType::ForeignKey => { panic!("ForeignKey ColumnType is a reference type only.") } - ColumnType::Jsonb => "jsonb", + ColumnType::Json => "Json", ColumnType::MessageId => "varchar(64)", - ColumnType::String255 => "varchar(255)", + ColumnType::Charfield => "varchar(255)", + ColumnType::Identity => "varchar(66)", } } } @@ -142,9 +143,10 @@ pub enum ColumnType { Timestamp = 12, Blob = 13, ForeignKey = 14, - Jsonb = 15, + Json = 15, MessageId = 16, - String255 = 17, + Charfield = 17, + Identity = 18, } impl From for i32 { @@ -165,9 +167,10 @@ impl From for i32 { ColumnType::Timestamp => 12, ColumnType::Blob => 13, ColumnType::ForeignKey => 14, - ColumnType::Jsonb => 15, + ColumnType::Json => 15, ColumnType::MessageId => 16, - ColumnType::String255 => 17, + ColumnType::Charfield => 17, + ColumnType::Identity => 18, } } } @@ -196,9 +199,10 @@ impl From for ColumnType { 12 => ColumnType::Timestamp, 13 => ColumnType::Blob, 14 => ColumnType::ForeignKey, - 15 => ColumnType::Jsonb, + 15 => ColumnType::Json, 16 => ColumnType::MessageId, - 17 => ColumnType::String255, + 17 => ColumnType::Charfield, + 18 => ColumnType::Identity, _ => panic!("Invalid column type."), } } @@ -222,9 +226,10 @@ impl From<&str> for ColumnType { "Timestamp" => ColumnType::Timestamp, "Blob" => ColumnType::Blob, "ForeignKey" => ColumnType::ForeignKey, - "Jsonb" => ColumnType::Jsonb, + "Json" => ColumnType::Json, "MessageId" => ColumnType::MessageId, - "String255" => ColumnType::String255, + "Charfield" => ColumnType::Charfield, + "Identity" => ColumnType::Identity, _ => panic!("Invalid column type: '{}'", name), } } diff --git a/packages/fuel-indexer-macros/src/indexer.rs b/packages/fuel-indexer-macros/src/indexer.rs index c672f7a79..ebadf6484 100644 --- a/packages/fuel-indexer-macros/src/indexer.rs +++ b/packages/fuel-indexer-macros/src/indexer.rs @@ -109,6 +109,7 @@ fn rust_type(ty: &TypeDeclaration) -> proc_macro2::TokenStream { "u64" => quote! { u64 }, "b256" => quote! { B256 }, "Log" => quote! { fuel::Log }, + "Identity" => quote! { fuel::Identity }, "BlockData" => quote! { BlockData }, "LogData" => quote! { fuel::LogData }, "Transfer" => quote! { fuel::Transfer }, diff --git a/packages/fuel-indexer-macros/src/schema.rs b/packages/fuel-indexer-macros/src/schema.rs index 5daf83938..a899d7c48 100644 --- a/packages/fuel-indexer-macros/src/schema.rs +++ b/packages/fuel-indexer-macros/src/schema.rs @@ -19,7 +19,8 @@ use std::io::Read; use std::path::{Path, PathBuf}; lazy_static! { - static ref COPY_TYPES: HashSet<&'static str> = HashSet::from(["Jsonb", "String255"]); + static ref COPY_TYPES: HashSet<&'static str> = + HashSet::from(["Json", "Charfield", "Identity"]); } fn process_type<'a>( diff --git a/packages/fuel-indexer-plugin/src/lib.rs b/packages/fuel-indexer-plugin/src/lib.rs index 6d9c18063..5babdc636 100644 --- a/packages/fuel-indexer-plugin/src/lib.rs +++ b/packages/fuel-indexer-plugin/src/lib.rs @@ -17,6 +17,12 @@ pub mod utils { pub use fuel_indexer_lib::utils::sha256_digest; } +pub mod prelude { + pub use super::types::*; + + pub use super::utils::*; +} + extern "C" { // TODO: error codes? or just panic and let the runtime handle it? fn ff_get_object(type_id: u64, ptr: *const u8, len: *mut u8) -> *mut u8; diff --git a/packages/fuel-indexer-schema/src/base.graphql b/packages/fuel-indexer-schema/src/base.graphql index 534ae54dc..4744dd4bc 100644 --- a/packages/fuel-indexer-schema/src/base.graphql +++ b/packages/fuel-indexer-schema/src/base.graphql @@ -13,9 +13,10 @@ scalar Timestamp scalar Color scalar ContractId scalar Salt -scalar Jsonb +scalar Json scalar MessageId -scalar String255 +scalar Charfield +scalar Identity enum IndexType { Btree, diff --git a/packages/fuel-indexer-schema/src/explorer.graphql b/packages/fuel-indexer-schema/src/explorer.graphql index 6fe1f7819..0b8334813 100644 --- a/packages/fuel-indexer-schema/src/explorer.graphql +++ b/packages/fuel-indexer-schema/src/explorer.graphql @@ -37,28 +37,28 @@ # timestamp: Int8! # from: Address! # to: Address! -# tokens_transferred: Jsonb! +# tokens_transferred: Json! # value: UInt8! # transaction_fee: UInt8! # gas_limit: UInt8! # gas_used: UInt8! # gas_price: UInt8! # nonce: Int8! -# input_data: Jsonb! +# input_data: Json! # } # type Account { # address: Address! # balance: UInt8! -# tokens: Jsonb! +# tokens: Json! # } # type Contract { # creator: Address! # creation_transaction: Transaction! -# source_code: Jsonb! -# abi: Jsonb! -# creation_code: Jsonb! +# source_code: Json! +# abi: Json! +# creation_code: Json! # events: Bytes32! # } diff --git a/packages/fuel-indexer-schema/src/lib.rs b/packages/fuel-indexer-schema/src/lib.rs index c880c70ae..c785a9640 100644 --- a/packages/fuel-indexer-schema/src/lib.rs +++ b/packages/fuel-indexer-schema/src/lib.rs @@ -2,23 +2,21 @@ extern crate alloc; use crate::sql_types::ColumnType; use core::convert::TryInto; use fuel_indexer_types::{ - Address, AssetId, Bytes32, Bytes4, Bytes8, ContractId, Jsonb, MessageId, Salt, + Address, AssetId, Bytes32, Bytes4, Bytes8, ContractId, Identity, Json, MessageId, + Salt, }; use serde::{Deserialize, Serialize}; pub use fuel_indexer_database_types as sql_types; -pub const BASE_SCHEMA: &str = include_str!("./base.graphql"); -pub const JOIN_DIRECTIVE_NAME: &str = "foreign_key"; -pub const UNIQUE_DIRECTIVE_NAME: &str = "unique"; - #[cfg(feature = "db-models")] pub mod db; - pub mod directives; pub mod utils; -const MAX_STRING_LEN: usize = 255; +pub const BASE_SCHEMA: &str = include_str!("./base.graphql"); +pub const UNIQUE_DIRECTIVE_NAME: &str = "unique"; +const MAX_CHARFIELD_LENGTH: usize = 255; #[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, Hash)] pub enum FtColumn { @@ -35,9 +33,10 @@ pub enum FtColumn { UInt8(u64), Timestamp(i64), Salt(Salt), - Jsonb(Jsonb), + Json(Json), MessageId(MessageId), - String255(String), + Charfield(String), + Identity(Identity), } impl FtColumn { @@ -119,24 +118,27 @@ impl FtColumn { ColumnType::ForeignKey => { panic!("ForeignKey not supported for FtColumn."); } - ColumnType::Jsonb => FtColumn::Jsonb(Jsonb( - String::from_utf8_lossy(&bytes[..size]).to_string(), - )), + ColumnType::Json => { + FtColumn::Json(Json(String::from_utf8_lossy(&bytes[..size]).to_string())) + } ColumnType::MessageId => { let message_id = MessageId::try_from(&bytes[..size]).expect("Invalid slice length"); FtColumn::MessageId(message_id) } - ColumnType::String255 => { - let trimmed: Vec = bytes[..size] - .iter() - .filter_map(|x| if *x != b' ' { Some(*x) } else { None }) - .collect(); - - let s = String::from_utf8_lossy(&trimmed).to_string(); + ColumnType::Charfield => { + let s = String::from_utf8_lossy(&bytes[..size]).trim().to_string(); - assert!(s.len() <= MAX_STRING_LEN, "String255 exceeds max length."); - FtColumn::String255(s) + assert!( + s.len() <= MAX_CHARFIELD_LENGTH, + "Charfield exceeds max length." + ); + FtColumn::Charfield(s) + } + ColumnType::Identity => { + let identity = + Identity::try_from(&bytes[..size]).expect("Invalid slice length"); + FtColumn::Identity(identity) } } } @@ -182,15 +184,19 @@ impl FtColumn { FtColumn::Salt(value) => { format!("'{:x}'", value) } - FtColumn::Jsonb(value) => { + FtColumn::Json(value) => { format!("'{}'", value.0) } FtColumn::MessageId(value) => { format!("'{:x}'", value) } - FtColumn::String255(value) => { + FtColumn::Charfield(value) => { format!("'{}'", value) } + FtColumn::Identity(value) => match value { + Identity::Address(v) => format!("'00{:x}'", v), + Identity::ContractId(v) => format!("'01{:x}'", v), + }, } } } @@ -218,8 +224,10 @@ mod tests { let salt = FtColumn::Salt(Salt::try_from([0x31; 32]).expect("Bad bytes")); let message_id = FtColumn::MessageId(MessageId::try_from([0x0F; 32]).expect("Bad bytes")); - let string255 = FtColumn::String255(String::from("hello world")); - let jsonb = FtColumn::Jsonb(Jsonb(r#"{"hello":"world"}"#.to_string())); + let charfield = FtColumn::Charfield(String::from("hello world")); + let json = FtColumn::Json(Json(r#"{"hello":"world"}"#.to_string())); + let identity = + FtColumn::Identity(Identity::Address(Address::try_from([0x12; 32]).unwrap())); insta::assert_yaml_snapshot!(id.query_fragment()); insta::assert_yaml_snapshot!(addr.query_fragment()); @@ -235,7 +243,8 @@ mod tests { insta::assert_yaml_snapshot!(uint8.query_fragment()); insta::assert_yaml_snapshot!(int64.query_fragment()); insta::assert_yaml_snapshot!(message_id.query_fragment()); - insta::assert_yaml_snapshot!(string255.query_fragment()); - insta::assert_yaml_snapshot!(jsonb.query_fragment()) + insta::assert_yaml_snapshot!(charfield.query_fragment()); + insta::assert_yaml_snapshot!(json.query_fragment()); + insta::assert_yaml_snapshot!(identity.query_fragment()); } } diff --git a/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-15.snap b/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-15.snap index 37d3d8d11..92c69f075 100644 --- a/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-15.snap +++ b/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-15.snap @@ -1,7 +1,7 @@ --- source: fuel-indexer-schema/src/lib.rs assertion_line: 234 -expression: string255.query_fragment() +expression: Charfield.query_fragment() --- "'hello world'" diff --git a/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-16.snap b/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-16.snap index fe5c944f6..fbb4367ca 100644 --- a/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-16.snap +++ b/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-16.snap @@ -1,7 +1,7 @@ --- source: fuel-indexer-schema/src/lib.rs assertion_line: 236 -expression: jsonb.query_fragment() +expression: Json.query_fragment() --- "'{\"hello\":\"world\"}'" diff --git a/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-17.snap b/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-17.snap new file mode 100644 index 000000000..648e82bd0 --- /dev/null +++ b/packages/fuel-indexer-schema/src/snapshots/fuel_indexer_schema__tests__fragments-17.snap @@ -0,0 +1,7 @@ +--- +source: fuel-indexer-schema/src/lib.rs +assertion_line: 237 +expression: Identity.query_fragment() +--- +"'001212121212121212121212121212121212121212121212121212121212121212'" + diff --git a/packages/fuel-indexer-schema/src/utils.rs b/packages/fuel-indexer-schema/src/utils.rs index d56ed14e3..0ab584022 100644 --- a/packages/fuel-indexer-schema/src/utils.rs +++ b/packages/fuel-indexer-schema/src/utils.rs @@ -265,9 +265,9 @@ type Tx { id: Bytes32! @unique block: Block! timestamp: Int8! - status: Jsonb! + status: Json! value: UInt8! - tokens_transferred: Jsonb! + tokens_transferred: Json! } type Account { diff --git a/packages/fuel-indexer-tests/Cargo.toml b/packages/fuel-indexer-tests/Cargo.toml index e6780215f..71775b04a 100644 --- a/packages/fuel-indexer-tests/Cargo.toml +++ b/packages/fuel-indexer-tests/Cargo.toml @@ -38,6 +38,7 @@ fuels = { version = "0.30", features = ["fuel-core-lib"] } fuels-abigen-macro = "0.30" fuels-core = "0.30" futures = "0.3" +hex = "0.4" itertools = "0.10" rand = "0.8" reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } diff --git a/packages/fuel-indexer-tests/assets/explorer_index.wasm b/packages/fuel-indexer-tests/assets/explorer_index.wasm index 98d08b434..ac13cbbd3 100644 Binary files a/packages/fuel-indexer-tests/assets/explorer_index.wasm and b/packages/fuel-indexer-tests/assets/explorer_index.wasm differ diff --git a/packages/fuel-indexer-tests/assets/fuel_indexer_test.graphql b/packages/fuel-indexer-tests/assets/fuel_indexer_test.graphql index 02763209d..99b26d69c 100644 --- a/packages/fuel-indexer-tests/assets/fuel_indexer_test.graphql +++ b/packages/fuel-indexer-tests/assets/fuel_indexer_test.graphql @@ -26,6 +26,7 @@ type PungEntity { id: ID! value: UInt8! is_pung: Int4! + pung_from: Identity! } type Block { @@ -38,7 +39,7 @@ type Block { type Tx { id: Bytes32! @unique block: Block! - input_data: Jsonb! + input_data: Json! timestamp: Int8! } diff --git a/packages/fuel-indexer-tests/assets/fuel_indexer_test.wasm b/packages/fuel-indexer-tests/assets/fuel_indexer_test.wasm index c3885b2a9..6904dc51c 100644 Binary files a/packages/fuel-indexer-tests/assets/fuel_indexer_test.wasm and b/packages/fuel-indexer-tests/assets/fuel_indexer_test.wasm differ diff --git a/packages/fuel-indexer-tests/assets/simple_wasm.wasm b/packages/fuel-indexer-tests/assets/simple_wasm.wasm index 277743358..94d980186 100644 Binary files a/packages/fuel-indexer-tests/assets/simple_wasm.wasm and b/packages/fuel-indexer-tests/assets/simple_wasm.wasm differ diff --git a/packages/fuel-indexer-tests/components/indices/fuel-indexer-test/src/lib.rs b/packages/fuel-indexer-tests/components/indices/fuel-indexer-test/src/lib.rs index faa8e83f7..7f6fe6836 100644 --- a/packages/fuel-indexer-tests/components/indices/fuel-indexer-test/src/lib.rs +++ b/packages/fuel-indexer-tests/components/indices/fuel-indexer-test/src/lib.rs @@ -1,6 +1,6 @@ extern crate alloc; use fuel_indexer_macros::indexer; -use fuel_indexer_plugin::{types::Bytes32, utils::sha256_digest}; +use fuel_indexer_plugin::prelude::*; // Copied over from the block-explorer example pub fn derive_id(id: [u8; 32], data: Vec) -> Bytes32 { @@ -40,7 +40,7 @@ mod fuel_indexer_test { id: tx.id, block: block.id, timestamp: block_data.time, - input_data: Jsonb(input_data.clone()), + input_data: Json(input_data.clone()), }; tx.save(); } @@ -120,6 +120,8 @@ mod fuel_indexer_test { id: 1, value: logdata_entity.value, is_pung: 1, + // TODO: https://github.com/FuelLabs/fuel-indexer/issues/386 + pung_from: Identity::from(logdata_entity.pung_from), }; entity.save(); diff --git a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/Forc.lock b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/Forc.lock index db98a95f0..00f486e30 100644 --- a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/Forc.lock +++ b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/Forc.lock @@ -1,6 +1,6 @@ [[package]] name = 'core' -source = 'path+from-root-0EEC2240A883E0FE' +source = 'path+from-root-AE2967028D5567DA' [[package]] name = 'fuel-indexer-test' @@ -9,5 +9,5 @@ dependencies = ['std'] [[package]] name = 'std' -source = 'git+https://github.com/fuellabs/sway?tag=v0.30.1#158f22115bf000d45862be95ff7fdb0ff5bdee4d' +source = 'git+https://github.com/fuellabs/sway?tag=v0.31.1#c32b0759d25c0b515cbf535f9fb9b8e6fda38ff2' dependencies = ['core'] diff --git a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test-abi.json b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test-abi.json index a8d01f664..00b100b04 100644 --- a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test-abi.json +++ b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test-abi.json @@ -8,79 +8,131 @@ }, { "typeId": 1, - "type": "bool", + "type": "b256", "components": null, "typeParameters": null }, { "typeId": 2, - "type": "str[32]", + "type": "bool", "components": null, "typeParameters": null }, { "typeId": 3, + "type": "enum Identity", + "components": [ + { + "name": "Address", + "type": 5, + "typeArguments": null + }, + { + "name": "ContractId", + "type": 6, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 4, + "type": "str[32]", + "components": null, + "typeParameters": null + }, + { + "typeId": 5, + "type": "struct Address", + "components": [ + { + "name": "value", + "type": 1, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 6, + "type": "struct ContractId", + "components": [ + { + "name": "value", + "type": 1, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 7, "type": "struct Ping", "components": [ { "name": "id", - "type": 6, + "type": 10, "typeArguments": null }, { "name": "value", - "type": 6, + "type": 10, "typeArguments": null }, { "name": "message", - "type": 2, + "type": 4, "typeArguments": null } ], "typeParameters": null }, { - "typeId": 4, + "typeId": 8, "type": "struct Pong", "components": [ { "name": "id", - "type": 6, + "type": 10, "typeArguments": null }, { "name": "value", - "type": 6, + "type": 10, "typeArguments": null } ], "typeParameters": null }, { - "typeId": 5, + "typeId": 9, "type": "struct Pung", "components": [ { "name": "id", - "type": 6, + "type": 10, "typeArguments": null }, { "name": "value", - "type": 6, + "type": 10, "typeArguments": null }, { "name": "is_pung", - "type": 1, + "type": 2, + "typeArguments": null + }, + { + "name": "pung_from", + "type": 3, "typeArguments": null } ], "typeParameters": null }, { - "typeId": 6, + "typeId": 10, "type": "u64", "components": null, "typeParameters": null @@ -89,25 +141,25 @@ "functions": [ { "inputs": [], - "name": "trigger_ping", + "name": "trigger_log", "output": { "name": "", - "type": 3, + "type": 0, "typeArguments": null } }, { "inputs": [], - "name": "trigger_pong", + "name": "trigger_logdata", "output": { "name": "", - "type": 4, + "type": 0, "typeArguments": null } }, { "inputs": [], - "name": "trigger_transfer", + "name": "trigger_messageout", "output": { "name": "", "type": 0, @@ -116,19 +168,19 @@ }, { "inputs": [], - "name": "trigger_log", + "name": "trigger_ping", "output": { "name": "", - "type": 0, + "type": 7, "typeArguments": null } }, { "inputs": [], - "name": "trigger_logdata", + "name": "trigger_pong", "output": { "name": "", - "type": 0, + "type": 8, "typeArguments": null } }, @@ -143,7 +195,7 @@ }, { "inputs": [], - "name": "trigger_transferout", + "name": "trigger_transfer", "output": { "name": "", "type": 0, @@ -152,7 +204,7 @@ }, { "inputs": [], - "name": "trigger_messageout", + "name": "trigger_transferout", "output": { "name": "", "type": 0, @@ -165,7 +217,7 @@ "logId": 0, "loggedType": { "name": "", - "type": 6, + "type": 10, "typeArguments": null } }, @@ -173,7 +225,7 @@ "logId": 1, "loggedType": { "name": "", - "type": 5, + "type": 9, "typeArguments": [] } }, @@ -181,7 +233,7 @@ "logId": 2, "loggedType": { "name": "", - "type": 6, + "type": 10, "typeArguments": null } } diff --git a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test.bin b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test.bin index a2c46e624..54b405bd1 100644 Binary files a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test.bin and b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/out/debug/fuel-indexer-test.bin differ diff --git a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/src/main.sw b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/src/main.sw index d9a959509..c094bf433 100644 --- a/packages/fuel-indexer-tests/contracts/fuel-indexer-test/src/main.sw +++ b/packages/fuel-indexer-tests/contracts/fuel-indexer-test/src/main.sw @@ -26,6 +26,7 @@ pub struct Pung { id: u64, value: u64, is_pung: bool, + pung_from: Identity, } abi FuelIndexer { @@ -71,6 +72,7 @@ impl FuelIndexer for Contract { id: 1, value: 456, is_pung: true, + pung_from: Identity::Address(Address::from(0x532ee5fb2cabec472409eb5f9b42b59644edb7bf9943eda9c2e3947305ed5e96)), }; log(p); } diff --git a/packages/fuel-indexer-tests/tests/e2e.rs b/packages/fuel-indexer-tests/tests/e2e.rs index f7042c793..8505a8250 100644 --- a/packages/fuel-indexer-tests/tests/e2e.rs +++ b/packages/fuel-indexer-tests/tests/e2e.rs @@ -3,6 +3,8 @@ use fuel_indexer_tests::{ defaults, fixtures::{http_client, postgres_connection}, }; +use fuel_indexer_types::{Address, Identity}; +use hex::FromHex; use sqlx::Row; use tokio::time::{sleep, Duration}; @@ -150,9 +152,20 @@ async fn test_can_trigger_and_index_logdata_event() { let value: i64 = row.get(1); let is_pung: i32 = row.get(2); + let pung_from: String = row.get(3); + let from_buff = <[u8; 33]>::from_hex(&pung_from).unwrap(); + + let addr_buff = <[u8; 32]>::from_hex( + "532ee5fb2cabec472409eb5f9b42b59644edb7bf9943eda9c2e3947305ed5e96", + ) + .unwrap(); assert_eq!(value, 456); assert_eq!(is_pung, 1); + assert_eq!( + Identity::from(from_buff), + Identity::Address(Address::from(addr_buff)), + ); } #[tokio::test] diff --git a/packages/fuel-indexer-tests/trybuild/fail_if_attribute_abi_arg_includes_invalid_type.stderr b/packages/fuel-indexer-tests/trybuild/fail_if_attribute_abi_arg_includes_invalid_type.stderr index f783e354d..57a6ab3ef 100644 --- a/packages/fuel-indexer-tests/trybuild/fail_if_attribute_abi_arg_includes_invalid_type.stderr +++ b/packages/fuel-indexer-tests/trybuild/fail_if_attribute_abi_arg_includes_invalid_type.stderr @@ -32,7 +32,7 @@ error[E0412]: cannot find type `BlockData` in this scope | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::fuel::BlockData; +2 | use fuel_indexer_plugin::prelude::fuel::BlockData; | 2 | use fuel_indexer_types::abi::BlockData; | @@ -45,7 +45,7 @@ error[E0422]: cannot find struct, variant or union type `BlockData` in this scop | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::fuel::BlockData; +2 | use fuel_indexer_plugin::prelude::fuel::BlockData; | 2 | use fuel_indexer_types::abi::BlockData; | @@ -58,7 +58,7 @@ error[E0422]: cannot find struct, variant or union type `TransactionData` in thi | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::fuel::TransactionData; +2 | use fuel_indexer_plugin::prelude::fuel::TransactionData; | 2 | use fuel_indexer_types::abi::TransactionData; | @@ -71,7 +71,7 @@ error[E0433]: failed to resolve: use of undeclared type `TransactionStatus` | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::tx::TransactionStatus; +2 | use fuel_indexer_plugin::prelude::tx::TransactionStatus; | 2 | use fuel_indexer_types::tx::TransactionStatus; | @@ -89,7 +89,7 @@ error[E0433]: failed to resolve: use of undeclared type `Transaction` | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::tx::Transaction; +2 | use fuel_indexer_plugin::prelude::tx::Transaction; | 2 | use fuel_indexer_types::tx::Transaction; | diff --git a/packages/fuel-indexer-tests/trybuild/fail_if_attribute_schema_arg_is_invalid.stderr b/packages/fuel-indexer-tests/trybuild/fail_if_attribute_schema_arg_is_invalid.stderr index 98d3125b1..09c6058c8 100644 --- a/packages/fuel-indexer-tests/trybuild/fail_if_attribute_schema_arg_is_invalid.stderr +++ b/packages/fuel-indexer-tests/trybuild/fail_if_attribute_schema_arg_is_invalid.stderr @@ -32,7 +32,7 @@ error[E0412]: cannot find type `BlockData` in this scope | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::fuel::BlockData; +2 | use fuel_indexer_plugin::prelude::fuel::BlockData; | 2 | use fuel_indexer_types::abi::BlockData; | @@ -45,7 +45,7 @@ error[E0422]: cannot find struct, variant or union type `BlockData` in this scop | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::fuel::BlockData; +2 | use fuel_indexer_plugin::prelude::fuel::BlockData; | 2 | use fuel_indexer_types::abi::BlockData; | @@ -58,7 +58,7 @@ error[E0422]: cannot find struct, variant or union type `TransactionData` in thi | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::fuel::TransactionData; +2 | use fuel_indexer_plugin::prelude::fuel::TransactionData; | 2 | use fuel_indexer_types::abi::TransactionData; | @@ -71,7 +71,7 @@ error[E0433]: failed to resolve: use of undeclared type `TransactionStatus` | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::tx::TransactionStatus; +2 | use fuel_indexer_plugin::prelude::tx::TransactionStatus; | 2 | use fuel_indexer_types::tx::TransactionStatus; | @@ -89,7 +89,7 @@ error[E0433]: failed to resolve: use of undeclared type `Transaction` | help: consider importing one of these items | -2 | use fuel_indexer_plugin::types::tx::Transaction; +2 | use fuel_indexer_plugin::prelude::tx::Transaction; | 2 | use fuel_indexer_types::tx::Transaction; | diff --git a/packages/fuel-indexer-types/Cargo.toml b/packages/fuel-indexer-types/Cargo.toml index 2f74e7aee..58986d57c 100644 --- a/packages/fuel-indexer-types/Cargo.toml +++ b/packages/fuel-indexer-types/Cargo.toml @@ -12,4 +12,5 @@ fuel-indexer-lib = { version = "0.1", path = "../fuel-indexer-lib" } fuel-tx = { version = "0.23", features = ["serde"] } fuel-types = "0.5" fuels-core = "0.30" +fuels-types = "0.30" serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } \ No newline at end of file diff --git a/packages/fuel-indexer-types/src/abi.rs b/packages/fuel-indexer-types/src/abi.rs index 9ae2caa8c..7965ea914 100644 --- a/packages/fuel-indexer-types/src/abi.rs +++ b/packages/fuel-indexer-types/src/abi.rs @@ -2,8 +2,13 @@ use crate::{ tx::{Transaction, TransactionStatus, TxId}, Address, AssetId, Bytes32, ContractId, MessageId, }; +use core::array::TryFromSliceError; use fuel_indexer_lib::utils::type_id; pub use fuel_tx::Receipt; +pub use fuels_core::{Parameterize, Token, Tokenizable}; +use fuels_types::{ + enum_variants::EnumVariants, errors::Error as SDKError, param_types::ParamType, +}; use serde::{Deserialize, Serialize}; pub const FUEL_TYPES_NAMESPACE: &str = "fuel"; @@ -13,7 +18,6 @@ pub trait NativeFuelType { } // TODO: https://github.com/FuelLabs/fuel-indexer/issues/285 - #[derive(Deserialize, Serialize, Debug, Clone, Default)] pub struct TransactionData { pub transaction: Transaction, @@ -126,3 +130,122 @@ impl NativeFuelType for MessageOut { type_id(FUEL_TYPES_NAMESPACE, "MessageOut") as usize } } + +const IDENTITY_LEN: usize = 33; + +// TODO: https://github.com/FuelLabs/fuel-indexer/issues/386 +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Identity { + Address(fuel_tx::Address), + ContractId(fuel_tx::ContractId), +} + +impl Tokenizable for Identity { + fn from_token(token: Token) -> Result { + if let Token::Enum(enum_selector) = token { + match *enum_selector { + (0u8, token, _) => Ok(Identity::Address(Address::from_token(token)?)), + (1u8, token, _) => { + Ok(Identity::ContractId(ContractId::from_token(token)?)) + } + (_, _, _) => Err(SDKError::InstantiationError(format!( + "Could not construct Identity from enum_selector. Received: {:?}", + enum_selector + ))), + } + } else { + Err(SDKError::InstantiationError(format!( + "Could not construct Identity from token. Received: {:?}", + token + ))) + } + } + fn into_token(self) -> Token { + let (dis, tok) = match self { + Self::Address(value) => (0u8, value.into_token()), + Self::ContractId(value) => (1u8, value.into_token()), + }; + if let ParamType::Enum { variants, .. } = Self::param_type() { + let selector = (dis, tok, variants); + Token::Enum(Box::new(selector)) + } else { + panic!("should never happen as Identity::param_type() returns valid Enum variants"); + } + } +} + +impl Parameterize for Identity { + fn param_type() -> ParamType { + let param_types = vec![Address::param_type(), ContractId::param_type()]; + let variants = EnumVariants::new(param_types) + .expect("should never happen as we provided valid Identity param types"); + ParamType::Enum { + variants, + generics: vec![], + } + } +} + +impl From<[u8; IDENTITY_LEN]> for Identity { + fn from(bytes: [u8; IDENTITY_LEN]) -> Self { + match bytes.first() { + Some(0u8) => Self::Address(Address::try_from(&bytes[1..]).expect("Failed")), + Some(1u8) => { + Self::ContractId(ContractId::try_from(&bytes[1..]).expect("Failed")) + } + _ => panic!("Failed"), + } + } +} + +impl From for [u8; IDENTITY_LEN] { + fn from(salt: Identity) -> [u8; IDENTITY_LEN] { + match salt { + Identity::Address(v) => { + let mut buff: [u8; IDENTITY_LEN] = [0u8; IDENTITY_LEN]; + buff[1..].copy_from_slice(v.as_ref()); + buff + } + Identity::ContractId(v) => { + let mut buff: [u8; IDENTITY_LEN] = [1u8; IDENTITY_LEN]; + buff[1..].copy_from_slice(v.as_ref()); + buff + } + } + } +} + +impl From for Address { + fn from(ident: Identity) -> Address { + match ident { + Identity::Address(v) => v, + _ => panic!("Conversion expects Address."), + } + } +} + +impl From for ContractId { + fn from(ident: Identity) -> ContractId { + match ident { + Identity::ContractId(v) => v, + _ => panic!("Conversion expects ContractId."), + } + } +} + +impl TryFrom<&[u8]> for Identity { + type Error = TryFromSliceError; + + fn try_from(bytes: &[u8]) -> Result { + <[u8; IDENTITY_LEN]>::try_from(bytes).map(|b| b.into()) + } +} + +impl From for Identity { + fn from(ident: fuels_core::Identity) -> Self { + match ident { + fuels_core::Identity::ContractId(v) => Self::ContractId(v), + fuels_core::Identity::Address(v) => Self::Address(v), + } + } +} diff --git a/packages/fuel-indexer-types/src/lib.rs b/packages/fuel-indexer-types/src/lib.rs index 240a4819f..50100d5a1 100644 --- a/packages/fuel-indexer-types/src/lib.rs +++ b/packages/fuel-indexer-types/src/lib.rs @@ -3,19 +3,21 @@ pub mod ffi; pub mod graphql; pub mod tx; +pub use crate::abi::Identity; pub use fuel_types::{ Address, AssetId, Bytes32, Bytes4, Bytes8, ContractId, MessageId, Salt, Word, }; pub use fuels_core::types::Bits256; use serde::{Deserialize, Serialize}; +pub type Error = Box; pub type ID = u64; pub type Int4 = i32; pub type Int8 = i64; pub type UInt4 = u32; pub type UInt8 = u64; pub type Timestamp = u64; -pub type String255 = String; +pub type Charfield = String; #[derive(Deserialize, Serialize, Clone, Eq, PartialEq, Debug, Hash)] -pub struct Jsonb(pub String); +pub struct Json(pub String); diff --git a/packages/fuel-indexer-types/src/tx.rs b/packages/fuel-indexer-types/src/tx.rs index fbfb50752..4c0a3e164 100644 --- a/packages/fuel-indexer-types/src/tx.rs +++ b/packages/fuel-indexer-types/src/tx.rs @@ -1,4 +1,4 @@ -use crate::Jsonb; +use crate::Json; use chrono::{DateTime, NaiveDateTime, Utc}; pub use fuel_tx::{field::*, Receipt, ScriptExecutionResult, Transaction, TxId}; use serde::{Deserialize, Serialize}; @@ -36,23 +36,23 @@ impl Default for TransactionStatus { } } -impl From for Jsonb { - fn from(t: TransactionStatus) -> Jsonb { +impl From for Json { + fn from(t: TransactionStatus) -> Json { match t { TransactionStatus::Failure { block_id, time, reason, - } => Jsonb(format!( + } => Json(format!( r#"{{"status":"failed","block":"{block_id}","time":"{time}","reason":"{reason}"}}"# )), - TransactionStatus::SqueezedOut { reason } => Jsonb(format!( + TransactionStatus::SqueezedOut { reason } => Json(format!( r#"{{"status":"squeezed_out","reason":"{reason}"}}"# )), - TransactionStatus::Submitted { submitted_at } => Jsonb(format!( + TransactionStatus::Submitted { submitted_at } => Json(format!( r#"{{"status":"submitted","time":"{submitted_at}"}}"# )), - TransactionStatus::Success { block_id, time } => Jsonb(format!( + TransactionStatus::Success { block_id, time } => Json(format!( r#"{{"status":"success","block":"{block_id}","time":"{time}"}}"# )), }