Skip to main content

Module bridge::limiter

use bridge::chain_ids; use bridge::treasury; use iota::address; use iota::bag; use iota::balance; use iota::clock; use iota::coin; use iota::config; use iota::deny_list; use iota::dynamic_field; use iota::dynamic_object_field; use iota::event; use iota::hex; use iota::object; use iota::object_bag; use iota::package; use iota::transfer; use iota::tx_context; use iota::types; use iota::url; use iota::vec_map; use std::address; use std::ascii; use std::bcs; use std::option; use std::string; use std::type_name; use std::u64; use std::vector;

Struct TransferLimiter

public struct TransferLimiter has store

Fields

Struct TransferRecord

public struct TransferRecord has store

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

Struct UpdateRouteLimitEvent

public 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: &bridge::limiter::TransferLimiter, route: &bridge::chain_ids::BridgeRoute): u64

Implementation

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

Function new

public(package) fun new(): bridge::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(package) fun check_and_record_sending_transfer<T>(self: &mut bridge::limiter::TransferLimiter, treasury: &bridge::treasury::BridgeTreasury, clock: &iota::clock::Clock, route: bridge::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(package) fun update_route_limit(self: &mut bridge::limiter::TransferLimiter, route: &bridge::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: &iota::clock::Clock): u64

Implementation

fun current_hour_since_epoch(clock: &Clock): u64 { clock::timestamp_ms(clock) / 3600000 }

Function adjust_transfer_records

fun adjust_transfer_records(self: &mut bridge::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(): iota::vec_map::VecMap<bridge::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 }