Contract 6cb56fcab1fb601d23af110e18d395a0f19070d99bae679f07b5487932e40405

← Back to Index 📥 Download WASM

Meta

rssdkver 27.0.4#3715435b643496244c9c22f0e7443b9c1f953ddb
rsver 1.97.1

Instances

  • CC2RZSK6LBVQMV47OKNLXDEIN3RNJEDJCR26YCKXQQ5ORBTJONESONIN
  • CC32BXNKSEXYXOSGMEVAQS7UWJIB4CBKVLW7R4I6BXRLMFER3XRYOPF4

Interface

fn name(env: soroban_sdk::Env) -> soroban_sdk::String

Pull the restricted asset in, mint wrapped 1:1. The from is gated: a non-allowlisted address cannot wrap because that would be a way to acquire the wrapped token.

fn wrap(env: soroban_sdk::Env, from: soroban_sdk::Address, amount: i128)

Admin adds an address to the transfer allowlist. The counterpart is disallow, which REMOVES the entry rather than writing false (presence is the semantic, matching the unfreeze pattern). disallow is REFUSED against any address the operator has marked with add_venue (panics with #16 VenueProtected); the venue guard is mandatory because removing the Blend pool or the facade from the allowlist strands every supplier at once. The new entry is bumped to the network maximum TTL because admission is permanent-by-design (a quiet-but-readmitted member must never see their entry archive) and re-allow after disallow re-applies the same bump.

fn allow(env: soroban_sdk::Env, id: soroban_sdk::Address)

Admin freezes an address. The from-side gate in transfer then refuses that address with #8. wrap and unwrap are NOT affected - a frozen member can still be paid out by a venue (Blend payout is transfer(pool -> member) where from is the pool, not the member) and can still redeem their own wrapped balance. Admins can be frozen; the admin multisig itself is the only address that can write the entry. Bumps the entry to the network maximum TTL because an archived Frozen entry fails at footprint load, which fails CLOSED.

Venue guard: a venue address (one the operator has marked with add_venue) is REFUSED with #9 VenueNotFreezable. Freezing the venue sender would strand every supplied balance until unfrozen, because transfer applies require_not_frozen to any from - and Blend's payout is transfer(pool -> member) where the POOL is the sender. The guard closes that. There is no remove_venue so the marker is monotone-additive; an address that was a venue stays one, and freeze continues to

fn freeze(env: soroban_sdk::Env, id: soroban_sdk::Address)
fn symbol(env: soroban_sdk::Env) -> soroban_sdk::String

Burn wrapped, return the restricted asset. Deliberately has NO eligibility check on from: the underlying asset's authorization flags refuse the payout if the holder is no longer eligible (SAC error #11). Adding a check here would be redundant and risks diverging from the asset's own view. Exit is structurally unpausable - see the file-level comment.

fn unwrap(env: soroban_sdk::Env, from: soroban_sdk::Address, amount: i128)
fn balance(env: soroban_sdk::Env, id: soroban_sdk::Address) -> i128
fn decimals(env: soroban_sdk::Env) -> u32

Admin removes an address from the transfer allowlist. The counterpart of allow. REMOVES the storage entry rather than writing false - mirrors unfreeze exactly so the invariant is symmetric with the age-out failure mode: a missing Allowed entry fails the next transaction at footprint load (which fails CLOSED for a withdrawal) rather than silently admitting. The transfer gate (require_allowed) uses has() and reads unwrap_or(false); after disallow the gate panics with #1 NotAllowed for any wrap or inbound transfer into the address - the same code as a never-admitted address, so existing telemetry and indexer rules for #1 continue to apply.

Venue guard: a venue address (one the operator has marked with add_venue) is REFUSED with #16 VenueProtected. Removing the Blend pool or the facade from the allowlist would strand every supplier at once: a supply transfers wrapped tokens TO the pool, so the pool must remain receivable. The guard is the structural mirror of the freeze guard (which uses `#

fn disallow(env: soroban_sdk::Env, id: soroban_sdk::Address)
fn is_venue(env: soroban_sdk::Env, id: soroban_sdk::Address) -> bool

Gate on the RECIPIENT (to). This is pool/src/pool/submit.rs:243 (transfer(pool, to, amount)) plus the pool-initiated credit paths; an ineligible to is refused with #1. Gate is a pure local storage read - see the file-level comment.

Sender-side freeze: a frozen from is refused with #8. The check is on from ONLY - deliberately NOT on to (a frozen address can still RECEIVE wrapped tokens, which is what makes a venue payout into a frozen member succeed). See the file-level comment for why this is the only place the check lives and why that placement is the only acceptable one.

fn transfer(
    env: soroban_sdk::Env,
    from: soroban_sdk::Address,
    to: soroban_sdk::Address,
    amount: i128,
)

Admin reverses a freeze. REMOVES the storage entry rather than writing false. Only frozen addresses then hold a Frozen entry; an archived entry fails at footprint load, which fails CLOSED (transfer blocked). The remove-on-unfreeze pattern keeps the invariant symmetric with the age-out failure mode - missing = not frozen, present = frozen. See the file-level comment for why this gate and the allowlist stay separate: unfreeze reverses a SENDER-side restriction and allow reverses a RECIPIENT-side one, so neither substitutes for the other.

fn unfreeze(env: soroban_sdk::Env, id: soroban_sdk::Address)

Admin marks an address as a venue AND allowlists it in a single call. ADD-ONLY - there is deliberately NO remove_venue function: if venue status could be removed, an admin could strip the marker from a venue and then call freeze on it, which would defeat the freeze guard and re-open the strand-every-supplier failure mode this contract is built to refuse. The marker is monotone-additive and, since disallow exists, it is now the ONLY monotone record here: the allowlist is revocable for ordinary admittees, while this marker must stay one-way because both guards it backs (#9 on freeze, #16 on disallow) are one-way trips.

Marking and allowlisting happen together because a venue that is not on the allowlist cannot receive wrapped tokens at all (transfer rejects with #1), so splitting them across two calls would leave a window in which the two records disagree. Both entries are bumped to the network maximum TTL for the same reason allow is: a quiet venue that ages out silently would strand its

fn add_venue(env: soroban_sdk::Env, id: soroban_sdk::Address)
fn is_frozen(env: soroban_sdk::Env, id: soroban_sdk::Address) -> bool
fn is_allowed(env: soroban_sdk::Env, id: soroban_sdk::Address) -> bool

The ONLY pause in this contract. Gates the ENTRY side (wrap) only. There is deliberately no exit pause - see the file-level comment for why a halt on unwrap would be a trap power. If redemption needs to be halted the issuer does it at the asset layer (SAC set_authorized).

fn set_paused(env: soroban_sdk::Env, paused: bool)

Constructor. Runs ATOMICALLY as part of contract creation, which is the only window in which admin / underlying / name / symbol can be set. There is no separate init step and so no init front-run window for an attacker to claim admin: the only way admin is set is the deploy transaction itself. Requires --admin <A> --underlying <U> --name <N> --symbol <S> at deploy time.

fn __constructor(
    env: soroban_sdk::Env,
    admin: soroban_sdk::Address,
    underlying: soroban_sdk::Address,
    name: soroban_sdk::String,
    symbol: soroban_sdk::String,
)

Like unwrap, but with a caller-chosen upper bound on amount that the contract binds the auth to. Sibling of unwrap; the existing unwrap is unchanged.

WHY THIS EXISTS. The unwrap auth is require_auth(), which infers the signed args from the invocation. For a facade that computes amount from live pool state AFTER calling the pool (e.g. an overpaid Repay where the residue is the pool refund), the actual amount argument can drift between simulation and inclusion (~17 stroops/second of accruing interest on this debt) and the signed auth no longer matches the invocation

  • the transaction fails with Error(auth, invalid_action) on the unwrap sub-invocation. require_auth_for_args lets the CONTRACT decide which values belong to the auth, so we authorise over cap only (a stable value the facade knows from the member's own request vector before any sub-invocation) and allow amount to drift without invalidating the tree.

SAFETY. Cap-only authorisation is safe here precisely because proceeds go

fn unwrap_capped(
    env: soroban_sdk::Env,
    from: soroban_sdk::Address,
    amount: i128,
    cap: i128,
)

Imports

WebAssembly Text (WAT) ▶