# 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