Draw: send NGNC from vault to hot wallet. ReytsCAD settlement to partner is handled off-chain by the API using the same ref_hash for linkage. OPS only.
fn draw(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
deposit_asset_sac: soroban_sdk::Address,
payment_asset_sac: soroban_sdk::Address,
deposit_amount: i128,
payment_amount: i128,
hot_wallet: soroban_sdk::Address,
ref_hash: soroban_sdk::BytesN<32>,
)
Block deposits, draws, normal withdrawals โ admin only. Emergency withdraw still works.
fn pause(env: soroban_sdk::Env)
Returns (paused, admin, ops, max_draw_per_tx, throttled_max_draw).
fn status(
env: soroban_sdk::Env,
) -> (bool, soroban_sdk::Address, soroban_sdk::Address, i128, i128)
Rotate ops address โ admin + new_ops must co-sign.
fn set_ops(env: soroban_sdk::Env, new_ops: soroban_sdk::Address)
Resume operations โ admin only.
fn unpause(env: soroban_sdk::Env)
Normal withdrawal to partner's allowlisted wallet โ OPS only.
fn withdraw(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset_sac: soroban_sdk::Address,
amount: i128,
destination: soroban_sdk::Address,
)
Rotate admin address โ both admin and new_admin must co-sign.
fn set_admin(env: soroban_sdk::Env, new_admin: soroban_sdk::Address)
Check if address is an allowed partner.
fn is_partner(env: soroban_sdk::Env, partner: soroban_sdk::Address) -> bool
Add a KYC'd partner with an initial deposit cap for a specific asset (M-02). Caps for additional assets are added via set_deposit_cap. Admin only.
fn add_partner(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset: soroban_sdk::Address,
deposit_cap: i128,
)
Set exchange rate for the (deposit_asset, payment_asset) pair โ OPS only.
Rate is stored as (deposit-currency-per-payment-currency) ร 10^7.
For the canonical NGNCโCAD draw:
new_rate = 1100 ร 10^7 = 11_000_000_000new_rate = 13_200_000_000During draw, the I-07 check enforces:
expected_payment_stroops = deposit_amount_stroops ร 10^7 / rate
Worked example: 1,000,000 NGNC (= 10^13 stroops) at rate 11_000_000_000:
10^13 ร 10^7 / 11ร10^9 = 10^11 / 11 โ 9_090_909_091 CAD stroops โ 909.09 CAD
Auto-calculates drift band. >=15% single-step OR >=1500 bps cumulative
drift within the UTC-day window โ pauses draws (RateBandPaused);
5-14% โ throttles to throttled_max_draw per tx (RateBandThrottled).
fn update_rate(
env: soroban_sdk::Env,
deposit_asset: soroban_sdk::Address,
payment_asset: soroban_sdk::Address,
new_rate: i128,
)
Get current rate for an asset pair.
fn current_rate(
env: soroban_sdk::Env,
deposit_asset: soroban_sdk::Address,
payment_asset: soroban_sdk::Address,
) -> (i128, u64)
Set/rotate the governance contract draw() settles RCAD through. Admin-only. Mainnet hardening: timelock this (deferred; proposal ยง3.6).
fn set_gov_addr(env: soroban_sdk::Env, gov: soroban_sdk::Address)
Update normal max draw per tx โ admin only.
fn set_max_draw(env: soroban_sdk::Env, new_max: i128)
I-01: deployment-time constructor. Runs atomically with contract creation, closing the deploy-init race window that initialize() had. Sets roles, draw limits, paused=false.
fn __constructor(
env: soroban_sdk::Env,
admin: soroban_sdk::Address,
ops: soroban_sdk::Address,
max_draw_per_tx: i128,
throttled_max_draw: i128,
)
Check if address is an allowed hot wallet.
fn is_hot_wallet(env: soroban_sdk::Env, addr: soroban_sdk::Address) -> bool
Add to draw source/destination allowlist โ admin only.
fn add_hot_wallet(env: soroban_sdk::Env, addr: soroban_sdk::Address)
Cancel a pending upgrade โ admin only.
fn cancel_upgrade(env: soroban_sdk::Env)
Remove partner from allowlist โ admin only. Positions preserved for emergency withdraw.
fn remove_partner(env: soroban_sdk::Env, partner: soroban_sdk::Address)
Execute pending WASM upgrade after 24h โ admin only.
fn execute_upgrade(env: soroban_sdk::Env)
Propose a WASM upgrade โ admin only. Starts 24h timer.
fn propose_upgrade(env: soroban_sdk::Env, new_wasm_hash: soroban_sdk::BytesN<32>)
Set or update partner's deposit cap for a specific asset (M-02). Caps are enforced per (partner, asset). Admin only.
fn set_deposit_cap(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset: soroban_sdk::Address,
new_cap: i128,
)
Companion to is_rate_band_overridden: returns the current cumulative drift counter (in basis points) for the asset pair. Useful for monitoring how close the system is to the throttle (>=500 bps) or pause (>=1500 bps) thresholds, and for understanding whether an override is about to auto-clear.
fn cumulative_drift(
env: soroban_sdk::Env,
deposit_asset: soroban_sdk::Address,
payment_asset: soroban_sdk::Address,
) -> i128
Get partner position for a specific asset.
fn partner_position(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset_sac: soroban_sdk::Address,
) -> i128
Credit partner position after detecting incoming payment โ OPS only. Checks cap + velocity + replay.
fn register_deposit(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset_sac: soroban_sdk::Address,
amount: i128,
tx_ref: soroban_sdk::BytesN<32>,
)
Check if wallet is allowlisted for partner withdrawals.
fn is_partner_wallet(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
wallet: soroban_sdk::Address,
) -> bool
Remove from allowlist โ admin only.
fn remove_hot_wallet(env: soroban_sdk::Env, addr: soroban_sdk::Address)
Add a withdrawal destination for partner โ admin only.
fn add_partner_wallet(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
wallet: soroban_sdk::Address,
)
Emergency withdrawal โ partner auth (not OPS). Full balance for given asset. Works while paused and after removal.
fn emergency_withdraw(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset_sac: soroban_sdk::Address,
destination: soroban_sdk::Address,
)
Acknowledge >=15% rate move (single-step OR cumulative across multiple sub-15% updates per M-01), re-enable draws โ admin only.
fn admin_override_rate(
env: soroban_sdk::Env,
deposit_asset: soroban_sdk::Address,
payment_asset: soroban_sdk::Address,
)
Get partner's deposit cap for a specific asset (M-02 per-asset cap).
fn partner_deposit_cap(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
asset: soroban_sdk::Address,
) -> i128
Remove a withdrawal destination for partner โ admin only.
fn remove_partner_wallet(
env: soroban_sdk::Env,
partner: soroban_sdk::Address,
wallet: soroban_sdk::Address,
)
Update throttled max draw (5-15% band) โ admin only.
fn set_throttled_max_draw(env: soroban_sdk::Env, new_max: i128)
Task #79 โ Certora Monitoring #4: sticky RateBandOverride getter.
Returns true if the rate-band-pause-override flag is currently SET for
this asset pair (admin_override_rate has been called and cumulative
drift has not yet returned below threshold to auto-clear it). When
true, the >=15% single-step / >=1500 bps cumulative drift pause is
being bypassed โ draws proceed with only the normal max_draw_per_tx
cap, not the throttled cap.
Off-chain monitoring should read this and emit Slack alerts whenever the flag is on. See testnet-drift-experiment-2026-06-03.md for the discovered behavior: the flag can stay "stuck on" all day if drift accumulates slowly via small updates after an override.
fn is_rate_band_overridden(
env: soroban_sdk::Env,
deposit_asset: soroban_sdk::Address,
payment_asset: soroban_sdk::Address,
) -> bool
Toggle whether draw() MUST perform the atomic gov settlement. Admin-only. Turning this on with GovAddr unset makes every draw fail (GovNotSet) rather than settle NGNC without RCAD โ refuse to set the foot-gun. Go-live order: set_gov_addr first, then turn this on.
fn set_require_atomic_settlement(env: soroban_sdk::Env, required: bool)