---
# System prepended metadata

title: API to get the accumulated transferred IOTA tokens

---

# 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