Skip to main content

Module 0xb::limiter

use 0x1::option; use 0x1::vector; use 0x2::clock; use 0x2::event; use 0x2::vec_map; use 0xb::chain_ids; use 0xb::treasury;

Struct TransferLimiter

struct TransferLimiter has store

Fields

Struct TransferRecord

struct TransferRecord has store

Fields
hour_head: u64
hour_tail: u64
per_hour_amounts: vector<u64>
total_amount: u64

Struct UpdateRouteLimitEvent

struct UpdateRouteLimitEvent has copy, drop

Fields
sending_chain: u8
receiving_chain: u8
new_limit: u64

Constants

const ELimitNotFoundForRoute: u64 = 0;

const MAX_TRANSFER_LIMIT: u64 = 18446744073709551615;

const USD_VALUE_MULTIPLIER: u64 = 100000000;

Function get_route_limit

public fun get_route_limit(self: &limiter::TransferLimiter, route: &chain_ids::BridgeRoute): u64

Implementation

public fun get_route_limit(self: &TransferLimiter, route: &BridgeRoute): u64 { self.transfer_limits[route] }

Function new

public(friend) fun new(): limiter::TransferLimiter

Implementation

public(package) fun new(): TransferLimiter { // hardcoded limit for bridge genesis TransferLimiter { transfer_limits: initial_transfer_limits(), transfer_records: vec_map::empty() } }

Function check_and_record_sending_transfer

public(friend) fun check_and_record_sending_transfer<T>(self: &mut limiter::TransferLimiter, treasury: &treasury::BridgeTreasury, clock: &clock::Clock, route: chain_ids::BridgeRoute, amount: u64): bool

Implementation

public(package) fun check_and_record_sending_transfer<T>( self: &mut TransferLimiter, treasury: &BridgeTreasury, clock: &Clock, route: BridgeRoute, amount: u64 ): bool { // Create record for route if not exists if (!self.transfer_records.contains(&route)) { self.transfer_records.insert(route, TransferRecord { hour_head: 0, hour_tail: 0, per_hour_amounts: vector[], total_amount: 0 }) }; let record = self.transfer_records.get_mut(&route); let current_hour_since_epoch = current_hour_since_epoch(clock);

record.adjust_transfer_records(current_hour_since_epoch);

// Get limit for the route let route_limit = self.transfer_limits.try_get(&route); assert!(route_limit.is_some(), ELimitNotFoundForRoute); let route_limit = route_limit.destroy_some(); let route_limit_adjusted = (route_limit as u128) * (treasury.decimal_multiplier<T>() as u128);

// Compute notional amount // Upcast to u128 to prevent overflow, to not miss out on small amounts. let value = (treasury.notional_value<T>() as u128); let notional_amount_with_token_multiplier = value * (amount as u128);

// Check if transfer amount exceed limit // Upscale them to the token's decimal. if ((record.total_amount as u128)

  • (treasury.decimal_multiplier<T>() as u128)
  • notional_amount_with_token_multiplier > route_limit_adjusted ) { return false };

// Now scale down to notional value let notional_amount = notional_amount_with_token_multiplier / (treasury.decimal_multiplier<T>() as u128); // Should be safe to downcast to u64 after dividing by the decimals let notional_amount = (notional_amount as u64);

// Record transfer value let new_amount = record.per_hour_amounts.pop_back() + notional_amount; record.per_hour_amounts.push_back(new_amount); record.total_amount = record.total_amount + notional_amount; true }

Function update_route_limit

public(friend) fun update_route_limit(self: &mut limiter::TransferLimiter, route: &chain_ids::BridgeRoute, new_usd_limit: u64)

Implementation

public(package) fun update_route_limit( self: &mut TransferLimiter, route: &BridgeRoute, new_usd_limit: u64 ) { let receiving_chain = *route.destination();

if (!self.transfer_limits.contains(route)) { self.transfer_limits.insert(*route, new_usd_limit); } else { *&mut self.transfer_limits[route] = new_usd_limit; };

emit(UpdateRouteLimitEvent { sending_chain: *route.source(), receiving_chain, new_limit: new_usd_limit, }) }

Function current_hour_since_epoch

fun current_hour_since_epoch(clock: &clock::Clock): u64

Implementation

Function adjust_transfer_records

fun adjust_transfer_records(self: &mut limiter::TransferRecord, current_hour_since_epoch: u64)

Implementation

fun adjust_transfer_records(self: &mut TransferRecord, current_hour_since_epoch: u64) { if (self.hour_head == current_hour_since_epoch) { return // nothing to backfill };

let target_tail = current_hour_since_epoch - 23;

// If hour_head is even older than 24 hours ago, it means all items in // per_hour_amounts are to be evicted. if (self.hour_head < target_tail) { self.per_hour_amounts = vector[]; self.total_amount = 0; self.hour_tail = target_tail; self.hour_head = target_tail; // Don't forget to insert this hour's record self.per_hour_amounts.push_back(0); } else { // self.hour_head is within 24 hour range. // some items in per_hour_amounts are still valid, we remove stale hours. while (self.hour_tail < target_tail) { self.total_amount = self.total_amount - self.per_hour_amounts.remove(0); self.hour_tail = self.hour_tail + 1; } };

// Backfill from hour_head to current hour while (self.hour_head < current_hour_since_epoch) { self.per_hour_amounts.push_back(0); self.hour_head = self.hour_head + 1; } }

Function initial_transfer_limits

fun initial_transfer_limits(): vec_map::VecMap<chain_ids::BridgeRoute, u64>

Implementation

fun initial_transfer_limits(): VecMap<BridgeRoute, u64> { let mut transfer_limits = vec_map::empty(); // 5M limit on Iota -> Ethereum mainnet transfer_limits.insert( chain_ids::get_route(chain_ids::eth_mainnet(), chain_ids::iota_mainnet()), 5_000_000 * USD_VALUE_MULTIPLIER );

// MAX limit for testnet and devnet transfer_limits.insert( chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_testnet()), MAX_TRANSFER_LIMIT );

transfer_limits.insert( chain_ids::get_route(chain_ids::eth_sepolia(), chain_ids::iota_custom()), MAX_TRANSFER_LIMIT );

transfer_limits.insert( chain_ids::get_route(chain_ids::eth_custom(), chain_ids::iota_testnet()), MAX_TRANSFER_LIMIT );

transfer_limits.insert( chain_ids::get_route(chain_ids::eth_custom(), chain_ids::iota_custom()), MAX_TRANSFER_LIMIT );

transfer_limits }