# API to get the accumulated transferred IOTA tokens 1) Define the table (`IotaTokenKey <MilestoneIndex> <TransferredTokens> ...`) - Refer to sync table ```sql CREATE TABLE IF NOT EXISTS {0}.analytics ( key text, milestone_index int, transaction_count int, message_count int, transferred_tokens bigint, PRIMARY KEY (key, milestone_index) ) WITH CLUSTERING ORDER BY (milestone_index DESC); ``` ```rust impl Insert<IotaTokenKey, TransferredTokenRecord> impl Select<IotaTokenKeyRange, Iter<TransferredTokenRecord>> /// Defines a range of milestone indexes to be synced #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] pub struct IotaTokenKeyRange { /// First milestone index (inclusive) pub from: u32, /// Last milestone index (exclusive) pub to: u32, } impl Default for IotaTokenKeyRange { fn default() -> Self { Self { from: 1, to: i32::MAX as u32, } } } /// An 'IOTA token' table row #[derive(Clone, Copy, Debug)] pub struct TransferredTokenRecord { pub milestone_index: MilestoneIndex, pub transferred_tokens: Option<Amount>, } impl TransferredTokenRecord { /// Creates a new sync row pub fn new(milestone_index: MilestoneIndex, transferred_tokens: Option<Amount>) -> Self { Self { milestone_index, transferred_tokens, } } } ``` 2) Sketch the storing flow (might be associated with the solidifier) - Current design - Feed sources to collectors to solidifiers to archiver - Solidifier collects the MilestoneData - After ```rust= let request = self .keyspace .insert(&sync_key, &synced_record) .consistency(Consistency::One) .build()?; ``` 3) Sketch the querying - Similar to ```rust /// Representation of the database sync data #[derive(Debug, Clone, Default, Serialize)] pub struct IotaTokens { /// The completed(synced and logged) milestones data or synced but unlogged pub(crate) processed_milestone_ranges: Vec<Range<u32>>, /// The accumulated transferred tokens pub(crate) tokens: u64, } #[get("/<keyspace>/tokens")] async fn sync(keyspaces: State<'_, HashSet<String>>, keyspace: String) -> Result<Json<IotaTokens>, ListenerError> { if !keyspaces.contains(&keyspace) { return Err(ListenerError::InvalidKeyspace(keyspace)); } let keyspace = ChronicleKeyspace::new(keyspace); IotaTokens::try_fetch(&keyspace, &SyncRange::default(), 3) .await .map(|s| Json(s)) .map_err(|e| ListenerError::Other(e.into())) } ``` # Query and Response ## CQL ```sql= SELECT milestone_index, message_count, transaction_count, transferred_tokens FROM {}.analytics WHERE key = ? AND milestone_index >= ? AND milestone_index < ?; ``` ```sql= "INSERT INTO {}.sync (key, milestone_index, message_count, transaction_count, transferred_tokens) VALUES (?, ?, ?, ?, ?)", ``` * Create `analytics` table ```sql= CREATE TABLE IF NOT EXISTS {}.analytics ( key text, milestone_index int, message_count int, transaction_count int, transferred_tokens bigint, PRIMARY KEY (key, milestone_index) ) WITH CLUSTERING ORDER BY (milestone_index DESC); ``` ## Query ``` /api/<keyspace>/analytics?[start=<u32>&end=<u32>] ``` ### Defaults - `start`: 1 - `end`: latest_milestone ## Response The response will return multiple records if there are gaps in synced data ```json= { [ "start": u32, "end": u32, "transaction_count": u64, "message_count": u64, "transferred_tokens": u128, ] } ``` ## Tasks - MilestoneData info method - Broker and Data cql part - Api