Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 28 additions & 2 deletions omni-utils-derive/src/trusted_relayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ fn gen_bypass_is_trusted(bypass_roles: &[Expr]) -> TokenStream2 {
return true;
}

::omni_utils::trusted_relayer::tr_relayers_map()
::omni_utils::trusted_relayer::tr_load_relayers()
.get(account_id)
.is_some_and(|state| {
::near_sdk::env::block_timestamp() >= state.activate_at.0
Expand Down Expand Up @@ -263,6 +263,32 @@ fn gen_public_methods(
) -> ::omni_utils::trusted_relayer::RelayerConfig {
<Self as ::omni_utils::trusted_relayer::TrustedRelayer>::_tr_get_config(self)
}

#[must_use]
pub fn get_active_relayers(
&self,
from_index: Option<u32>,
limit: Option<u32>,
) -> Vec<(::near_sdk::AccountId, ::omni_utils::trusted_relayer::RelayerState)> {
<Self as ::omni_utils::trusted_relayer::TrustedRelayer>::_tr_get_active_relayers(
self,
from_index,
limit,
)
}

#[must_use]
pub fn get_pending_relayers(
&self,
from_index: Option<u32>,
limit: Option<u32>,
) -> Vec<(::near_sdk::AccountId, ::omni_utils::trusted_relayer::RelayerState)> {
<Self as ::omni_utils::trusted_relayer::TrustedRelayer>::_tr_get_pending_relayers(
self,
from_index,
limit,
)
}
}
}
}
Expand Down Expand Up @@ -319,7 +345,7 @@ fn gen_method_bypass_guard(bypass_roles: &[Expr]) -> syn::Stmt {
__caller.clone(),
) {
::near_sdk::require!(
::omni_utils::trusted_relayer::tr_relayers_map()
::omni_utils::trusted_relayer::tr_load_relayers()
.get(&__caller)
.is_some_and(|state| {
::near_sdk::env::block_timestamp() >= state.activate_at.0
Expand Down
105 changes: 96 additions & 9 deletions omni-utils/src/trusted_relayer.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use core::ops::{Deref, DerefMut};

use near_sdk::borsh::{self, BorshDeserialize};
use near_sdk::json_types::{U64, U128};
use near_sdk::serde_json::json;
use near_sdk::store::LookupMap;
use near_sdk::store::IterableMap;
use near_sdk::{AccountId, NearToken, Promise, env, near, require};

const TR_CONFIG_KEY: &[u8] = b"__tr_config";
const TR_RELAYERS_PREFIX: &[u8] = b"__tr_relayers";
const TR_RELAYERS_META_KEY: &[u8] = b"__tr_relayers_meta";
const TR_DEFAULT_PAGE_LIMIT: u32 = 100;

#[derive(Debug, Clone)]
#[near(serializers = [json, borsh])]
Expand Down Expand Up @@ -71,8 +75,59 @@ pub fn tr_save_config(config: &RelayerConfig) {
);
}

pub fn tr_relayers_map() -> LookupMap<AccountId, RelayerState> {
LookupMap::new(TR_RELAYERS_PREFIX)
/// Wrapper around `IterableMap` that automatically persists iteration metadata
/// (the internal `len` field) on drop. Entry data is flushed by the inner
/// `IterableMap`'s own `Drop` impl; this wrapper handles the metadata that
/// `IterableMap` cannot persist on its own in a detached (non-struct-field) pattern.
///
/// Metadata is only written when the map has been mutated (via `DerefMut`),
/// so read-only usage in view calls does not trigger `storage_write`.
pub struct RelayerMap {
inner: IterableMap<AccountId, RelayerState>,
modified: bool,
}

impl Deref for RelayerMap {
type Target = IterableMap<AccountId, RelayerState>;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl DerefMut for RelayerMap {
fn deref_mut(&mut self) -> &mut Self::Target {
self.modified = true;
&mut self.inner
}
}

impl Drop for RelayerMap {
fn drop(&mut self) {
if self.modified {
env::storage_write(
TR_RELAYERS_META_KEY,
&borsh::to_vec(&self.inner).unwrap_or_else(|_| {
env::panic_str("Failed to serialize relayers map metadata")
}),
);
}
}
}

/// Load the relayers map, restoring iteration metadata from storage.
/// On first use (no metadata yet), creates a fresh empty map.
/// Metadata is persisted automatically when the returned `RelayerMap` is dropped.
pub fn tr_load_relayers() -> RelayerMap {
let inner = match env::storage_read(TR_RELAYERS_META_KEY) {
Some(bytes) => BorshDeserialize::try_from_slice(&bytes)
.unwrap_or_else(|_| env::panic_str("Failed to deserialize relayers map metadata")),
None => IterableMap::new(TR_RELAYERS_PREFIX),
};
RelayerMap {
inner,
modified: false,
}
}

/// Trusted relayer staking support for NEAR contracts.
Expand All @@ -87,14 +142,14 @@ pub trait TrustedRelayer {
return true;
}

tr_relayers_map()
tr_load_relayers()
.get(account_id)
.is_some_and(|state| env::block_timestamp() >= state.activate_at.0)
}

fn _tr_apply(&mut self) {
let account_id = env::predecessor_account_id();
let mut relayers = tr_relayers_map();
let mut relayers = tr_load_relayers();

require!(
relayers.get(&account_id).is_none(),
Expand Down Expand Up @@ -137,7 +192,7 @@ pub trait TrustedRelayer {

fn _tr_resign(&mut self) -> Promise {
let account_id = env::predecessor_account_id();
let mut relayers = tr_relayers_map();
let mut relayers = tr_load_relayers();

let state = relayers
.remove(&account_id)
Expand All @@ -158,7 +213,7 @@ pub trait TrustedRelayer {
}

fn _tr_reject(&mut self, account_id: AccountId) -> Promise {
let mut relayers = tr_relayers_map();
let mut relayers = tr_load_relayers();

let state = relayers
.remove(&account_id)
Expand All @@ -181,14 +236,14 @@ pub trait TrustedRelayer {
}

fn _tr_get_application(&self, account_id: &AccountId) -> Option<RelayerState> {
tr_relayers_map()
tr_load_relayers()
.get(account_id)
.filter(|state| env::block_timestamp() < state.activate_at.0)
.cloned()
}

fn _tr_get_stake(&self, account_id: &AccountId) -> Option<U128> {
tr_relayers_map()
tr_load_relayers()
.get(account_id)
.filter(|state| env::block_timestamp() >= state.activate_at.0)
.map(|state| U128(state.stake.as_yoctonear()))
Expand All @@ -197,4 +252,36 @@ pub trait TrustedRelayer {
fn _tr_get_config(&self) -> RelayerConfig {
tr_load_config()
}

fn _tr_get_active_relayers(
&self,
from_index: Option<u32>,
limit: Option<u32>,
) -> Vec<(AccountId, RelayerState)> {
let relayers = tr_load_relayers();
let now = env::block_timestamp();
relayers
.iter()
.filter(|(_, state)| now >= state.activate_at.0)
.skip(from_index.unwrap_or(0) as usize)
.take(limit.unwrap_or(TR_DEFAULT_PAGE_LIMIT) as usize)
.map(|(id, state)| (id.clone(), state.clone()))
.collect()
}

fn _tr_get_pending_relayers(
&self,
from_index: Option<u32>,
limit: Option<u32>,
) -> Vec<(AccountId, RelayerState)> {
let relayers = tr_load_relayers();
let now = env::block_timestamp();
relayers
.iter()
.filter(|(_, state)| now < state.activate_at.0)
.skip(from_index.unwrap_or(0) as usize)
.take(limit.unwrap_or(TR_DEFAULT_PAGE_LIMIT) as usize)
.map(|(id, state)| (id.clone(), state.clone()))
.collect()
}
}
Loading
Loading