Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
fc1e131
add fee collector operation
maciejdfinity Oct 24, 2025
9e7c58d
index build fix
maciejdfinity Oct 24, 2025
8914b52
remove unused conversion
maciejdfinity Oct 27, 2025
440545a
convert back from flattened
maciejdfinity Oct 27, 2025
5ed8d30
clippy
maciejdfinity Oct 28, 2025
b1a7a40
panic for fee col blocks
maciejdfinity Oct 28, 2025
372c508
build fix
maciejdfinity Oct 29, 2025
1bdf8a3
Merge branch 'master' into maciej-feecol-block
maciejdfinity Oct 29, 2025
3dcf6a6
build fix
maciejdfinity Oct 29, 2025
6a30c76
Merge branch 'master' into maciej-feecol-block
maciejdfinity Oct 29, 2025
d010fbd
revert unintentional change
maciejdfinity Oct 29, 2025
42072ed
build fix
maciejdfinity Oct 30, 2025
0398011
update did files
maciejdfinity Oct 30, 2025
cfbbc26
Merge branch 'master' into maciej-feecol-block
maciejdfinity Oct 31, 2025
a5b886c
handle new fee collector in index
maciejdfinity Oct 31, 2025
2fe87b4
add fee collector to approve
maciejdfinity Oct 31, 2025
5d5e118
clippy
maciejdfinity Oct 31, 2025
8102274
refactor get_fee_collector
maciejdfinity Nov 11, 2025
3f83e5f
Merge branch 'master' into maciej-feecol-block
maciejdfinity Nov 11, 2025
cbe5156
clippy
maciejdfinity Nov 11, 2025
b5530ec
change op name to 107set_fee_collector
maciejdfinity Nov 12, 2025
fd4dab5
add fee collector block generation to the test block builder
maciejdfinity Nov 12, 2025
08aba67
fix block conversion, caller doesn't work still
maciejdfinity Nov 13, 2025
eba90cd
change caller to principal
maciejdfinity Nov 13, 2025
8125a18
build fix
maciejdfinity Nov 13, 2025
a782c66
update did files
maciejdfinity Nov 13, 2025
a201faf
create a proptest
maciejdfinity Nov 13, 2025
5f2c5df
test u64 and u256 blocks
maciejdfinity Nov 13, 2025
f28d46b
change created_at_time to ts
maciejdfinity Nov 13, 2025
eeca831
test index handling of the 107 fee collector
maciejdfinity Nov 13, 2025
c9a3a32
change panic message
maciejdfinity Nov 13, 2025
61d4e0b
build fix
maciejdfinity Nov 13, 2025
975cdc9
clippy
maciejdfinity Nov 13, 2025
50e90ae
test approve fees
maciejdfinity Nov 13, 2025
bfb852e
small refactor
maciejdfinity Nov 14, 2025
17cb2b2
Update rs/ledger_suite/icrc1/index-ng/src/main.rs
maciejdfinity Nov 14, 2025
dc2eb00
Update rs/ledger_suite/icrc1/test_utils/src/icrc3.rs
maciejdfinity Nov 14, 2025
3a858f5
Update rs/ledger_suite/icrc1/index-ng/src/main.rs
maciejdfinity Nov 14, 2025
26b1efc
clippy
maciejdfinity Nov 14, 2025
a08a50f
Merge branch 'master' into maciej-feecol-block
maciejdfinity Nov 14, 2025
de06078
rustdoc for fee collector 107
maciejdfinity Nov 14, 2025
6fa9607
add fee collector operation
maciejdfinity Nov 18, 2025
f3c2280
Update rs/ledger_suite/icrc1/index-ng/src/main.rs
maciejdfinity Nov 18, 2025
fec306f
Merge branch 'master' into maciej-feecol-block
maciejdfinity Nov 18, 2025
3c8283f
update changelog
maciejdfinity Nov 19, 2025
bf53fb8
rename fee_collector to set_fee_collector
maciejdfinity Nov 24, 2025
8f8f140
Merge branch 'master' into maciej-feecol-block
maciejdfinity Nov 24, 2025
b41b566
update canbench results
maciejdfinity Nov 24, 2025
589bb91
Merge branch 'maciej-feecol-block' into maciej-rosetta-feecol
maciejdfinity Nov 24, 2025
32e1968
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Nov 25, 2025
4c38fdb
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Nov 27, 2025
3171dd6
fee collector storing and retrieving from the database
maciejdfinity Nov 27, 2025
015b692
typo
maciejdfinity Nov 28, 2025
74e8054
add simple test
maciejdfinity Nov 28, 2025
e551549
extend the test
maciejdfinity Nov 28, 2025
22272e5
dummy principal for fc block in balances table, test
maciejdfinity Nov 28, 2025
27754a3
remove separate table for fee collectors
maciejdfinity Dec 1, 2025
c45a302
add index on operation_type
maciejdfinity Dec 1, 2025
2a865c1
return error instead of panic if amount is missing
maciejdfinity Dec 1, 2025
3deef11
better error msg
maciejdfinity Dec 1, 2025
d9ac1b5
remove unused FeeCollector rosetta core operation
maciejdfinity Dec 1, 2025
1dd9d73
clippy
maciejdfinity Dec 1, 2025
681d089
clippy
maciejdfinity Dec 1, 2025
774490c
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Dec 1, 2025
21055eb
clippy
maciejdfinity Dec 1, 2025
255084c
remove unused comment
maciejdfinity Dec 1, 2025
e457710
small fixes, implement arb_fee_collector
maciejdfinity Dec 1, 2025
e3b5868
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Dec 2, 2025
51d6341
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Dec 3, 2025
90f6fdd
add btype to rosetta block metadata
maciejdfinity Dec 3, 2025
6a58bb9
remove todos
maciejdfinity Dec 3, 2025
ff5258a
add fee collector to block strategy
maciejdfinity Dec 4, 2025
b61ec33
formatting
maciejdfinity Dec 4, 2025
ee9d541
clippy
maciejdfinity Dec 4, 2025
d02ea5d
Merge branch 'maciej-rosetta-feecol' of github.com:dfinity/ic into ma…
maciejdfinity Dec 4, 2025
84c1536
clippy
maciejdfinity Dec 4, 2025
04a9925
clippy
maciejdfinity Dec 4, 2025
6837917
formatting
maciejdfinity Dec 4, 2025
5d0e1b4
fix search transactions test
maciejdfinity Dec 5, 2025
e0d7205
change to valid blockchain strategy
maciejdfinity Dec 5, 2025
78e974a
remove line
maciejdfinity Dec 5, 2025
a8f8a85
ignore legacy fc test
maciejdfinity Dec 5, 2025
76cb1d3
use separate fee collector table instead of index on operation_type
maciejdfinity Dec 6, 2025
b9aa459
fix, record to and from pricipals
maciejdfinity Dec 6, 2025
19f0491
improve tests
maciejdfinity Dec 7, 2025
f817488
store fee collector information in rosetta_metadata table
maciejdfinity Dec 8, 2025
197b781
store highest processed block index in metadata
maciejdfinity Dec 8, 2025
c7eda70
clippy
maciejdfinity Dec 8, 2025
fb40fe2
build fix
maciejdfinity Dec 8, 2025
75798c6
small refactor
maciejdfinity Dec 8, 2025
eba6fac
typo
maciejdfinity Dec 8, 2025
9b99d3e
check if blocks process in order
maciejdfinity Dec 8, 2025
ed6cbb7
clear rosetta_metadata before recalculating balances
maciejdfinity Dec 8, 2025
843e630
update comment
maciejdfinity Dec 8, 2025
78875e0
only store operation in the blocks table
maciejdfinity Dec 8, 2025
593bd3c
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Dec 9, 2025
3c4bb10
Merge branch 'master' into maciej-rosetta-feecol
maciejdfinity Dec 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions rs/ledger_suite/icrc1/test_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,21 @@ fn operation_strategy<Tokens: TokensType>(
fee,
});

let fee_collector_strategy = (
prop::option::of(principal_strategy()),
prop::option::of(account_strategy()),
)
.prop_map(move |(caller, fee_collector)| Operation::FeeCollector {
fee_collector,
caller,
});

prop_oneof![
mint_strategy,
burn_strategy,
transfer_strategy,
approve_strategy,
fee_collector_strategy,
]
})
}
Expand Down
13 changes: 13 additions & 0 deletions rs/rosetta-api/icrc1/src/common/storage/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,19 @@ pub fn create_tables(connection: &Connection) -> Result<()> {
[],
)?;

// The rosetta_metadata table is meant to store values like the index
// of the highest processed block. This is different than the metadata
// table above which stores the ICRC1 token metadata.
connection.execute(
r#"
CREATE TABLE IF NOT EXISTS rosetta_metadata (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would Rosetta behave if we:

  1. Sync against a ledger with this version of Rosetta that creates a rosetta_metadata table, and then
  2. Downgrade to an older version of Rosetta, but keep the database from this newer version?

As long as there was no 107feecol block in the ledger yet, I could imagine that everything will work? What about if one or more 107feecol blocks were processed? Would the old Rosetta still start up with the new DB? Would it correctly e.g., determine the highest block (probably not if the latest block was a 107feecol block, but maybe otherwise yes?)? Is there any easy way to introduce a breaking database change, so that an old Rosetta version wouldn't start up when using a new DB? We discussed adding (in a separate ticket) a "schema version" entry to the rosetta_metadata table that we could check going forward, but for this upgrade that wouldn't work. Should we be looking at a major version bump when we create a release with the ICRC-107 support?

key TEXT PRIMARY KEY,
value BLOB NOT NULL
);
"#,
[],
)?;

Copy link
Contributor Author

@maciejdfinity maciejdfinity Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the metadata table, updating ckBTC balances take ~4m. This is similar to having an index on operation_type and searching for the fee collector in the blocks table and having a separate table for fee collectors.

create_indexes(connection)
}

Expand Down
16 changes: 9 additions & 7 deletions rs/rosetta-api/icrc1/src/common/storage/storage_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,9 @@ impl StorageClient {

/// Retrieves the highest block index in the account balance table.
/// Returns None if the account balance table is empty.
pub fn get_highest_block_idx_in_account_balance_table(&self) -> Result<Option<u64>> {
pub fn get_highest_processed_block_idx(&self) -> Result<Option<u64>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now returns the highest processed block idx using the rosetta_metadata table, so the method rustdoc should be updated.

let open_connection = self.storage_connection.lock().unwrap();
storage_operations::get_highest_block_idx_in_account_balance_table(&open_connection)
storage_operations::get_highest_processed_block_idx(&open_connection)
}

// Retrieves the account balance at a certain block height
Expand Down Expand Up @@ -405,7 +405,8 @@ mod tests {
use ic_icrc1::blocks::encoded_block_to_generic_block;
use ic_icrc1::blocks::generic_block_to_encoded_block;
use ic_icrc1_test_utils::{
arb_amount, blocks_strategy, metadata_strategy, valid_blockchain_with_gaps_strategy,
arb_amount, blocks_strategy, metadata_strategy, valid_blockchain_strategy,
valid_blockchain_with_gaps_strategy,
};
use ic_icrc1_tokens_u64::U64;
use ic_icrc1_tokens_u256::U256;
Expand All @@ -432,7 +433,7 @@ mod tests {

proptest! {
#[test]
fn test_read_and_write_blocks_u64(blockchain in prop::collection::vec(blocks_strategy::<U64>(arb_amount()),0..5)){
fn test_read_and_write_blocks_u64(blockchain in valid_blockchain_strategy::<U64>(5)){
let storage_client_memory = StorageClient::new_in_memory().unwrap();
let mut rosetta_blocks = vec![];
for (index,block) in blockchain.into_iter().enumerate(){
Expand All @@ -459,7 +460,7 @@ mod tests {
}

#[test]
fn test_read_and_write_blocks_u256(blockchain in prop::collection::vec(blocks_strategy::<U256>(arb_amount()),0..5)){
fn test_read_and_write_blocks_u256(blockchain in valid_blockchain_strategy::<U256>(5)){
let storage_client_memory = StorageClient::new_in_memory().unwrap();
let mut rosetta_blocks = vec![];
for (index,block) in blockchain.into_iter().enumerate(){
Expand Down Expand Up @@ -508,10 +509,11 @@ mod tests {

// Duplicate the last transaction generated
let duplicate_tx_block = RosettaBlock::from_generic_block(last_block.get_generic_block(), last_block.index + 1).unwrap();
let count_before = storage_client_memory.get_transactions_by_hash(duplicate_tx_block.clone().get_transaction_hash()).unwrap().len();
storage_client_memory.store_blocks([duplicate_tx_block.clone()].to_vec()).unwrap();

// The hash of the duplicated transaction should still be the same --> There should be two transactions with the same transaction hash.
assert_eq!(storage_client_memory.get_transactions_by_hash(duplicate_tx_block.clone().get_transaction_hash()).unwrap().len(),2);
// The hash of the duplicated transaction should still be the same --> There should be one more transaction with the same transaction hash.
assert_eq!(storage_client_memory.get_transactions_by_hash(duplicate_tx_block.clone().get_transaction_hash()).unwrap().len(), count_before + 1);
}
}

Expand Down
140 changes: 119 additions & 21 deletions rs/rosetta-api/icrc1/src/common/storage/storage_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ use std::collections::{BTreeMap, HashMap};
use std::str::FromStr;
use tracing::{info, trace};

pub const METADATA_FEE_COL: &str = "fee_collector_107";
pub const METADATA_BLOCK_IDX: &str = "highest_processed_block_index";

/// Gets the current value of a counter from the database.
/// Returns None if the counter doesn't exist.
pub fn get_counter_value(
Expand Down Expand Up @@ -106,6 +109,20 @@ pub fn initialize_counter_if_missing(
Ok(())
}

pub fn get_107_fee_collector_or_legacy(
rosetta_block: &RosettaBlock,
connection: &Connection,
fee_collector_107: Option<Option<Account>>,
) -> anyhow::Result<Option<Account>> {
// First check if we have a 107 fee collector
if let Some(fee_collector_107) = fee_collector_107 {
return Ok(fee_collector_107);
}

// There is no 107 fee collector, check legacy fee collector in the block
get_fee_collector_from_block(rosetta_block, connection)
}

// Helper function to resolve the fee collector account from a block
pub fn get_fee_collector_from_block(
rosetta_block: &RosettaBlock,
Expand Down Expand Up @@ -170,6 +187,27 @@ pub fn get_metadata(connection: &Connection) -> anyhow::Result<Vec<MetadataEntry
Ok(result)
}

pub fn get_rosetta_metadata(connection: &Connection, key: &str) -> anyhow::Result<Option<Vec<u8>>> {
let mut stmt_metadata = connection.prepare_cached(&format!(
"SELECT value FROM rosetta_metadata WHERE key = '{key}'"
))?;
let rows = stmt_metadata.query_map(params![], |row| row.get(0))?;
let mut result = vec![];
for row in rows {
let entry: Vec<u8> = row?;
result.push(entry);
}
if result.len() == 1 {
Ok(Some(result[0].clone()))
} else if result.is_empty() {
// Return None if no metadata entry found
Ok(None)
} else {
// If more than one metadata entry was found return an error
bail!(format!("Multiple metadata entries found for key: {key}"))
}
Comment on lines +200 to +208
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The swap_remove() could be used to remove a clone, but this isn't used that often and there's not that much data to be cloned, so from a performance point of view there's not too much benefit.

Suggested change
if result.len() == 1 {
Ok(Some(result[0].clone()))
} else if result.is_empty() {
// Return None if no metadata entry found
Ok(None)
} else {
// If more than one metadata entry was found return an error
bail!(format!("Multiple metadata entries found for key: {key}"))
}
match result.len() {
0 => Ok(None),
1 => Ok(Some(result.swap_remove(0))),
_ => bail!(format!("Multiple metadata entries found for key: {key}")),
}

}

pub fn update_account_balances(
connection: &mut Connection,
flush_cache_and_shrink_memory: bool,
Expand Down Expand Up @@ -250,7 +288,7 @@ pub fn update_account_balances(

// The next block to be updated is the highest block index in the account balance table + 1 if the table is not empty and 0 otherwise
let next_block_to_be_updated =
get_highest_block_idx_in_account_balance_table(connection)?.map_or(0, |idx| idx + 1);
get_highest_processed_block_idx(connection)?.map_or(0, |idx| idx + 1);
let highest_block_idx =
get_block_with_highest_block_idx(connection)?.map_or(0, |block| block.index);

Expand All @@ -266,9 +304,26 @@ pub fn update_account_balances(
// This also makes the inserting of the account balances batchable and therefore faster
let mut account_balances_cache: HashMap<Account, BTreeMap<u64, Nat>> = HashMap::new();

let mut current_fee_collector_107 = match get_rosetta_metadata(connection, METADATA_FEE_COL)? {
Some(value) => {
let fc: Option<Account> = candid::decode_one(&value)?;
Some(fc)
}
None => None,
};
Comment on lines +307 to +313
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Not sure if this is more readable or not.

Suggested change
let mut current_fee_collector_107 = match get_rosetta_metadata(connection, METADATA_FEE_COL)? {
Some(value) => {
let fc: Option<Account> = candid::decode_one(&value)?;
Some(fc)
}
None => None,
};
let mut current_fee_collector_107 = get_rosetta_metadata(connection, METADATA_FEE_COL)?
.map(|value| candid::decode_one::<Option<Account>>(&value))
.transpose()?;

let collector_before = current_fee_collector_107;

// As long as there are blocks to be fetched, keep on iterating over the blocks in the database with the given BATCH_SIZE interval
while !rosetta_blocks.is_empty() {
let mut last_block_index = batch_start_idx;
for rosetta_block in rosetta_blocks {
if rosetta_block.index < last_block_index {
bail!(format!(
"Processing blocks not in order, previous processed block: {last_block_index}, current block {}",
rosetta_block.index
));
}
last_block_index = rosetta_block.index;
match rosetta_block.get_transaction().operation {
crate::common::storage::types::IcrcOperation::Burn {
from,
Expand All @@ -290,9 +345,11 @@ pub fn update_account_balances(
connection,
&mut account_balances_cache,
)?;
if let Some(collector) =
get_fee_collector_from_block(&rosetta_block, connection)?
{
if let Some(collector) = get_107_fee_collector_or_legacy(
&rosetta_block,
connection,
current_fee_collector_107,
)? {
credit(
collector,
fee,
Expand All @@ -317,9 +374,11 @@ pub fn update_account_balances(
connection,
&mut account_balances_cache,
)?;
if let Some(collector) =
get_fee_collector_from_block(&rosetta_block, connection)?
{
if let Some(collector) = get_107_fee_collector_or_legacy(
&rosetta_block,
connection,
current_fee_collector_107,
)? {
credit(
collector,
fee,
Expand All @@ -342,11 +401,21 @@ pub fn update_account_balances(
.unwrap_or(Nat(BigUint::zero()));
debit(
from,
fee,
fee.clone(),
rosetta_block.index,
connection,
&mut account_balances_cache,
)?;

if let Some(Some(collector)) = current_fee_collector_107 {
credit(
collector,
fee,
rosetta_block.index,
connection,
&mut account_balances_cache,
)?;
}
}
crate::common::storage::types::IcrcOperation::Transfer {
from,
Expand Down Expand Up @@ -378,9 +447,11 @@ pub fn update_account_balances(
&mut account_balances_cache,
)?;

if let Some(collector) =
get_fee_collector_from_block(&rosetta_block, connection)?
{
if let Some(collector) = get_107_fee_collector_or_legacy(
&rosetta_block,
connection,
current_fee_collector_107,
)? {
credit(
collector,
fee,
Expand All @@ -390,6 +461,12 @@ pub fn update_account_balances(
)?;
}
}
crate::common::storage::types::IcrcOperation::FeeCollector {
fee_collector,
caller: _,
} => {
current_fee_collector_107 = Some(fee_collector);
}
}
}

Expand All @@ -407,6 +484,13 @@ pub fn update_account_balances(
})?;
}
}
if collector_before != current_fee_collector_107
&& let Some(collector) = current_fee_collector_107
{
insert_tx.prepare_cached("INSERT INTO rosetta_metadata (key, value) VALUES (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value;")?.execute(params![METADATA_FEE_COL, candid::encode_one(collector)?])?;
}
let last_block_index_bytes = last_block_index.to_le_bytes();
insert_tx.prepare_cached("INSERT INTO rosetta_metadata (key, value) VALUES (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value;")?.execute(params![METADATA_BLOCK_IDX, last_block_index_bytes])?;
insert_tx.commit()?;

if flush_cache_and_shrink_memory {
Expand All @@ -416,7 +500,7 @@ pub fn update_account_balances(
}

// Fetch the next batch of blocks
batch_start_idx = get_highest_block_idx_in_account_balance_table(connection)?
batch_start_idx = get_highest_processed_block_idx(connection)?
.context("No blocks in account balance table after inserting")?
+ 1;
batch_end_idx = batch_start_idx + batch_size;
Expand Down Expand Up @@ -514,6 +598,22 @@ pub fn store_blocks(
fee,
expires_at,
),
crate::common::storage::types::IcrcOperation::FeeCollector {
fee_collector: _,
caller: _,
} => (
"107feecol",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this constant should be defined in the icrc-ledger-types but it's not used there yet. I added a note to DEFI-1901 to create the constant when we implement the 107 schema.

None,
None,
None,
None,
None,
None,
Nat::from(0u64),
None,
None,
None,
),
};

// SQLite doesn't support unsigned 64-bit integers. We need to convert the timestamps to signed
Expand Down Expand Up @@ -666,16 +766,10 @@ pub fn get_blocks_by_transaction_hash(
read_blocks(&mut stmt, params![hash.as_slice().to_vec()])
}

pub fn get_highest_block_idx_in_account_balance_table(
connection: &Connection,
) -> anyhow::Result<Option<u64>> {
match connection
.prepare_cached("SELECT block_idx FROM account_balances WHERE block_idx = (SELECT MAX(block_idx) FROM account_balances)")?
.query_map(params![], |row| row.get(0))?
.next()
{
pub fn get_highest_processed_block_idx(connection: &Connection) -> anyhow::Result<Option<u64>> {
match get_rosetta_metadata(connection, METADATA_BLOCK_IDX)? {
Some(value) => Ok(Some(u64::from_le_bytes(value.as_slice().try_into()?))),
None => Ok(None),
Some(res) => Ok(res?),
}
}

Expand Down Expand Up @@ -865,6 +959,10 @@ pub fn repair_fee_collector_balances(

info!("Starting balance reconciliation...");
connection.execute("DELETE FROM account_balances", params![])?;
connection.execute(
&format!("DELETE FROM rosetta_metadata WHERE key = '{METADATA_BLOCK_IDX}' OR key = '{METADATA_FEE_COL}'"),
params![],
)?;

if block_count > 0 {
info!("Reprocessing all blocks...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ fn create_test_rosetta_block(
effective_fee: None,
fee_collector: None,
fee_collector_block_index: None,
btype: None,
};

RosettaBlock {
Expand Down Expand Up @@ -108,6 +109,7 @@ fn create_test_approve_block(
effective_fee: None,
fee_collector: None,
fee_collector_block_index: None,
btype: None,
};

RosettaBlock {
Expand Down Expand Up @@ -439,6 +441,20 @@ fn test_fee_collector_resolution_and_repair() -> anyhow::Result<()> {
// Manually create broken balances (missing fee collector credits for block 2)
connection.execute("DELETE FROM account_balances", params![])?;

// Insert metadata that needs to be cleared
connection.execute(
"INSERT INTO rosetta_metadata (key, value) VALUES (?1, ?2)",
params![METADATA_BLOCK_IDX, 100_000_000u64.to_le_bytes()],
)?;
let no_fee_col: Option<Account> = None;
connection.execute(
"INSERT INTO rosetta_metadata (key, value) VALUES (?1, ?2)",
params![
METADATA_FEE_COL,
candid::encode_one(no_fee_col).expect("failed to encode fee collector")
],
)?;

// Correct balances for mint and block 1
connection.execute("INSERT INTO account_balances (block_idx, principal, subaccount, amount) VALUES (0, ?1, ?2, '1000000000')",
params![from_account.owner.as_slice(), from_account.effective_subaccount().as_slice()])?;
Expand Down
Loading
Loading