-
Notifications
You must be signed in to change notification settings - Fork 181
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): token balances subscription #2831
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,11 +16,14 @@ | |
SubscribeEntitiesRequest, SubscribeEntityResponse, SubscribeEventMessagesRequest, | ||
SubscribeEventsRequest, SubscribeEventsResponse, SubscribeIndexerRequest, | ||
SubscribeIndexerResponse, SubscribeModelsRequest, SubscribeModelsResponse, | ||
UpdateEntitiesSubscriptionRequest, UpdateEventMessagesSubscriptionRequest, | ||
SubscribeTokenBalancesResponse, UpdateEntitiesSubscriptionRequest, | ||
UpdateEventMessagesSubscriptionRequest, UpdateTokenBalancesSubscriptionRequest, | ||
WorldMetadataRequest, | ||
}; | ||
use crate::types::schema::{Entity, SchemaError}; | ||
use crate::types::{EntityKeysClause, Event, EventQuery, IndexerUpdate, ModelKeysClause, Query}; | ||
use crate::types::{ | ||
EntityKeysClause, Event, EventQuery, IndexerUpdate, ModelKeysClause, Query, TokenBalance, | ||
}; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum Error { | ||
|
@@ -295,6 +298,76 @@ | |
None => empty_state_update(), | ||
})))) | ||
} | ||
|
||
/// Subscribe to token balances. | ||
pub async fn subscribe_token_balances( | ||
&mut self, | ||
contract_addresses: Vec<Felt>, | ||
account_addresses: Vec<Felt>, | ||
) -> Result<TokenBalanceStreaming, Error> { | ||
let request = RetrieveTokenBalancesRequest { | ||
contract_addresses: contract_addresses | ||
.into_iter() | ||
.map(|c| c.to_bytes_be().to_vec()) | ||
.collect(), | ||
account_addresses: account_addresses | ||
.into_iter() | ||
.map(|a| a.to_bytes_be().to_vec()) | ||
.collect(), | ||
}; | ||
let stream = self | ||
.inner | ||
.subscribe_token_balances(request) | ||
.await | ||
.map_err(Error::Grpc) | ||
.map(|res| res.into_inner())?; | ||
Ok(TokenBalanceStreaming(stream.map_ok(Box::new(|res| { | ||
(res.subscription_id, res.balance.unwrap().try_into().expect("must able to serialize")) | ||
})))) | ||
} | ||
Comment on lines
+302
to
+327
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding validation for the unwrap operation. Ohayo, sensei! While the implementation is generally good, the unwrap operation on line 325 could be made more robust. Consider adding proper error handling: Ok(TokenBalanceStreaming(stream.map_ok(Box::new(|res| {
- (res.subscription_id, res.balance.unwrap().try_into().expect("must able to serialize"))
+ (
+ res.subscription_id,
+ res.balance
+ .ok_or_else(|| Error::Schema(SchemaError::MissingExpectedData("balance".to_string())))
+ .and_then(|b| b.try_into())
+ .expect("Failed to deserialize balance")
+ )
}))))
|
||
|
||
/// Update a token balances subscription. | ||
pub async fn update_token_balances_subscription( | ||
&mut self, | ||
subscription_id: u64, | ||
contract_addresses: Vec<Felt>, | ||
account_addresses: Vec<Felt>, | ||
) -> Result<(), Error> { | ||
let request = UpdateTokenBalancesSubscriptionRequest { | ||
subscription_id, | ||
contract_addresses: contract_addresses | ||
.into_iter() | ||
.map(|c| c.to_bytes_be().to_vec()) | ||
.collect(), | ||
account_addresses: account_addresses | ||
.into_iter() | ||
.map(|a| a.to_bytes_be().to_vec()) | ||
.collect(), | ||
}; | ||
self.inner | ||
.update_token_balances_subscription(request) | ||
.await | ||
.map_err(Error::Grpc) | ||
.map(|res| res.into_inner()) | ||
} | ||
} | ||
|
||
type TokenBalanceMappedStream = MapOk< | ||
tonic::Streaming<SubscribeTokenBalancesResponse>, | ||
Box<dyn Fn(SubscribeTokenBalancesResponse) -> (SubscriptionId, TokenBalance) + Send>, | ||
>; | ||
|
||
#[derive(Debug)] | ||
pub struct TokenBalanceStreaming(TokenBalanceMappedStream); | ||
|
||
impl Stream for TokenBalanceStreaming { | ||
type Item = <TokenBalanceMappedStream as Stream>::Item; | ||
fn poll_next( | ||
mut self: std::pin::Pin<&mut Self>, | ||
cx: &mut std::task::Context<'_>, | ||
) -> std::task::Poll<Option<Self::Item>> { | ||
self.0.poll_next_unpin(cx) | ||
} | ||
} | ||
|
||
type ModelDiffMappedStream = MapOk< | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohayo sensei! Watch out for concurrency with "INSERT OR REPLACE".
In high-concurrency environments, multiple updates could lead to race conditions where one subscriber's write might overwrite another's changes. Consider using transactions at a higher level or row-level locking.