From adc1b4a9c73f01d66b01d80e283cc9147ddb230a Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Mon, 2 Dec 2024 12:14:41 +0700 Subject: [PATCH] Add decoding of contract_log events of InterruptChainEvent --- .../migrations/0001_initialize.up.sql | 30 ---- backend-rust/src/graphql_api.rs | 133 +++++++++--------- 2 files changed, 68 insertions(+), 95 deletions(-) diff --git a/backend-rust/migrations/0001_initialize.up.sql b/backend-rust/migrations/0001_initialize.up.sql index 8e086f94..623a873f 100644 --- a/backend-rust/migrations/0001_initialize.up.sql +++ b/backend-rust/migrations/0001_initialize.up.sql @@ -373,36 +373,6 @@ CREATE INDEX event_index_per_contract_idx ON contract_events (event_index_per_co -- Important for quickly filtering contract events by a specific contract. CREATE INDEX contract_events_idx ON contract_events (contract_index, contract_sub_index); --- All CIS2 tokens. A token is added in this table whenever a `CIS2 mint event` is logged for the first --- time by a contract claiming to follow the `CIS2 standard`. -CREATE TABLE tokens ( - -- An index/id for the token (row number). - index - BIGINT GENERATED ALWAYS AS IDENTITY - PRIMARY KEY, - -- Contract index that the event is associated with. - contract_index - BIGINT - NOT NULL, - -- Contract subindex that the event is associated with. - contract_sub_index - BIGINT - NOT NULL, - -- Unique token name (hash of ...). - token_name - TEXT - NOT NULL, - -- Metadata url. - metadata_url - TEXT - NOT NULL, - -- Accumulated total supply of the token calculated by considering all `mint/burn` events associated - -- to the token. - total_supply - BIGINT - NOT NULL -); - -- Important for quickly filtering tokens by token_name. CREATE INDEX tokens_idx ON tokens (token_name); diff --git a/backend-rust/src/graphql_api.rs b/backend-rust/src/graphql_api.rs index 1070e503..92053070 100644 --- a/backend-rust/src/graphql_api.rs +++ b/backend-rust/src/graphql_api.rs @@ -820,62 +820,9 @@ LIMIT 30", // WHERE slot_time > (LOCALTIMESTAMP - $1::interval) // the elements in the list that come after the specified cursor." after: // String "Returns the last _n_ elements from the list." last: Int "Returns // the elements in the list that come before the specified cursor." before: - // String): TokensConnection - // TODO - // token(contractIndex: UnsignedLong! + // String): TokensConnection token(contractIndex: UnsignedLong! // contractSubIndex: UnsignedLong! tokenId: String!): Token! - async fn token<'a>( - &self, - ctx: &Context<'a>, - contract_address_index: ContractIndex, - contract_address_sub_index: ContractIndex, - token_id: String, - ) -> ApiResult { - let pool = get_pool(ctx)?; - - // let row = sqlx::query!( - // r#"SELECT - // module_reference, - // name as contract_name, - // contracts.amount, - // blocks.slot_time as block_slot_time, - // transactions.block_height, - // transactions.hash as transaction_hash, - // accounts.address as creator - // FROM contracts - // JOIN transactions ON transaction_index = transactions.index - // JOIN blocks ON transactions.block_height = blocks.height - // JOIN accounts ON transactions.sender = accounts.index - // WHERE contracts.index = $1 AND contracts.sub_index = $2"#, - // contract_address_index.0 as i64, - // contract_address_sub_index.0 as i64, - // ) - // .fetch_optional(pool) - // .await? - // .ok_or(ApiError::NotFound)?; - - // let snapshot = ContractSnapshot { - // block_height: row.block_height, - // contract_address_index, - // contract_address_sub_index, - // contract_name: row.contract_name, - // module_reference: row.module_reference, - // amount: row.amount, - // }; - - Ok(Token { - initial_transaction: Transaction, - contract_index: ContractIndex, - contract_sub_index: ContractIndex, - token_id: String, - metadata_url: String, - total_supply: BigInteger, - contract_address_formatted: String, - token_address: String, - }) - } - async fn contract<'a>( &self, ctx: &Context<'a>, @@ -4008,7 +3955,8 @@ pub fn events_from_summary( address, events, } => Ok(Event::ContractInterrupted(ContractInterrupted { - contract_address: address.into(), + contract_address: address.into(), + contract_logs_raw: events.iter().map(|e| e.as_ref().to_vec()).collect(), })), ContractTraceElement::Resumed { address, @@ -5115,15 +5063,70 @@ pub struct ChainUpdatePayload { #[derive(SimpleObject, serde::Serialize, serde::Deserialize)] pub struct ContractInterrupted { - contract_address: ContractAddress, - // eventsAsHex("Returns the first _n_ elements from the list." first: Int "Returns the elements - // in the list that come after the specified cursor." after: String "Returns the last _n_ - // elements from the list." last: Int "Returns the elements in the list that come before the - // specified cursor." before: String): StringConnection events("Returns the first _n_ - // elements from the list." first: Int "Returns the elements in the list that come after the - // specified cursor." after: String "Returns the last _n_ elements from the list." last: Int - // "Returns the elements in the list that come before the specified cursor." before: String): - // StringConnection + contract_address: ContractAddress, + // All logged events by the smart contract during this section of the transaction execution. + contract_logs_raw: Vec>, +} + +#[ComplexObject] +impl ContractInterrupted { + async fn events_as_hex(&self) -> ApiResult> { + let mut connection = connection::Connection::new(true, true); + + self.contract_logs_raw.iter().enumerate().for_each(|(index, log)| { + connection.edges.push(connection::Edge::new(index.to_string(), hex::encode(log))); + }); + + // Nice-to-have: pagination info but not used at front-end currently. + + Ok(connection) + } + + async fn events<'a>( + &self, + ctx: &Context<'a>, + ) -> ApiResult> { + let pool = get_pool(ctx)?; + + let row = sqlx::query!( + " + SELECT + contracts.module_reference as module_reference, + name as contract_name, + schema as display_schema + FROM contracts + JOIN smart_contract_modules ON smart_contract_modules.module_reference = \ + contracts.module_reference + WHERE index = $1 AND sub_index = $2 + ", + self.contract_address.index.0 as i64, + self.contract_address.sub_index.0 as i64 + ) + .fetch_optional(pool) + .await? + .ok_or(ApiError::NotFound)?; + + let opt_event_schema = row + .display_schema + .as_ref() + .and_then(|schema| VersionedModuleSchema::new(schema, &None).ok()) + .and_then(|versioned_schema| { + versioned_schema.get_event_schema(&row.contract_name).ok() + }); + + let mut connection = connection::Connection::new(true, true); + + for (index, log) in self.contract_logs_raw.iter().enumerate() { + let decoded_log = + decode_value_with_schema(opt_event_schema.as_ref(), "event", log, "contract_log"); + + connection.edges.push(connection::Edge::new(index.to_string(), decoded_log)); + } + + // Nice-to-have: pagination info but not used at front-end currently. + + Ok(connection) + } } #[derive(SimpleObject, serde::Serialize, serde::Deserialize)] @@ -5140,7 +5143,7 @@ pub struct ContractUpdated { amount: Amount, receive_name: String, version: ContractVersion, - // All logged events by the smart contract during the transaction execution. + // All logged events by the smart contract during this section of the transaction execution. contract_logs_raw: Vec>, input_parameter: Vec, }