Contract 3a6d5abf7e6c9a215e8ec9d452d9dd2d3717edfb2a52c2ed7a1f14af01a067b9

← Back to Index 📥 Download WASM

Meta

cliver 27.0.0#5a7c5fe76530bf4248477ac812fc757146b98cc4
rssdk_spec_shaking 2
rssdkver 26.1.0#175aa41306f383057a8cdfc84b68d931664fc34e
rsver 1.96.0

Instances

  • CBDSJWTW7FU3264HONJW4IZHLTUHNPA3CZW5DDH5P4LVKZNEO7WVABGQ

Interface

Cancel a pending operation. canceller must hold CANCELLER or GUARDIAN.

Takes the operation itself rather than its id, as [Self::execute] does, and rebuilds the id from it. The id is a hash, so a canceller holding only that cannot be told apart from one cancelling the owner's recovery path — and the alternative, a stored per-id marker, buys that distinction with persistent state, a TTL to size, and a cleanup path to get right. Rebuilding is free and makes the caller name what it is killing.

fn cancel(
    env: soroban_sdk::Env,
    canceller: soroban_sdk::Address,
    target: soroban_sdk::Address,
    function: soroban_sdk::Symbol,
    args: soroban_sdk::Vec,
    salt: soroban_sdk::BytesN<32>,
)

Execute a Ready operation, invoking target.function(args) authorized as this contract. executor must authorize the call and hold the EXECUTOR role — the role is enforced on every execution, there is no anonymous path. A deployment that wants permissionless execution grants EXECUTOR to a designated relayer address explicitly. Returns the target's result.

fn execute(
    env: soroban_sdk::Env,
    executor: soroban_sdk::Address,
    target: soroban_sdk::Address,
    function: soroban_sdk::Symbol,
    args: soroban_sdk::Vec,
    salt: soroban_sdk::BytesN<32>,
) -> soroban_sdk::Val

Execute a Ready upgrade(new_wasm_hash) self-operation scheduled under salt. Address and storage are preserved. executor must hold EXECUTOR.

The salt matters here too: rolling back to a wasm hash this contract has run before is a normal recovery step, and a fixed salt made it permanently unschedulable.

fn upgrade(
    env: soroban_sdk::Env,
    executor: soroban_sdk::Address,
    new_wasm_hash: soroban_sdk::BytesN<32>,
    salt: soroban_sdk::BytesN<32>,
)

Whether account holds role.

fn has_role(
    env: soroban_sdk::Env,
    account: soroban_sdk::Address,
    role: soroban_sdk::Symbol,
) -> bool

Schedule an admin (target, function, args) call on the timelock; returns its operation id. The delay is derived from the method's [Tier] via [classify] — the caller does not choose it, so a Sensitive change (upgrade, admin/ownership, live-pool risk) always carries the elevated floor.

proposer must hold the role that owns the derived tier: ADL for apply_adl, FEE_DISTRIBUTOR for set_fee_merkle_root, and PROPOSER for the governance tiers (see [ensure_can_schedule]). The scoped keys reach their own tier and nothing else.

fn schedule(
    env: soroban_sdk::Env,
    proposer: soroban_sdk::Address,
    target: soroban_sdk::Address,
    function: soroban_sdk::Symbol,
    args: soroban_sdk::Vec,
    salt: soroban_sdk::BytesN<32>,
) -> soroban_sdk::BytesN<32>

The ADL-tier delay in ledgers. Zero selects [Self::execute_adl]; a non-zero value selects the ordinary schedule/execute/cancel lifecycle.

fn adl_delay(env: soroban_sdk::Env) -> u32

Execute a Ready grant_role(account, role) self-operation scheduled under salt. executor must hold EXECUTOR, exactly as on [Self::execute_self].

The salt is a parameter rather than a fixed zero because an executed id is Done forever — the timelock never returns one to Unset, and cancel refuses a Done entry — so a hardcoded salt made this entry point single-use per (function, account, role). Granting a role, revoking it, and later restoring it is ordinary operations work, and the third step reverted permanently with an error that read as a timelock bug.

fn grant_role(
    env: soroban_sdk::Env,
    executor: soroban_sdk::Address,
    account: soroban_sdk::Address,
    role: soroban_sdk::Symbol,
    salt: soroban_sdk::BytesN<32>,
)

Immediately execute target.apply_adl(args) through governance.

ADL names a live candidate and defaults to a zero-ledger delay. A zero-delay operational action is not a timelocked governance proposal, so this path deliberately creates no [Operation], salt or pending state: the scoped backend is authorized here and the target call either succeeds in this invocation or rolls back with it.

operator must hold the dedicated ADL role. The function is hard-coded rather than supplied by the caller, so this capability reaches only apply_adl. A non-zero ADL delay disables this gateway; delayed ADL operations use the ordinary schedule/execute/cancel lifecycle.

fn execute_adl(
    env: soroban_sdk::Env,
    operator: soroban_sdk::Address,
    target: soroban_sdk::Address,
    args: soroban_sdk::Vec,
) -> soroban_sdk::Val

Execute a Ready revoke_role(account, role) self-operation scheduled under salt. executor must hold EXECUTOR. See [Self::grant_role] for why the salt is a parameter.

fn revoke_role(
    env: soroban_sdk::Env,
    executor: soroban_sdk::Address,
    account: soroban_sdk::Address,
    role: soroban_sdk::Symbol,
    salt: soroban_sdk::BytesN<32>,
)

Execute a Ready Sensitive operation whose target is this governance contract. Self-calls cannot use [Self::execute] because Soroban rejects governance -> governance re-entry, so this path consumes the operation with OZ's set_execute_operation and dispatches the allowlisted action internally: upgrade, grant_role, revoke_role, or one of the five per-tier set_*_delay retunes. Each retune takes one u32 of ledgers, bounded by [MAX_DELAY_LEDGERS] as at construction, and is the only way to move that delay after deploy — the values live in storage, so a later upgrade alone cannot reach them.

Like [Self::execute], executor must authorize the call and hold the EXECUTOR role: scheduling and the Sensitive delay decide what can run and when, the executor decides the moment it lands, and a guardian can cancel while it is pending.

fn execute_self(
    env: soroban_sdk::Env,
    executor: soroban_sdk::Address,
    function: soroban_sdk::Symbol,
    args: soroban_sdk::Vec,
    salt: soroban_sdk::BytesN<32>,
)

Initialize governance: admin becomes owner + access-control admin and holds all seven operational roles. One delay per [Tier], all in ledgers, each independent of the others and each retunable afterwards by its own self-operation. DEFAULT_SENSITIVE_DELAY_LEDGERS, [DEFAULT_ADL_DELAY_LEDGERS] and [DEFAULT_FEE_DISTRIBUTION_DELAY_LEDGERS] are the recommended mainnet values; a dev/testnet deploy passes 0 for all five, which is the only way to turn the timelock off — no tier's value silently zeroes another.

Handing ADL / FEE_DISTRIBUTOR to the off-chain services is a grant_role self-operation, after which the admin should drop its own copies.

OZ's own min_delay is pinned to 0 and left there. It exists to stop a caller passing schedule_operation a delay under the configured floor, and here no caller ever passes one: [Self::schedule] is the only path in and derives the delay from the method's tier. Leaving the library floor armed would only mean it silently overrode four of our five tier

fn __constructor(
    env: soroban_sdk::Env,
    admin: soroban_sdk::Address,
    deployer_delay: u32,
    standard_delay: u32,
    sensitive_delay: u32,
    adl_delay: u32,
    fee_distribution_delay: u32,
)

Immediately invoke a creation-only admin call — create_pool, initialize_pool, approve_pool, configure — authorized as this contract, bypassing the timelock. deployer must hold DEPLOYER, and the method must classify as [Tier::Deployer] (else NotADeployerOperation). These methods reject an already-configured target; LockYield also tombstones every revoke_pool, so approve_pool cannot resurrect it. Restoring that pool requires the separate, Sensitive reapprove_pool operation. Thus this fast path can bring up a new pool but can never retune or restore a live one — that (and registering a margin asset) stays on the Sensitive setters, reachable only through schedule + the delay. Returns the target's result.

fn deployer_call(
    env: soroban_sdk::Env,
    deployer: soroban_sdk::Address,
    target: soroban_sdk::Address,
    function: soroban_sdk::Symbol,
    args: soroban_sdk::Vec,
) -> soroban_sdk::Val

The Deployer tier's delay, in ledgers — for a creation-only op that is scheduled rather than driven through the immediate [Self::deployer_call].

fn deployer_delay(env: soroban_sdk::Env) -> u32

Emergency brake: immediately (bypassing the timelock) pause target by invoking target.set_paused(true) authorized as this contract. guardian must hold GUARDIAN. The target must be a contract whose admin is this governance and that exposes set_paused(bool).

Pause only. paused == false is rejected (GuardianCanOnlyPause); unpausing is a Tier::Standard set_paused(false) through schedule. The brake is deliberately one-directional: halting during an incident cannot wait for a delay, but restarting is a judgement call that should be visible as a pending operation and cancellable by anyone holding CANCELLER or GUARDIAN. Forwarding the flag let a single compromised guardian key clear a pause the instant it was set — reopening the exploit window with nothing scheduled to cancel, while honest governance had to wait out the full delay to re-pause.

The paused argument survives the restriction rather than being dropped so the call shape (and every runbook and script built on it) stays put,

fn guardian_pause(
    env: soroban_sdk::Env,
    guardian: soroban_sdk::Address,
    target: soroban_sdk::Address,
    paused: bool,
)

The [Tier] a method schedules at — lets ops tell ahead of time whether a call is an instant deployer_call, a min-delay schedule, or a Sensitive-floor schedule.

fn operation_tier(env: soroban_sdk::Env, function: soroban_sdk::Symbol) -> Tier

The Standard tier's delay, in ledgers.

fn standard_delay(env: soroban_sdk::Env) -> u32

The elevated delay a Sensitive-tier op schedules at (ledgers), set at construction.

fn sensitive_delay(env: soroban_sdk::Env) -> u32

The delay a fee-distribution-tier op (set_fee_merkle_root) schedules at (ledgers).

fn fee_distribution_delay(env: soroban_sdk::Env) -> u32

Schedule a grant or revoke of role on account that no canceller or guardian can veto. Owner-gated; returns the operation id. Execute it with execute_self once the Sensitive delay has run, passing the same arguments and this path's reserved salt (readable from the emitted operation, or rebuilt as 32 0xFF bytes).

The recovery lever, and the only one: role administration is otherwise a Sensitive self-operation, and cancel accepts any pending operation from any canceller or guardian — so a compromised holder vetoes its own removal indefinitely, and a compromised guardian keeps the engine paused on top of that. Forbidding cancellation of every revoke_role would swap that deadlock for a worse one: a compromised proposer could then strip every other holder unopposed, and since granting a role is itself a proposer-scheduled operation, nothing could put them back.

Timelocked like any other Sensitive change — the owner gets notice, not immediacy — and scoped to role changes alone.

salt is the o

fn owner_schedule_role_change(
    env: soroban_sdk::Env,
    account: soroban_sdk::Address,
    role: soroban_sdk::Symbol,
    grant: bool,
    salt: soroban_sdk::BytesN<32>,
) -> soroban_sdk::BytesN<32>

Imports

WebAssembly Text (WAT) ▶