pub struct OnboardingBridge;Implementations§
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub fn initialize(
env: Env,
admin: Address,
fee_collector: Address,
fee_bps: u32,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn initialize( env: Env, admin: Address, fee_collector: Address, fee_bps: u32, nonce: Option<u64>, ) -> Result<(), BridgeError>
Initialises the bridge contract. Must be called exactly once before any other function.
Sets the admin, fee collector, and initial fee rate, then marks the contract as initialised and extends the instance TTL.
§Arguments
admin(Address) — Address that will hold administrative privileges. Must authorise this call.fee_collector(Address) — Address entitled to callwithdraw_fees.fee_bps(u32) — Initial fee in basis points. Must be ≤ 1 000 (10 %).nonce(Option<u64>) — Optional sequential nonce for the admin. PassNoneto skip nonce enforcement.
§Authorization
Requires admin.require_auth().
§Errors
BridgeError::AlreadyInitialized— Contract has already been initialised.BridgeError::FeeTooHigh—fee_bpsexceeds 1 000.BridgeError::DuplicateNonce—noncedoes not match the stored value.
§Events
("Initialized", admin, fee_collector)— data:(fee_bps,)
§Security Considerations
This function is the single gate that prevents double-initialisation.
The check is performed before require_auth so that the initialised flag
is always respected regardless of authorisation state. Deploy and call
initialize atomically (e.g. in the same transaction) to prevent
front-running by a third party who could set themselves as admin.
§Examples
// bridge.initialize(&admin, &fee_collector, &50u32, &None);
// assert_eq!(bridge.query_fee_bps(), 50u32);Sourcepub fn initialize_with_wasm_hash(
env: Env,
admin: Address,
fee_collector: Address,
fee_bps: u32,
nonce: Option<u64>,
wasm_hash: BytesN<32>,
) -> Result<(), BridgeError>
pub fn initialize_with_wasm_hash( env: Env, admin: Address, fee_collector: Address, fee_bps: u32, nonce: Option<u64>, wasm_hash: BytesN<32>, ) -> Result<(), BridgeError>
Initializes the bridge and records the hash of the deployed WASM.
Deploy tooling must pass the SHA-256 hash of the exact WASM artifact
used to create this contract. The value becomes the old_hash in the
first ContractUpgraded event.
Sourcepub fn fund_c_address(
env: Env,
source: Address,
target: Address,
asset: Address,
amount: i128,
nonce: Option<u64>,
deadline: Option<u64>,
) -> Result<(), BridgeError>
pub fn fund_c_address( env: Env, source: Address, target: Address, asset: Address, amount: i128, nonce: Option<u64>, deadline: Option<u64>, ) -> Result<(), BridgeError>
Funds a C-address with tokens from a source account.
Transfers amount from source into the contract, deducts the
effective fee, then forwards the net amount to target. The effective
fee is the minimum of the global fee rate, the per-asset cap, and any
volume-based tier that applies to source.
If a loyalty token has been configured, the contract mints a loyalty
reward to source after the transfer.
§Arguments
source(Address) — The account providing the tokens. Must authorise.target(Address) — The C-address receiving the net amount.asset(Address) — The whitelisted token contract address.amount(i128) — Gross amount to transfer. Must be > 0.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp (seconds) after which the call is rejected. PassNonefor no expiry.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TransactionExpired—deadlineis in the past.BridgeError::InvalidAmount—amount≤ 0.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode is on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::DailyLimitExceeded— Transfer would exceedsource’s daily limit for this asset.BridgeError::DuplicateNonce—noncemismatch.
§Events
("CAddressFunded", asset, source, target)— data:(amount, fee)
§Security Considerations
Access checks (check_access) are evaluated before require_auth so
that blocked/non-allowlisted targets are rejected without consuming the
caller’s authorization budget. The fee is floored (integer division), so
for very small amounts the effective fee may be 0.
§Examples
// Fund 500 stroops to `target` with no deadline or nonce:
// bridge.fund_c_address(&source, &target, &usdc, &500i128, &None, &None);Sourcepub fn batch_fund_c_address(
env: Env,
source: Address,
targets: Vec<Address>,
amounts: Vec<i128>,
asset: Address,
nonce: Option<u64>,
deadline: Option<u64>,
) -> Result<(), BridgeError>
pub fn batch_fund_c_address( env: Env, source: Address, targets: Vec<Address>, amounts: Vec<i128>, asset: Address, nonce: Option<u64>, deadline: Option<u64>, ) -> Result<(), BridgeError>
Funds multiple C-addresses in a single transaction from one source account.
Pulls sum(amounts) from source in one token transfer, then iterates
over each (target, amount) pair. Blocked or non-allowlisted targets are
skipped (their amount is refunded to source) rather than aborting
the entire batch. A single BatchCompleted event summarises successes
and failures at the end.
Transfers to the same target address are aggregated into a single token transfer to reduce fee consumption.
§Arguments
source(Address) — The account providing all tokens. Must authorise.targets(Vec<Address>) — Ordered list of recipient C-addresses.amounts(Vec<i128>) — Gross amount for each recipient. Must be the same length astargets. Every element must be > 0.asset(Address) — The whitelisted token contract address.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp cutoff.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::BatchTooLarge—targets.len()exceedsMAX_BATCH_SIZE(100).BridgeError::TransactionExpired—deadlineis in the past.BridgeError::MismatchedArrays—targets.len() != amounts.len().BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::InvalidAmount— Any element ofamountsis ≤ 0 or below the configured minimum transfer amount.BridgeError::DailyLimitExceeded— The aggregate batch amount would exceedsource’s daily limit for this asset.BridgeError::DuplicateNonce—noncemismatch.
§Events
("CAddressFunded", asset, source, target)— Emitted per successful entry; data:(amount, fee).("BatchTransferFailed", source, target)— Emitted per skipped entry; data:(amount, "access_denied").("BatchCompleted", source)— Emitted once at the end; data:(num_success, num_failures).
§Security Considerations
The full batch total is pulled from source upfront. If any entries are
blocked, those amounts are returned to source at the end of execution.
The validation loop that checks for zero/negative amounts runs before
the initial token pull, so no tokens are moved on validation failures.
§Examples
// let targets = Vec::from_array(&env, [addr1, addr2]);
// let amounts = Vec::from_array(&env, [1000i128, 500i128]);
// bridge.batch_fund_c_address(&source, &targets, &amounts, &usdc, &None, &None);Sourcepub fn set_fee_bps(
env: Env,
new_fee_bps: u32,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn set_fee_bps( env: Env, new_fee_bps: u32, nonce: Option<u64>, ) -> Result<(), BridgeError>
Updates the global fee rate in basis points.
The new rate applies to all subsequent fund_c_address and
batch_fund_c_address calls. Per-asset caps and volume tiers further
constrain the effective rate downward.
§Arguments
new_fee_bps(u32) — New fee rate. Must be ≤ 1 000 (10 %).nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::FeeTooHigh—new_fee_bpsexceeds 1 000.BridgeError::DuplicateNonce—noncemismatch.
§Events
("FeeBpsChanged", old_fee_bps, new_fee_bps)— data:(admin,)
§Examples
// bridge.set_fee_bps(&200u32, &None); // set to 2 %
// assert_eq!(bridge.query_fee_bps(), 200u32);Sourcepub fn set_source_daily_limit(
env: Env,
source: Address,
asset: Address,
limit_amount: i128,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn set_source_daily_limit( env: Env, source: Address, asset: Address, limit_amount: i128, nonce: Option<u64>, ) -> Result<(), BridgeError>
Sets a maximum daily transfer limit for a specific (source, asset) pair.
Once set, any fund_c_address call from source using asset that
would push the day’s cumulative volume past limit_amount is rejected.
Set limit_amount to 0 to disable the limit entirely.
§Arguments
source(Address) — The address whose daily throughput is being capped.asset(Address) — The asset the limit applies to.limit_amount(i128) — Maximum gross tokens allowed per calendar day (UTC, measured in ledger timestamp / 86 400). Use0to disable.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Examples
// Allow user to move at most 10 000 USDC per day:
// bridge.set_source_daily_limit(&user, &usdc, &10_000i128, &None);Sourcepub fn query_source_daily_limit(
env: Env,
source: Address,
asset: Address,
) -> Result<i128, BridgeError>
pub fn query_source_daily_limit( env: Env, source: Address, asset: Address, ) -> Result<i128, BridgeError>
Returns the daily transfer limit for a (source, asset) pair.
Returns 0 if no limit has been configured, meaning transfers are
unrestricted for that pair.
§Arguments
source(Address) — The address to query.asset(Address) — The asset to query.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn set_asset_fee_cap(
env: Env,
asset: Address,
max_fee_bps: u32,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn set_asset_fee_cap( env: Env, asset: Address, max_fee_bps: u32, nonce: Option<u64>, ) -> Result<(), BridgeError>
Sets a per-asset maximum fee cap in basis points.
The effective fee for asset is min(global_fee_bps, cap).
Useful for stablecoins or high-value assets where the global rate
would otherwise be too aggressive.
§Arguments
asset(Address) — The token contract whose fee is being capped.max_fee_bps(u32) — Cap in basis points. Must be ≤ 1 000.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::FeeTooHigh—max_fee_bpsexceeds 1 000.BridgeError::DuplicateNonce—noncemismatch.
§Examples
// Cap USDC fees at 0.5 % regardless of global rate:
// bridge.set_asset_fee_cap(&usdc, &50u32, &None);Sourcepub fn query_asset_fee_cap(env: Env, asset: Address) -> Result<u32, BridgeError>
pub fn query_asset_fee_cap(env: Env, asset: Address) -> Result<u32, BridgeError>
Returns the fee cap configured for asset.
Returns the contract-wide MAX_FEE_BPS (1 000) if no cap has been set,
meaning the global rate applies uncapped.
§Arguments
asset(Address) — The token contract to query.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn set_fee_collector(
env: Env,
new_fee_collector: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn set_fee_collector( env: Env, new_fee_collector: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Changes the address that is entitled to call withdraw_fees.
§Arguments
new_fee_collector(Address) — Replacement fee collector.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::DuplicateNonce—noncemismatch.
§Events
("FeeCollectorChanged", old_collector, new_fee_collector)— data:(admin,)
§Examples
// bridge.set_fee_collector(&new_collector, &None);
// assert_eq!(bridge.query_fee_collector(), new_collector);Sourcepub fn propose_new_fee_collector(
env: Env,
new_collector: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn propose_new_fee_collector( env: Env, new_collector: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Proposes a fee-collector handoff that must be accepted by new_collector.
This two-step path is preferred over set_fee_collector for normal
operations because it proves the proposed collector controls the key
before the role is moved.
Sourcepub fn accept_fee_collector(env: Env) -> Result<(), BridgeError>
pub fn accept_fee_collector(env: Env) -> Result<(), BridgeError>
Accepts a pending fee-collector handoff.
pub fn query_pending_fee_collector(env: Env) -> Option<Address>
pub fn set_admin( env: Env, new_admin: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Sourcepub fn propose_new_admin(
env: Env,
new_admin: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn propose_new_admin( env: Env, new_admin: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Proposes an admin handoff that must be accepted by new_admin.
This two-step path is preferred over set_admin for normal operations
because it prevents accidentally transferring control to an unusable key.
Sourcepub fn accept_admin(env: Env) -> Result<(), BridgeError>
pub fn accept_admin(env: Env) -> Result<(), BridgeError>
Accepts a pending admin handoff.
pub fn query_pending_admin(env: Env) -> Option<Address>
Sourcepub fn clear_pending_admin(
env: Env,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn clear_pending_admin( env: Env, nonce: Option<u64>, ) -> Result<(), BridgeError>
Cancels a pending admin handoff.
Sourcepub fn clear_pending_fee_collector(
env: Env,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn clear_pending_fee_collector( env: Env, nonce: Option<u64>, ) -> Result<(), BridgeError>
Cancels a pending fee-collector handoff.
pub fn set_minimum_amount( env: Env, amount: i128, nonce: Option<u64>, ) -> Result<(), BridgeError>
Sourcepub fn query_minimum_amount(env: Env) -> Result<i128, BridgeError>
pub fn query_minimum_amount(env: Env) -> Result<i128, BridgeError>
Returns the configured minimum transfer amount.
Returns the value last stored by set_minimum_amount, or 0 (no
minimum) if it has never been called. The minimum is enforced by
fund_c_address, which rejects amounts below it with
BridgeError::InvalidAmount.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn withdraw_fees(
env: Env,
asset: Address,
amount: i128,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn withdraw_fees( env: Env, asset: Address, amount: i128, nonce: Option<u64>, ) -> Result<(), BridgeError>
Withdraws accrued protocol fees to the fee collector.
Transfers amount of asset from the contract to the fee collector and
decrements the on-chain accrued-fees counter.
§Arguments
asset(Address) — The token contract whose accrued fees are being withdrawn.amount(i128) — Amount to withdraw. Must be > 0 and ≤ accrued balance.nonce(Option<u64>) — Optional sequential nonce for the fee collector.
§Authorization
Requires the current fee collector’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—amount≤ 0.BridgeError::InsufficientReclaimable—amountexceeds the accrued fee balance forasset.BridgeError::DuplicateNonce—noncemismatch.
§Events
("FeesWithdrawn", fee_collector)— data:(amount, asset)
§Security Considerations
Only the fee collector may call this function. Accrued fees are tracked
separately from the contract’s token balance, so this function can never
withdraw tokens that were sent to the contract for other purposes (use
reclaim_tokens for that).
§Examples
// Withdraw all 5 accrued fee tokens:
// bridge.withdraw_fees(&usdc, &5i128, &None);pub fn set_max_withdraw_per_tx( env: Env, amount: i128, nonce: Option<u64>, ) -> Result<(), BridgeError>
pub fn query_max_withdraw_per_tx(env: Env) -> Result<i128, BridgeError>
pub fn query_fee_bps(env: Env) -> Result<u32, BridgeError>
Sourcepub fn set_referral_rate(
env: Env,
bps: u32,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn set_referral_rate( env: Env, bps: u32, nonce: Option<u64>, ) -> Result<(), BridgeError>
Sets the referral fee rate as a share of the protocol fee.
When fund_c_address_with_referral is called with a referrer, the
referrer receives fee × referral_rate / 10_000 of the protocol fee,
and the remainder accrues to the contract.
§Arguments
bps(u32) — Referral share in basis points relative to the fee (0–10 000). E.g.2000means the referrer gets 20 % of the fee.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::FeeTooHigh—bpsexceeds 10 000.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ReferralRateChanged", bps)— no additional data.
Sourcepub fn query_referral_rate(env: Env) -> Result<u32, BridgeError>
pub fn query_referral_rate(env: Env) -> Result<u32, BridgeError>
Returns the current referral rate in basis points.
Returns 0 (no referral split) if set_referral_rate has never been called.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn fund_c_address_with_referral(
env: Env,
source: Address,
target: Address,
asset: Address,
amount: i128,
referrer: Option<Address>,
nonce: Option<u64>,
deadline: Option<u64>,
) -> Result<(), BridgeError>
pub fn fund_c_address_with_referral( env: Env, source: Address, target: Address, asset: Address, amount: i128, referrer: Option<Address>, nonce: Option<u64>, deadline: Option<u64>, ) -> Result<(), BridgeError>
Funds a C-address with an optional referrer that receives a share of the fee.
Behaves identically to fund_c_address except that when referrer is
Some(addr), the referral portion of the protocol fee is transferred
directly to that address immediately. The remainder accrues in the
contract as usual.
fee = floor(amount × effective_fee_bps / 10_000)
referral_fee = floor(fee × referral_rate / 10_000) (0 if referrer is None)
protocol_fee = fee − referral_fee
net = amount − fee§Arguments
source(Address) — The account providing the tokens. Must authorise.target(Address) — The C-address receivingnettokens.asset(Address) — The whitelisted token contract.amount(i128) — Gross amount. Must be > 0.referrer(Option<Address>) — Address to receive the referral cut, orNonefor no referral.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp cutoff.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—amount≤ 0.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::DailyLimitExceeded— Daily limit exceeded for(source, asset).BridgeError::InvalidReferrer—referrerissourceortarget.
§Events
("ReferralPaid", source, referrer)— Emitted only whenreferrerisSomeandreferral_fee > 0; data:(rf, asset).("CAddressFunded", asset, source, target)— data:(amount, fee).
§Security Considerations
As with the other funding entry points, callers may provide a nonce
and deadline for ordered replay protection and expiry.
§Examples
// bridge.fund_c_address_with_referral(
// &source, &target, &usdc, &1000i128, &Some(referrer), &None, &None,
// );Sourcepub fn query_fee_collector(env: Env) -> Result<Address, BridgeError>
pub fn query_fee_collector(env: Env) -> Result<Address, BridgeError>
Returns the current fee collector address.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_admin(env: Env) -> Result<Address, BridgeError>
pub fn query_admin(env: Env) -> Result<Address, BridgeError>
Returns the current admin address.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_balance(env: Env, c_address: Address, asset: Address) -> i128
pub fn query_balance(env: Env, c_address: Address, asset: Address) -> i128
Returns the token balance of c_address for asset.
This is a pure read-through to the token contract; it does not require the contract to be initialised and has no access-control checks.
§Arguments
c_address(Address) — The address whose balance is queried.asset(Address) — The token contract address.
Sourcepub fn query_all_balances(
env: Env,
assets: Vec<Address>,
) -> Result<Map<Address, i128>, BridgeError>
pub fn query_all_balances( env: Env, assets: Vec<Address>, ) -> Result<Map<Address, i128>, BridgeError>
Returns the bridge contract’s own balance for each asset in assets.
Useful for monitoring the contract’s total holdings across multiple tokens in a single call.
§Arguments
assets(Vec<Address>) — List of token contract addresses to query. Must contain at mostMAX_BATCH_SIZE(100) entries.
§Returns
A Map<Address, i128> mapping each asset address to the contract’s balance.
Assets with a zero balance are included.
§Errors
BridgeError::BatchTooLarge—assets.len()exceedsMAX_BATCH_SIZE(100).
§Examples
// let assets = Vec::from_array(&env, [usdc, xlm]);
// let balances = bridge.query_all_balances(&assets);Sourcepub fn query_contract_balance(
env: Env,
asset: Address,
) -> Result<i128, BridgeError>
pub fn query_contract_balance( env: Env, asset: Address, ) -> Result<i128, BridgeError>
Returns the contract’s total token balance for asset.
This includes both accrued fees and any tokens held for other purposes
(e.g. timelocked funds). Use query_accrued_fees to isolate the fee
portion.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_fee_balance(env: Env, asset: Address) -> Result<i128, BridgeError>
pub fn query_fee_balance(env: Env, asset: Address) -> Result<i128, BridgeError>
Deprecated compatibility alias for [query_contract_balance].
Sourcepub fn query_is_initialized(env: Env) -> bool
pub fn query_is_initialized(env: Env) -> bool
Returns true if the contract has been initialised.
Sourcepub fn query_nonce(env: Env, caller: Address) -> u64
pub fn query_nonce(env: Env, caller: Address) -> u64
Returns the current sequential nonce value for caller.
The returned value is the next nonce that must be passed to succeed if
the caller chooses to enforce nonce checking. Returns 0 for addresses
that have never used a nonce.
§Arguments
caller(Address) — The address whose nonce is queried.
Sourcepub fn query_calculate_fee(
env: Env,
gross_amount: i128,
) -> Result<(i128, i128), BridgeError>
pub fn query_calculate_fee( env: Env, gross_amount: i128, ) -> Result<(i128, i128), BridgeError>
Simulates the fee and net amount for a given gross amount at the current global fee rate.
Does not account for per-asset caps or volume tiers; use this for a quick estimate only.
§Arguments
gross_amount(i128) — The hypothetical gross transfer amount.
§Returns
(fee, net) where fee = floor(gross × fee_bps / 10_000) and
net = gross − fee.
§Examples
// At fee_bps = 100 (1 %):
// let (fee, net) = bridge.query_calculate_fee(&1000i128);
// assert_eq!(fee, 10i128);
// assert_eq!(net, 990i128);Sourcepub fn query_effective_fee(
env: Env,
source: Address,
asset: Address,
amount: i128,
) -> Result<(u32, i128, i128), BridgeError>
pub fn query_effective_fee( env: Env, source: Address, asset: Address, amount: i128, ) -> Result<(u32, i128, i128), BridgeError>
Returns the real effective fee that would be charged for a specific
(source, asset, amount) combination, taking into account the global
fee rate, any volume-based tier applicable to source, and the
per-asset fee cap.
Unlike Self::query_calculate_fee, which only uses the flat global
fee rate, this function runs the full fee-resolution pipeline:
- Global rate — the contract-wide fee_bps.
- Volume tier — if
source's cumulative bridged volume falls within a configuredFeeTier, its rate provides an additional upper bound. - Asset cap — the per-asset maximum fee cap provides another upper bound.
The referral fee split does not affect the gross fee amount; it only determines how the fee is distributed between the fee collector and the referrer.
§Arguments
source— The address that would provide the tokens.asset— The whitelisted token contract address.amount— The hypothetical gross transfer amount.
§Returns
(effective_fee_bps, fee, net) where:
effective_fee_bpsis the resolved basis-point rate after cap + tier.fee = floor(amount × effective_fee_bps / 10_000).net = amount − fee.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::Overflow— Arithmetic overflow in fee calculation.
§Examples
// At fee_bps = 100 (1 %), no asset cap, no tiers:
// let (bps, fee, net) = bridge.query_effective_fee(&source, &usdc, &1000i128);
// assert_eq!(bps, 100u32);
// assert_eq!(fee, 10i128);
// assert_eq!(net, 990i128);Sourcepub fn query_total_bridged(
env: Env,
asset: Address,
) -> Result<i128, BridgeError>
pub fn query_total_bridged( env: Env, asset: Address, ) -> Result<i128, BridgeError>
Returns the cumulative net amount of asset that has been delivered to
recipients since deployment.
“Total bridged” counts only the net portion (gross minus fee), not the gross transferred by sources.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_total_fees_collected(
env: Env,
asset: Address,
) -> Result<i128, BridgeError>
pub fn query_total_fees_collected( env: Env, asset: Address, ) -> Result<i128, BridgeError>
Returns the cumulative gross fees collected for asset since deployment.
This counter only increases and is not decremented when fees are
withdrawn. To see the currently pending (not yet withdrawn) fee
balance, use query_accrued_fees.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn pause(env: Env, nonce: Option<u64>) -> Result<(), BridgeError>
pub fn pause(env: Env, nonce: Option<u64>) -> Result<(), BridgeError>
Pauses the contract, disabling all mutating operations.
While paused, calls to fund_c_address, batch_fund_c_address,
withdraw_fees, set_fee_bps, set_fee_collector, set_admin, and
several other state-modifying functions return
BridgeError::ContractPaused. Read-only query_* functions are
unaffected.
§Arguments
nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractPaused",)— data:(admin,)
§Security Considerations
Pausing is an emergency mechanism. It does not prevent the admin from scheduling or executing upgrades, which are intentionally not pause-gated so that an upgrade can fix whatever condition required the pause.
Sourcepub fn unpause(env: Env, nonce: Option<u64>) -> Result<(), BridgeError>
pub fn unpause(env: Env, nonce: Option<u64>) -> Result<(), BridgeError>
Resumes normal contract operation after a pause.
§Arguments
nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractUnpaused",)— data:(admin,)
Sourcepub fn query_is_paused(env: Env) -> bool
pub fn query_is_paused(env: Env) -> bool
Returns true if the contract is currently paused.
Sourcepub fn upgrade(
env: Env,
new_wasm_hash: BytesN<32>,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn upgrade( env: Env, new_wasm_hash: BytesN<32>, nonce: Option<u64>, ) -> Result<(), BridgeError>
Immediately upgrades the contract WASM to new_wasm_hash.
This is the untimelocked upgrade path retained for development,
testnet, and controlled break-glass operations. For production
deployments, prefer schedule_upgrade + execute_upgrade which
enforces a ~24-hour delay, giving users time to react.
§Arguments
new_wasm_hash(BytesN<32>) — The hash of the new WASM blob, which must already have been uploaded to the network viaDeployer::upload_contract_wasm.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractUpgraded",)— data:(old_hash, new_wasm_hash, admin)
§Security Considerations
After this call the contract executes new code in the same transaction.
The old_hash in the event lets off-chain monitors detect unexpected
upgrades. Consider using the timelocked path for mainnet deployments.
Sourcepub fn schedule_upgrade(
env: Env,
new_wasm_hash: BytesN<32>,
nonce: Option<u64>,
) -> Result<u32, BridgeError>
pub fn schedule_upgrade( env: Env, new_wasm_hash: BytesN<32>, nonce: Option<u64>, ) -> Result<u32, BridgeError>
Schedules a WASM upgrade that becomes executable after a ~24-hour timelock.
The upgrade is executable once
env.ledger().sequence() ≥ current_sequence + UPGRADE_TIMELOCK_LEDGERS
(17 280 ledgers at 5 s/ledger ≈ 24 hours), and remains executable for
one additional timelock-length window.
Only one pending upgrade may exist at a time. Call cancel_upgrade
first if you need to replace a pending upgrade.
§Arguments
new_wasm_hash(BytesN<32>) — Hash of the new WASM blob to apply after the timelock elapses.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Returns
The ledger sequence number at or after which execute_upgrade may be
called (executable_after_ledger).
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("UpgradeScheduled",)— data:(new_wasm_hash, executable_after_ledger, admin)
§Security Considerations
Off-chain monitoring tools should watch for UpgradeScheduled events
and alert stakeholders so they can review the proposed WASM before the
timelock expires. Use cancel_upgrade to abort if the scheduled hash
turns out to be malicious.
§Examples
// let unlock_ledger = bridge.schedule_upgrade(&new_wasm_hash, &None);
// // wait until env.ledger().sequence() >= unlock_ledger, then:
// bridge.execute_upgrade(&new_wasm_hash, &None);Sourcepub fn execute_upgrade(
env: Env,
expected_hash: BytesN<32>,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn execute_upgrade( env: Env, expected_hash: BytesN<32>, nonce: Option<u64>, ) -> Result<(), BridgeError>
Executes a previously scheduled upgrade once its timelock has elapsed.
expected_hash must match the hash that was passed to schedule_upgrade.
This prevents a race condition where the admin could change the pending
hash between scheduling and execution by requiring the caller to commit
to the exact hash they are applying.
The pending upgrade record is cleared before calling
update_current_contract_wasm to prevent re-entrant replay.
§Arguments
expected_hash(BytesN<32>) — Must matchPendingUpgrade::new_wasm_hash.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::UpgradeNotScheduled— No pending upgrade exists.BridgeError::UpgradeHashMismatch—expected_hashdoes not match the scheduled hash.BridgeError::UpgradeTimelockActive— The timelock has not yet elapsed.BridgeError::UpgradeTimelockExpired— The execution window has elapsed.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractUpgraded",)— data:(old_hash, new_wasm_hash, admin)
Sourcepub fn cancel_upgrade(env: Env, nonce: Option<u64>) -> Result<(), BridgeError>
pub fn cancel_upgrade(env: Env, nonce: Option<u64>) -> Result<(), BridgeError>
Cancels a pending scheduled upgrade.
After cancellation, execute_upgrade will return
BridgeError::UpgradeNotScheduled until a new upgrade is scheduled.
§Arguments
nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::UpgradeNotScheduled— No pending upgrade to cancel.BridgeError::DuplicateNonce—noncemismatch.
§Events
("UpgradeCancelled",)— data:(cancelled_wasm_hash, admin)
Sourcepub fn query_pending_upgrade(env: Env) -> Option<PendingUpgrade>
pub fn query_pending_upgrade(env: Env) -> Option<PendingUpgrade>
Returns the pending scheduled upgrade, if any.
Returns None if no upgrade has been scheduled or if a previous
upgrade has already been executed or cancelled.
Sourcepub fn emergency_migrate(
env: Env,
new_contract: Address,
migrate_data: bool,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn emergency_migrate( env: Env, new_contract: Address, migrate_data: bool, nonce: Option<u64>, ) -> Result<(), BridgeError>
Migrates the contract state to a new contract address in case of emergency.
§Arguments
new_contract(Address) — The address of the new contract.migrate_data(bool) — If true, emits a complete snapshot of the supported configuration and accounting state as events.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
Sourcepub fn add_to_blocklist(
env: Env,
address: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn add_to_blocklist( env: Env, address: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Adds address to the blocklist.
Blocked addresses cannot be used as target in any funding call.
Existing timelocked entries for a blocked address are not affected
retroactively; however, claim_timelocked itself is not blocked (the
recipient calls it directly). Blocking takes effect immediately for all
new funding calls.
§Arguments
address(Address) — The address to block.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn remove_from_blocklist(
env: Env,
address: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn remove_from_blocklist( env: Env, address: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Removes address from the blocklist, restoring its ability to receive funds.
§Arguments
address(Address) — The address to unblock.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn add_to_allowlist(
env: Env,
address: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn add_to_allowlist( env: Env, address: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Adds address to the allowlist.
Only relevant when the contract is in allowlist mode
(set_allowlist_mode(true)). In that mode, only allowlisted addresses
may be used as target in funding calls.
§Arguments
address(Address) — The address to allowlist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn remove_from_allowlist(
env: Env,
address: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn remove_from_allowlist( env: Env, address: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Removes address from the allowlist.
If the contract is in allowlist mode, the address will no longer be able to receive funds until re-added.
§Arguments
address(Address) — The address to remove from the allowlist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn set_allowlist_mode(
env: Env,
enabled: bool,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn set_allowlist_mode( env: Env, enabled: bool, nonce: Option<u64>, ) -> Result<(), BridgeError>
Enables or disables allowlist mode.
When enabled is true, only addresses that have been explicitly added
via add_to_allowlist may receive tokens. When false (the default),
any non-blocked address may receive tokens.
The blocklist is always enforced regardless of this setting.
§Arguments
enabled(bool) —trueto enable allowlist mode,falseto disable.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn query_is_blocked(env: Env, address: Address) -> bool
pub fn query_is_blocked(env: Env, address: Address) -> bool
Returns true if address is on the blocklist.
Sourcepub fn query_is_allowlisted(env: Env, address: Address) -> bool
pub fn query_is_allowlisted(env: Env, address: Address) -> bool
Returns true if address is on the allowlist.
Sourcepub fn query_allowlist_mode(env: Env) -> bool
pub fn query_allowlist_mode(env: Env) -> bool
Returns true if allowlist mode is currently enabled.
Sourcepub fn reclaim_tokens(
env: Env,
asset: Address,
amount: i128,
destination: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn reclaim_tokens( env: Env, asset: Address, amount: i128, destination: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Allows the admin to recover tokens that were accidentally sent to the contract and are not owed as fees.
While active, the reclaimable amount is
contract_token_balance − accrued_fees − locked_timelock. After an
emergency migration deactivates the contract, the full remaining
balance is reclaimable so the old contract cannot strand funds.
§Arguments
asset(Address) — The token to reclaim.amount(i128) — Amount to recover. Must be > 0 and ≤ reclaimable.destination(Address) — Address to send the recovered tokens to.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidAmount—amount≤ 0.BridgeError::InsufficientReclaimable—amountexceedscontract_balance − accrued_fees.BridgeError::DuplicateNonce—noncemismatch.
§Events
("TokensReclaimed", admin, asset)— data:(amount, destination)
§Security Considerations
While active, the check
reclaimable = balance − accrued_fees − locked_timelock ensures that
fee reserves and unclaimed TimelockEntry deposits are ring-fenced.
After emergency_migrate, recovery intentionally takes precedence over
those reservations because the old contract is permanently disabled.
Unrevealed CommitmentEntry records never hold contract balance in the
first place.
Sourcepub fn add_asset(
env: Env,
asset: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn add_asset( env: Env, asset: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Adds asset to the token whitelist.
Only whitelisted assets may be used in fund_c_address,
batch_fund_c_address, and related funding functions. Adding an asset
that is already whitelisted is idempotent.
§Arguments
asset(Address) — The token contract address to whitelist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn remove_asset(
env: Env,
asset: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn remove_asset( env: Env, asset: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Removes asset from the token whitelist.
After removal, any funding call that references this asset returns
BridgeError::AssetNotWhitelisted. Existing accrued fee counters
and historical stats for the asset are retained in storage.
§Arguments
asset(Address) — The token contract address to remove.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn query_is_asset_whitelisted(
env: Env,
asset: Address,
) -> Result<bool, BridgeError>
pub fn query_is_asset_whitelisted( env: Env, asset: Address, ) -> Result<bool, BridgeError>
Returns true if asset is currently on the whitelist.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_whitelisted_assets(
env: Env,
offset: u32,
limit: u32,
) -> Result<Vec<Address>, BridgeError>
pub fn query_whitelisted_assets( env: Env, offset: u32, limit: u32, ) -> Result<Vec<Address>, BridgeError>
Returns a page of currently whitelisted asset addresses.
The whitelist can grow without bound over the contract’s lifetime, so results are paginated rather than returned in a single unbounded call.
§Arguments
offset(u32) — Number of whitelist entries to skip.limit(u32) — Maximum number of entries to return. Capped atMAX_BATCH_SIZE(100); values above that are silently clamped.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn add_swap_pool(
env: Env,
pool: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn add_swap_pool( env: Env, pool: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Adds pool to the DEX swap-pool whitelist.
Only whitelisted pool addresses may appear in the swap_route passed
to fund_c_address_with_swap. Adding a pool that is already
whitelisted is idempotent.
§Arguments
pool(Address) — The DEX pool contract address to whitelist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn remove_swap_pool(
env: Env,
pool: Address,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn remove_swap_pool( env: Env, pool: Address, nonce: Option<u64>, ) -> Result<(), BridgeError>
Removes pool from the DEX swap-pool whitelist.
After removal, any fund_c_address_with_swap call whose swap_route
references this pool returns BridgeError::PoolNotWhitelisted.
§Arguments
pool(Address) — The DEX pool contract address to remove.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Sourcepub fn query_is_pool_whitelisted(
env: Env,
pool: Address,
) -> Result<bool, BridgeError>
pub fn query_is_pool_whitelisted( env: Env, pool: Address, ) -> Result<bool, BridgeError>
Returns true if pool is currently on the swap-pool whitelist.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn set_loyalty_token(
env: Env,
token: Address,
amount_per_fund: i128,
) -> Result<(), BridgeError>
pub fn set_loyalty_token( env: Env, token: Address, amount_per_fund: i128, ) -> Result<(), BridgeError>
Configures the loyalty token and the fixed reward minted to the source
on every successful fund_c_address call.
The contract must already hold a balance of token equal to or greater
than the rewards it intends to distribute. There is no automatic minting;
the contract transfers from its own balance.
§Arguments
token(Address) — The loyalty token contract address.amount_per_fund(i128) — Fixed amount transferred tosourceon eachfund_c_addresscall. Use0to effectively disable rewards. Must be ≥ 0.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidAmount—amount_per_fund< 0.
§Events
("LoyaltyTokenSet", admin)— data:(token, amount_per_fund)
Sourcepub fn query_loyalty_token(env: Env) -> Result<(Address, i128), BridgeError>
pub fn query_loyalty_token(env: Env) -> Result<(Address, i128), BridgeError>
Returns the loyalty token address and reward amount per fund.
§Returns
(token_address, amount_per_fund).
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::LoyaltyTokenNotSet— No loyalty token has been configured.
Sourcepub fn set_fee_tiers(env: Env, tiers: Vec<FeeTier>) -> Result<(), BridgeError>
pub fn set_fee_tiers(env: Env, tiers: Vec<FeeTier>) -> Result<(), BridgeError>
Configures volume-based fee tiers for the bridge.
Once tiers are set, the fee applied to a fund_c_address call is
determined by the source address’s cumulative bridged volume:
for each tier in tiers:
if source_volume ∈ [tier.min_volume, tier.max_volume]:
effective_fee_bps = min(global_fee_bps, tier.fee_bps)
break
else:
effective_fee_bps = global_fee_bps (fallback)The per-asset cap still applies on top of the tiered rate.
§Arguments
tiers(Vec<FeeTier>) — Ordered list of fee tiers. Must contain at mostMAX_FEE_TIERS(50) entries. Each tier’sfee_bpsmust be ≤ 1 000.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.- [
BridgeError::TooManyFeeTiers] —tiers.len()exceedsMAX_FEE_TIERS(50). BridgeError::FeeTooHigh— Any tier’sfee_bpsexceeds 1 000.
§Events
("FeeTiersSet", admin)— data:(tiers.len(),)
Sourcepub fn query_fee_tiers(env: Env) -> Result<Vec<FeeTier>, BridgeError>
pub fn query_fee_tiers(env: Env) -> Result<Vec<FeeTier>, BridgeError>
Returns the configured fee tiers.
If no tiers have been set, returns a single synthetic tier covering the
full volume range [0, i128::MAX] at the current global fee rate.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_current_tier(
env: Env,
source: Address,
) -> Result<FeeTier, BridgeError>
pub fn query_current_tier( env: Env, source: Address, ) -> Result<FeeTier, BridgeError>
Returns the fee tier that currently applies to source, based on their
cumulative bridged volume.
If no tier matches, returns a synthetic default tier using the global fee rate, covering the full volume range.
§Arguments
source(Address) — The address to look up.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn fund_c_address_crosschain(
env: Env,
chain_id: u32,
tx_hash: BytesN<32>,
target: Address,
asset: Address,
amount: i128,
sigs: Vec<RelayerSig>,
) -> Result<(), BridgeError>
pub fn fund_c_address_crosschain( env: Env, chain_id: u32, tx_hash: BytesN<32>, target: Address, asset: Address, amount: i128, sigs: Vec<RelayerSig>, ) -> Result<(), BridgeError>
Credits a C-address from a cross-chain event, verified by M-of-N relayer signatures.
This function allows off-chain relayers to bridge tokens that arrived
on another chain (e.g. Ethereum, Solana) to a Soroban C-address.
The contract must already hold a sufficient balance of asset to pay
out net_amount to the target.
§Payload Construction
Relayers must sign sha256(payload) where:
nonce = sha256(chain_id_be4 || tx_hash)
payload = chain_id_be4
|| tx_hash
|| sha256(target_strkey_bytes)
|| sha256(asset_strkey_bytes)
|| amount_be16
|| nonce§Parameters
chain_id(u32) — Numeric source-chain ID (e.g. 1 = Ethereum mainnet, 101 = Solana mainnet).tx_hash(BytesN<32>) — The 32-byte hash of the source-chain transaction.target(Address) — The Soroban C-address to credit.asset(Address) — Whitelisted token contract address.amount(i128) — Gross amount (fee is deducted before creditingtarget).sigs(Vec<RelayerSig>) — At leastthresholddistinct relayer Ed25519 signatures over the payload hash (see above).
§Authorization
No Soroban require_auth — authentication is via Ed25519 signatures from
registered relayers. The caller may be any account.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—amount≤ 0.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::ReplayedNonce— This(chain_id, tx_hash)combination has already been processed.BridgeError::NotRelayer— A signature’s pubkey is not a registered relayer.BridgeError::DuplicateRelayerSignature— The same relayer pubkey appears more than once insigs.BridgeError::BelowThreshold— Fewer thanthresholdvalid signatures.BridgeError::InvalidAddress— A strkey exceedsMAX_STRKEY_LEN(64) bytes.
§Events
("CrossChainFunded", target)— data:(chain_id, tx_hash, amount, fee, asset)
§Security Considerations
The nonce is derived deterministically from (chain_id, tx_hash) and
marked used before the token transfer, preventing replay attacks. An
invalid Ed25519 signature causes a host-level trap (panic) rather than
returning an error code, so callers should pre-validate signatures
off-chain. The contract verifies that sigs contains distinct
pubkeys — a relayer submitting the same signature twice only counts
once toward the threshold; duplicates are rejected with
BridgeError::DuplicateRelayerSignature.
Sourcepub fn add_relayer(
env: Env,
pubkey: BytesN<32>,
nonce: Option<u64>,
) -> Result<(), BridgeError>
pub fn add_relayer( env: Env, pubkey: BytesN<32>, nonce: Option<u64>, ) -> Result<(), BridgeError>
Registers an Ed25519 public key as a trusted relayer.
Registered relayers may contribute signatures to fund_c_address_crosschain.
Adding the same public key twice is idempotent.
§Arguments
pubkey(BytesN<32>) — Ed25519 public key of the relayer.nonce(Option<u64>) — Optional sequential admin nonce.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncedoes not match the stored admin nonce.
§Events
("RelayerAdded",)— data:(admin, pubkey)
Sourcepub fn remove_relayer(env: Env, pubkey: BytesN<32>) -> Result<(), BridgeError>
pub fn remove_relayer(env: Env, pubkey: BytesN<32>) -> Result<(), BridgeError>
Removes a relayer from the trusted set.
The removal is rejected if it would reduce the active relayer count below the current threshold, which would make cross-chain funding impossible.
§Arguments
pubkey(BytesN<32>) — Ed25519 public key of the relayer to remove.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::BelowThreshold— Removing this relayer would drop the count below the required threshold.
Sourcepub fn set_relayer_threshold(
env: Env,
threshold: u32,
) -> Result<(), BridgeError>
pub fn set_relayer_threshold( env: Env, threshold: u32, ) -> Result<(), BridgeError>
Sets the minimum number of relayer signatures required to process a cross-chain funding event.
§Arguments
threshold(u32) — Must be ≤ the current number of registered relayers.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ThresholdExceedsRelayers—thresholdis greater than the number of registered relayers.
Sourcepub fn query_relayer_threshold(env: Env) -> Result<u32, BridgeError>
pub fn query_relayer_threshold(env: Env) -> Result<u32, BridgeError>
Returns the current M-of-N relayer signature threshold.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn query_is_relayer(
env: Env,
pubkey: BytesN<32>,
) -> Result<bool, BridgeError>
pub fn query_is_relayer( env: Env, pubkey: BytesN<32>, ) -> Result<bool, BridgeError>
Returns true if pubkey is a registered relayer.
§Arguments
pubkey(BytesN<32>) — Ed25519 public key to check.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn fund_c_address_timelocked(
env: Env,
source: Address,
target: Address,
asset: Address,
amount: i128,
release_time: u64,
cliff_time: u64,
nonce: Option<u64>,
deadline: Option<u64>,
) -> Result<u64, BridgeError>
pub fn fund_c_address_timelocked( env: Env, source: Address, target: Address, asset: Address, amount: i128, release_time: u64, cliff_time: u64, nonce: Option<u64>, deadline: Option<u64>, ) -> Result<u64, BridgeError>
Creates a time-gated funding record.
Transfers amount from source into the contract immediately. The
tokens remain locked until release_time, at which point target may
call claim_timelocked to receive the net amount (after fee deduction).
§Arguments
source(Address) — The address depositing the tokens. Must authorise.target(Address) — The address that may claim the tokens afterrelease_time.asset(Address) — The whitelisted token contract.amount(i128) — Gross amount to lock. Must be > 0.release_time(u64) — Unix timestamp (seconds) after which the tokens may be claimed. Must be strictly in the future.cliff_time(u64) — Optional cliff timestamp. If > 0 it must be ≤release_time. Currently informational only; not enforced byclaim_timelocked.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp (seconds) after which the call is rejected. PassNonefor no expiry.
§Authorization
Requires source.require_auth().
§Returns
The numeric ID of the newly created timelock entry. Use this ID with
claim_timelocked and query_timelocked.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TransactionExpired—deadlineis in the past.BridgeError::InvalidAmount—amount≤ 0.BridgeError::InvalidReleaseTime—release_time≤ current timestamp, orcliff_time > release_time.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::DuplicateNonce—noncemismatch.
§Events
("TimelockCreated", source, target)— data:(id, amount, asset, release_time, cliff_time)
§Security Considerations
The fee rate applied is the rate at claim time, not deposit time.
If the global fee rate changes between deposit and claim, the net amount
received by target may differ from the amount at deposit time.
Sourcepub fn claim_timelocked(env: Env, id: u64) -> Result<(), BridgeError>
pub fn claim_timelocked(env: Env, id: u64) -> Result<(), BridgeError>
Claims a matured timelock entry, releasing the net tokens to target.
The effective fee at the time of claiming is deducted from amount and
the net is transferred to target. The timelock entry is marked
claimed = true to prevent double-claims.
§Arguments
id(u64) — The timelock entry ID returned byfund_c_address_timelocked.
§Authorization
Requires target.require_auth() (the recipient of the timelock entry).
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TimelockNotFound— No entry exists forid.BridgeError::TimelockNotMatured—release_timehas not passed yet.BridgeError::Unauthorized— The entry has already been claimed.
§Events
("TimelockClaimed", target)— data:(id, net_amount, fee, asset)
§Security Considerations
The claimed flag is persisted before the token transfer. Because
Soroban execution is single-threaded within a ledger, this effectively
prevents re-entrancy. The fee rate is the current global rate at
claim time, which may differ from the rate at deposit time.
Sourcepub fn query_timelocked(env: Env, id: u64) -> Result<TimelockEntry, BridgeError>
pub fn query_timelocked(env: Env, id: u64) -> Result<TimelockEntry, BridgeError>
Returns the timelock entry for id.
§Arguments
id(u64) — The timelock entry ID.
§Errors
BridgeError::TimelockNotFound— No entry exists forid.
Sourcepub fn extend_instance_ttl(env: Env, ttl: u32) -> Result<(), BridgeError>
pub fn extend_instance_ttl(env: Env, ttl: u32) -> Result<(), BridgeError>
Extends the instance-storage TTL to ensure contract state does not expire.
ttl is capped at MAX_ALLOWED_TTL (3 110 400 ledgers, ~1 year).
The threshold used to trigger extension is ttl / 4.
§Arguments
ttl(u32) — Desired TTL in ledgers. Values belowMIN_ALLOWED_TTLare rejected; larger values are capped at the configuredMaxPersistentTtl.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
§Events
("InstanceTtlExtended",)— data:(admin, actual_ttl)
Sourcepub fn extend_persistent_ttl(
env: Env,
key_asset: Address,
ttl: u32,
) -> Result<(), BridgeError>
pub fn extend_persistent_ttl( env: Env, key_asset: Address, ttl: u32, ) -> Result<(), BridgeError>
Extends the persistent-storage TTL for the three per-asset counter keys
(AccruedFees, TotalBridged, TotalFeesCollected) of key_asset.
Only keys that already exist in storage are extended; missing keys are silently skipped.
§Arguments
key_asset(Address) — The asset whose persistent counters should have their TTL extended.ttl(u32) — Desired TTL in ledgers. Values belowMIN_ALLOWED_TTLare rejected; larger values are capped at the configuredMaxPersistentTtl.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
§Events
("PersistentTtlExtended",)— data:(admin, key_asset, actual_ttl)
Sourcepub fn extend_timelock_ttl(
env: Env,
id: u64,
ttl: u32,
) -> Result<(), BridgeError>
pub fn extend_timelock_ttl( env: Env, id: u64, ttl: u32, ) -> Result<(), BridgeError>
Extends the persistent-storage TTL for a specific timelock entry.
extend_persistent_ttl only ever covered per-asset counter keys, so a
long-dated TimelockEntry (see fund_c_address_timelocked) had no way
to have its TTL refreshed and could be archived by the network before
release_time, making it permanently unclaimable. Call this
periodically for any timelock whose release_time is far in the future.
§Arguments
id(u64) — The timelock entry ID, as returned byfund_c_address_timelocked.ttl(u32) — Desired TTL in ledgers. Values belowMIN_ALLOWED_TTLare rejected; larger values are capped at the configuredMaxPersistentTtl.
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::TimelockNotFound— No entry exists forid.
§Events
("TimelockTtlExtended",)— data:(id, actual_ttl)
Sourcepub fn extend_commitment_ttl(
env: Env,
id: u64,
ttl: u32,
) -> Result<(), BridgeError>
pub fn extend_commitment_ttl( env: Env, id: u64, ttl: u32, ) -> Result<(), BridgeError>
Extends the persistent-storage TTL for a specific commit-reveal entry.
§Arguments
id(u64) — The commitment ID, as returned bycommit_fund.ttl(u32) — Desired TTL in ledgers (capped atMAX_ALLOWED_TTL).
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::CommitmentNotFound— No entry exists forid.
§Events
("CommitmentTtlExtended",)— data:(id, actual_ttl)
Sourcepub fn extend_relayer_ttl(
env: Env,
pubkey: BytesN<32>,
ttl: u32,
) -> Result<(), BridgeError>
pub fn extend_relayer_ttl( env: Env, pubkey: BytesN<32>, ttl: u32, ) -> Result<(), BridgeError>
Extends the persistent-storage TTL for a registered relayer’s entry.
§Arguments
pubkey(BytesN<32>) — The relayer’s Ed25519 public key.ttl(u32) — Desired TTL in ledgers (capped atMAX_ALLOWED_TTL).
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::NotRelayer—pubkeyis not a registered relayer.
§Events
("RelayerTtlExtended",)— data:(pubkey, actual_ttl)
Sourcepub fn extend_source_persistent_ttl(
env: Env,
source: Address,
asset: Address,
ttl: u32,
) -> Result<(), BridgeError>
pub fn extend_source_persistent_ttl( env: Env, source: Address, asset: Address, ttl: u32, ) -> Result<(), BridgeError>
Extends the persistent-storage TTL for all per-(source, asset) and
per-source keys used by the core funding paths: SourceDailyLimit,
the current day’s DailyUsage, UserDeposit, SourceBridgedVolume,
the sequential Nonce, and the auth-entry AuthNonce counter.
Only keys that already exist in storage are extended; missing keys are
silently skipped, mirroring extend_persistent_ttl.
§Arguments
source(Address) — The address whose per-source entries should have their TTL extended.asset(Address) — The asset half of the(source, asset)keys.ttl(u32) — Desired TTL in ledgers (capped atMAX_ALLOWED_TTL).
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
§Events
("SourcePersistentTtlExtended",)— data:(admin, source, asset, actual_ttl)
Sourcepub fn set_max_instance_ttl(env: Env, ttl: u32) -> Result<(), BridgeError>
pub fn set_max_instance_ttl(env: Env, ttl: u32) -> Result<(), BridgeError>
Overrides the maximum instance-storage TTL used by the internal
extend_instance_ttl helper called on every mutating operation.
Values above MAX_ALLOWED_TTL are silently capped. Values below
MIN_ALLOWED_TTL are rejected outright: extend_instance_ttl derives
its extension threshold as max_ttl / 4, so a max of 0 (or any very
small value) would make the threshold 0 too, effectively disabling
TTL extension and risking near-term expiry of all instance data.
§Arguments
ttl(u32) — New maximum in ledgers. Must be ≥MIN_ALLOWED_TTL.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidTtl—ttlis belowMIN_ALLOWED_TTL.
Sourcepub fn set_max_persistent_ttl(env: Env, ttl: u32) -> Result<(), BridgeError>
pub fn set_max_persistent_ttl(env: Env, ttl: u32) -> Result<(), BridgeError>
Overrides the maximum persistent-storage TTL used by extend_persistent_ttl.
Values above MAX_ALLOWED_TTL are silently capped. Values below
MIN_ALLOWED_TTL are rejected for the same reason as
set_max_instance_ttl: a near-zero max would collapse the extension
threshold to zero and disable TTL extension entirely.
§Arguments
ttl(u32) — New maximum in ledgers. Must be ≥MIN_ALLOWED_TTL.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidTtl—ttlis belowMIN_ALLOWED_TTL.
Sourcepub fn query_ttl_config(env: Env) -> Result<(u32, u32, u32, u32), BridgeError>
pub fn query_ttl_config(env: Env) -> Result<(u32, u32, u32, u32), BridgeError>
Returns the four TTL configuration values.
§Returns
(max_instance_ttl, max_persistent_ttl, hard_ceiling, critical_threshold)
where:
max_instance_ttl— current configurable max for instance storagemax_persistent_ttl— current configurable max for persistent storagehard_ceiling—MAX_ALLOWED_TTLconstant (3 110 400 ledgers)critical_threshold—CRITICAL_ENTRY_TTL_THRESHOLD(100 000 ledgers)
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn verify_auth_entry(
env: Env,
source: Address,
nonce: u64,
valid_after_ledger: u32,
valid_before_ledger: u32,
) -> Result<(), BridgeError>
pub fn verify_auth_entry( env: Env, source: Address, nonce: u64, valid_after_ledger: u32, valid_before_ledger: u32, ) -> Result<(), BridgeError>
Validates and permanently consumes a Soroban authorization-entry nonce.
This prevents Soroban authorization-entry reuse attacks by:
- Requiring the current ledger sequence to be within
[valid_after_ledger, valid_before_ledger). - Checking that
(source, nonce)has not been used before. - Permanently marking the pair as used in persistent storage.
- Emitting
AuthUsed(source, nonce)for off-chain tracking.
The nonce is scoped to this contract’s own persistent storage, so the same numeric nonce may be used with a different contract without conflict.
§Arguments
source(Address) — The address whose authorization entry is consumed.nonce(u64) — The nonce to burn. Must not have been used before.valid_after_ledger(u32) — Inclusive lower bound on the current ledger sequence number.valid_before_ledger(u32) — Exclusive upper bound on the current ledger sequence number.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::AuthNonceExpired— Current ledger sequence is outside the[valid_after_ledger, valid_before_ledger)window.BridgeError::AuthNonceAlreadyUsed— This(source, nonce)pair has already been consumed.
§Events
("AuthUsed", source)— data:(nonce,)
§Security Considerations
The window [valid_after_ledger, valid_before_ledger) should be kept
narrow (e.g. current ledger ± a few hundred blocks) to minimise the
replay window. Once consumed, a (source, nonce) pair can never be
re-used regardless of how much time passes.
§Examples
// let nonce = bridge.query_auth_nonce(&source);
// let seq = env.ledger().sequence();
// bridge.verify_auth_entry(&source, &nonce, &seq, &(seq + 100));Sourcepub fn query_auth_nonce(env: Env, source: Address) -> u64
pub fn query_auth_nonce(env: Env, source: Address) -> u64
Returns the next unused auth nonce for source.
This is the lowest nonce value that has not yet been consumed for this
address. Callers should use this value when constructing a new
authorization entry to pass to verify_auth_entry.
§Arguments
source(Address) — The address to query.
Sourcepub fn query_auth_nonce_used(env: Env, source: Address, nonce: u64) -> bool
pub fn query_auth_nonce_used(env: Env, source: Address, nonce: u64) -> bool
Returns true if a specific auth nonce has already been consumed for source.
§Arguments
source(Address) — The address to query.nonce(u64) — The nonce to check.
Sourcepub fn query_accrued_fees(env: Env, asset: Address) -> Result<i128, BridgeError>
pub fn query_accrued_fees(env: Env, asset: Address) -> Result<i128, BridgeError>
Returns the accrued (pending, not yet withdrawn) fee balance for asset.
Accrued fees accumulate on every fund_c_address call and are
decremented when withdraw_fees is called. This value is always
≤ query_contract_balance (the contract’s actual token balance).
§Arguments
asset(Address) — The token to query.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Sourcepub fn commit_fund(
env: Env,
source: Address,
target: Address,
asset: Address,
amount_hash: BytesN<32>,
deadline: u64,
) -> Result<u64, BridgeError>
pub fn commit_fund( env: Env, source: Address, target: Address, asset: Address, amount_hash: BytesN<32>, deadline: u64, ) -> Result<u64, BridgeError>
Stores a blinded funding commitment without revealing the amount.
The caller commits to a specific (source, target, asset, amount) by
providing a domain-separated amount_hash. The actual amount stays
hidden until reveal_fund is called, preventing
front-runners from observing the value before the commitment is settled.
§Arguments
source(Address) — The account that will supply the tokens.target(Address) — The C-address that will receive the net amount.asset(Address) — Whitelisted token contract address.amount_hash(BytesN<32>) — hash of the commitment domain, contract, network, source, target, asset, amount, and nonce.deadline(u64) — Unix timestamp;reveal_fundmust be called before this time.
§Authorization
Requires source.require_auth().
§Returns
A numeric commitment ID used to reference this entry in reveal_fund
and query_commitment.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TransactionExpired—deadlineis in the past.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.
§Events
("CommitFund", source, target)— data:(id, amount_hash, asset, deadline)
Sourcepub fn reveal_fund(
env: Env,
commitment_id: u64,
source: Address,
target: Address,
asset: Address,
amount: i128,
nonce: u64,
) -> Result<(), BridgeError>
pub fn reveal_fund( env: Env, commitment_id: u64, source: Address, target: Address, asset: Address, amount: i128, nonce: u64, ) -> Result<(), BridgeError>
Executes a previously committed fund transfer after the minimum delay.
Verifies the domain-separated commitment hash before transferring tokens, ensuring the caller cannot substitute a different amount or commitment context.
§Arguments
commitment_id(u64) — ID returned bycommit_fund.source(Address) — Must match the committed source.target(Address) — Must match the committed target.asset(Address) — Must match the committed asset.amount(i128) — Actual gross amount; must satisfy the hash.nonce(u64) — Blinding nonce used when computingamount_hash.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::CommitmentNotFound— No entry forcommitment_id.BridgeError::CommitmentAlreadyRevealed— Already revealed.BridgeError::CommitmentExpired— Past the reveal deadline.BridgeError::CommitmentNotMatured— Minimum delay not yet elapsed.BridgeError::Unauthorized—source,target, orassetdo not match the commitment.BridgeError::CommitmentHashMismatch— Hash does not match.BridgeError::InvalidAmount—amount≤ 0.BridgeError::Overflow— Fee arithmetic overflowed.
§Events
("CommitRevealFunded", asset, source, target)— data:(commitment_id, amount, fee)
Sourcepub fn cancel_commitment(
env: Env,
commitment_id: u64,
) -> Result<(), BridgeError>
pub fn cancel_commitment( env: Env, commitment_id: u64, ) -> Result<(), BridgeError>
Cancels an unrevealed commitment and removes its persistent entry.
The committed source may cancel at any time, including after the reveal deadline has passed. This lets abandoned commitments be cleaned up without waiting for storage TTL expiry.
Sourcepub fn query_commitment(
env: Env,
id: u64,
) -> Result<CommitmentEntry, BridgeError>
pub fn query_commitment( env: Env, id: u64, ) -> Result<CommitmentEntry, BridgeError>
Sourcepub fn fund_c_address_with_swap(
env: Env,
source: Address,
target: Address,
source_asset: Address,
target_asset: Address,
source_amount: i128,
min_target_amount: i128,
swap_route: Vec<Address>,
nonce: Option<u64>,
deadline: Option<u64>,
) -> Result<(), BridgeError>
pub fn fund_c_address_with_swap( env: Env, source: Address, target: Address, source_asset: Address, target_asset: Address, source_amount: i128, min_target_amount: i128, swap_route: Vec<Address>, nonce: Option<u64>, deadline: Option<u64>, ) -> Result<(), BridgeError>
Fund a C-address by swapping source_asset into target_asset first.
Flow:
- Pull
source_amountofsource_assetfromsourceinto the contract. - Invoke the single whitelisted pool in
swap_routeusing the standard two-tokenswap(min_amount_out, to)interface. - Verify the increase in the bridge’s
target_assetbalance is ≥min_target_amount. - Deduct the fee (in
target_asset) and transfer the net amount totarget.
§Arguments
source— Account providingsource_asset. Must authorise.target— Destination C-address to receivetarget_asset.source_asset— Token contract the source holds (e.g. USDC).target_asset— Token contract the target should receive (e.g. XLM).source_amount— Gross amount ofsource_assetto pull from source.min_target_amount— Slippage guard: revert if the swap yields less.swap_route— Must contain exactly one DEX pool contract address, and that address must be on the swap-pool whitelist (seeadd_swap_pool). The pool must implement:swap(min_amount_out: i128, to: Address) -> i128.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—source_amountormin_target_amount≤ 0, orsource_amountis below the configured minimum.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode is on andtargetis not listed.BridgeError::AssetNotWhitelisted—source_assetortarget_assetis not whitelisted.BridgeError::MultiHopNotSupported—swap_routedoes not contain exactly one pool.BridgeError::PoolNotWhitelisted— The pool inswap_routeis not on the swap-pool whitelist.BridgeError::DailyLimitExceeded—source_amountexceeds the source’s daily limit forsource_asset.BridgeError::SwapFailed— The pool returned zero tokens out or transferred no target tokens.BridgeError::SlippageExceeded— Swap output <min_target_amount.
§Security Considerations
swap_route addresses are invoked with env.invoke_contract after
the contract has already transferred tokens to them. Without a
whitelist, a malicious or unvetted pool could keep the transferred
tokens and return a fabricated amount_out, effectively stealing
from the source. Every address in swap_route is therefore required
to be present in the admin-managed pool whitelist (add_swap_pool /
remove_swap_pool), which mirrors the existing asset whitelist
pattern. Only the admin should whitelist pools, and only after
auditing their swap implementation and token-return behavior.
Multi-hop routes are intentionally out of scope: the contract cannot
generally know which token an intermediate pool actually returns, and
assuming it equals target_asset mid-route can cause the contract to
transfer the wrong token into the next pool. swap_route is therefore
restricted to exactly one hop; longer routes return
BridgeError::MultiHopNotSupported rather than silently
miscomputing the swap.
Sourcepub fn register_meta_signer(
env: Env,
source: Address,
pubkey: BytesN<32>,
) -> Result<(), BridgeError>
pub fn register_meta_signer( env: Env, source: Address, pubkey: BytesN<32>, ) -> Result<(), BridgeError>
Binds an Ed25519 public key to source for use with execute_meta_fund.
execute_meta_fund authenticates purely via an Ed25519 signature check —
it never calls require_auth. Without this registry, verifying that
some keypair signed the payload proves nothing about whose funds are
being moved: any keypair holder could submit a validly-signed meta-tx
naming an arbitrary params.source. Calling this once (authorised by
source itself) establishes the binding that execute_meta_fund then
enforces on every subsequent call.
§Arguments
source(Address) — The account this pubkey is allowed to sign for.pubkey(BytesN<32>) — The Ed25519 public key to bind tosource.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.
§Events
("MetaSignerRegistered", source)— data:(pubkey,)
Sourcepub fn unregister_meta_signer(
env: Env,
source: Address,
) -> Result<(), BridgeError>
pub fn unregister_meta_signer( env: Env, source: Address, ) -> Result<(), BridgeError>
Removes the meta-transaction signing key bound to source.
Requires source.require_auth(). Subsequent meta-transactions for the
source are rejected until a new key is registered.
Sourcepub fn query_meta_signer(env: Env, source: Address) -> Option<BytesN<32>>
pub fn query_meta_signer(env: Env, source: Address) -> Option<BytesN<32>>
Returns the Ed25519 public key currently bound to source via
register_meta_signer, or None if no key has been registered.
§Arguments
source(Address) — The address to query.
Sourcepub fn set_meta_tx_network_id(
env: Env,
network_id: BytesN<32>,
) -> Result<(), BridgeError>
pub fn set_meta_tx_network_id( env: Env, network_id: BytesN<32>, ) -> Result<(), BridgeError>
Configures the network identifier included in every meta-transaction signature. The value should be the canonical identifier of the network where this contract is deployed.
Sourcepub fn execute_meta_fund(
env: Env,
params: MetaFundParams,
pubkey: BytesN<32>,
signature: BytesN<64>,
) -> Result<(), BridgeError>
pub fn execute_meta_fund( env: Env, params: MetaFundParams, pubkey: BytesN<32>, signature: BytesN<64>, ) -> Result<(), BridgeError>
Execute a fund_c_address on behalf of a user who signed the parameters off-chain.
Pattern (EIP-712-style adapted for Stellar / Soroban):
- The user constructs a
MetaFundParamsstruct, serialises it as:payload = sha256( "meta_fund" (8 bytes, ASCII) || network_id (32 bytes) || current_contract_address (address bytes) || source_strkey (sha256 of strkey bytes, 32 bytes) || target_strkey (sha256 of strkey bytes, 32 bytes) || asset_strkey (sha256 of strkey bytes, 32 bytes) || amount_be16 (i128 big-endian, 16 bytes) || nonce_be8 (u64 big-endian, 8 bytes) || deadline_be8 (u64 big-endian, 8 bytes) ) - The user signs
payloadwith their Ed25519 key and gives(signature, pubkey, params)to a relayer. - The relayer calls
execute_meta_fund— it verifies the signature, checks the deadline and nonce, then performs the same token-transfer flow asfund_c_address.
The source must approve this bridge contract to spend the requested
asset amount before signing the meta-transaction. This allowance lets
the bridge use transfer_from without requiring a Soroban
authorization entry from the source in the relayer’s transaction.
§Arguments
params— Funding parameters signed by the user.pubkey— The user’s Ed25519 public key (BytesN<32>). Must already be bound toparams.sourceviaregister_meta_signer.signature— Ed25519 signature over the canonical payload hash.
§Authorization
No require_auth() — authentication is via the registered Ed25519
signature, and token spending is authorized by the source’s allowance
to this bridge contract.
§Errors
BridgeError::NotInitialized— Contract not initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::MetaTxExpired—params.deadlineis in the past.BridgeError::MetaTxNonceAlreadyUsed— Nonce already consumed.BridgeError::MetaTxInvalidSignature— Signature verification failed.BridgeError::InvalidAmount—params.amount≤ 0.BridgeError::AddressBlocked—params.targetis blocked.BridgeError::AddressNotAllowlisted— Allowlist mode and target not listed.BridgeError::AssetNotWhitelisted— Asset not whitelisted.BridgeError::DailyLimitExceeded— Daily limit exceeded.BridgeError::InvalidAddress— A strkey exceedsMAX_STRKEY_LEN(64) bytes.BridgeError::MetaTxPubkeySourceMismatch—pubkeyis not the keyparams.sourceregistered viaregister_meta_signer.
§Events
("MetaFundExecuted", asset, source, target)— data:(amount, fee, nonce)
Sourcepub fn query_meta_tx_nonce_used(env: Env, source: Address, nonce: u64) -> bool
pub fn query_meta_tx_nonce_used(env: Env, source: Address, nonce: u64) -> bool
Returns true if the given meta-transaction nonce has already been used
for source.
Call this before constructing a MetaFundParams to get the next safe nonce,
or to verify a pending meta-tx has not been replayed.
§Arguments
source— The user’s Stellar address.nonce— The nonce to check.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_initialize() -> [u8; 1180]
pub const fn spec_xdr_initialize() -> [u8; 1180]
Initialises the bridge contract. Must be called exactly once before any other function.
Sets the admin, fee collector, and initial fee rate, then marks the contract as initialised and extends the instance TTL.
§Arguments
admin(Address) — Address that will hold administrative privileges. Must authorise this call.fee_collector(Address) — Address entitled to callwithdraw_fees.fee_bps(u32) — Initial fee in basis points. Must be ≤ 1 000 (10 %).nonce(Option<u64>) — Optional sequential nonce for the admin. PassNoneto skip nonce enforcement.
§Authorization
Requires admin.require_auth().
§Errors
BridgeError::AlreadyInitialized— Contract has already been initialised.BridgeError::FeeTooHigh—fee_bpsexceeds 1 000.BridgeError::DuplicateNonce—noncedoes not match the stored value.
§Events
("Initialized", admin, fee_collector)— data:(fee_bps,)
§Security Considerations
This function is the single gate that prevents double-initialisation.
The check is performed before require_auth so that the initialised flag
is always respected regardless of authorisation state. Deploy and call
initialize atomically (e.g. in the same transaction) to prevent
front-running by a third party who could set themselves as admin.
§Examples
// bridge.initialize(&admin, &fee_collector, &50u32, &None);
// assert_eq!(bridge.query_fee_bps(), 50u32);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_initialize_with_wasm_hash() -> [u8; 440]
pub const fn spec_xdr_initialize_with_wasm_hash() -> [u8; 440]
Initializes the bridge and records the hash of the deployed WASM.
Deploy tooling must pass the SHA-256 hash of the exact WASM artifact
used to create this contract. The value becomes the old_hash in the
first ContractUpgraded event.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_fund_c_address() -> [u8; 1220]
pub const fn spec_xdr_fund_c_address() -> [u8; 1220]
Funds a C-address with tokens from a source account.
Transfers amount from source into the contract, deducts the
effective fee, then forwards the net amount to target. The effective
fee is the minimum of the global fee rate, the per-asset cap, and any
volume-based tier that applies to source.
If a loyalty token has been configured, the contract mints a loyalty
reward to source after the transfer.
§Arguments
source(Address) — The account providing the tokens. Must authorise.target(Address) — The C-address receiving the net amount.asset(Address) — The whitelisted token contract address.amount(i128) — Gross amount to transfer. Must be > 0.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp (seconds) after which the call is rejected. PassNonefor no expiry.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TransactionExpired—deadlineis in the past.BridgeError::InvalidAmount—amount≤ 0.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode is on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::DailyLimitExceeded— Transfer would exceedsource’s daily limit for this asset.BridgeError::DuplicateNonce—noncemismatch.
§Events
("CAddressFunded", asset, source, target)— data:(amount, fee)
§Security Considerations
Access checks (check_access) are evaluated before require_auth so
that blocked/non-allowlisted targets are rejected without consuming the
caller’s authorization budget. The fee is floored (integer division), so
for very small amounts the effective fee may be 0.
§Examples
// Fund 500 stroops to `target` with no deadline or nonce:
// bridge.fund_c_address(&source, &target, &usdc, &500i128, &None, &None);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_batch_fund_c_address() -> [u8; 1232]
pub const fn spec_xdr_batch_fund_c_address() -> [u8; 1232]
Funds multiple C-addresses in a single transaction from one source account.
Pulls sum(amounts) from source in one token transfer, then iterates
over each (target, amount) pair. Blocked or non-allowlisted targets are
skipped (their amount is refunded to source) rather than aborting
the entire batch. A single BatchCompleted event summarises successes
and failures at the end.
Transfers to the same target address are aggregated into a single token transfer to reduce fee consumption.
§Arguments
source(Address) — The account providing all tokens. Must authorise.targets(Vec<Address>) — Ordered list of recipient C-addresses.amounts(Vec<i128>) — Gross amount for each recipient. Must be the same length astargets. Every element must be > 0.asset(Address) — The whitelisted token contract address.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp cutoff.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::BatchTooLarge—targets.len()exceedsMAX_BATCH_SIZE(100).BridgeError::TransactionExpired—deadlineis in the past.BridgeError::MismatchedArrays—targets.len() != amounts.len().BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::InvalidAmount— Any element ofamountsis ≤ 0 or below the configured minimum transfer amount.BridgeError::DailyLimitExceeded— The aggregate batch amount would exceedsource’s daily limit for this asset.BridgeError::DuplicateNonce—noncemismatch.
§Events
("CAddressFunded", asset, source, target)— Emitted per successful entry; data:(amount, fee).("BatchTransferFailed", source, target)— Emitted per skipped entry; data:(amount, "access_denied").("BatchCompleted", source)— Emitted once at the end; data:(num_success, num_failures).
§Security Considerations
The full batch total is pulled from source upfront. If any entries are
blocked, those amounts are returned to source at the end of execution.
The validation loop that checks for zero/negative amounts runs before
the initial token pull, so no tokens are moved on validation failures.
§Examples
// let targets = Vec::from_array(&env, [addr1, addr2]);
// let amounts = Vec::from_array(&env, [1000i128, 500i128]);
// bridge.batch_fund_c_address(&source, &targets, &amounts, &usdc, &None, &None);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_fee_bps() -> [u8; 1012]
pub const fn spec_xdr_set_fee_bps() -> [u8; 1012]
Updates the global fee rate in basis points.
The new rate applies to all subsequent fund_c_address and
batch_fund_c_address calls. Per-asset caps and volume tiers further
constrain the effective rate downward.
§Arguments
new_fee_bps(u32) — New fee rate. Must be ≤ 1 000 (10 %).nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::FeeTooHigh—new_fee_bpsexceeds 1 000.BridgeError::DuplicateNonce—noncemismatch.
§Events
("FeeBpsChanged", old_fee_bps, new_fee_bps)— data:(admin,)
§Examples
// bridge.set_fee_bps(&200u32, &None); // set to 2 %
// assert_eq!(bridge.query_fee_bps(), 200u32);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_source_daily_limit() -> [u8; 1152]
pub const fn spec_xdr_set_source_daily_limit() -> [u8; 1152]
Sets a maximum daily transfer limit for a specific (source, asset) pair.
Once set, any fund_c_address call from source using asset that
would push the day’s cumulative volume past limit_amount is rejected.
Set limit_amount to 0 to disable the limit entirely.
§Arguments
source(Address) — The address whose daily throughput is being capped.asset(Address) — The asset the limit applies to.limit_amount(i128) — Maximum gross tokens allowed per calendar day (UTC, measured in ledger timestamp / 86 400). Use0to disable.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Examples
// Allow user to move at most 10 000 USDC per day:
// bridge.set_source_daily_limit(&user, &usdc, &10_000i128, &None);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_source_daily_limit() -> [u8; 460]
pub const fn spec_xdr_query_source_daily_limit() -> [u8; 460]
Returns the daily transfer limit for a (source, asset) pair.
Returns 0 if no limit has been configured, meaning transfers are
unrestricted for that pair.
§Arguments
source(Address) — The address to query.asset(Address) — The asset to query.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_asset_fee_cap() -> [u8; 1032]
pub const fn spec_xdr_set_asset_fee_cap() -> [u8; 1032]
Sets a per-asset maximum fee cap in basis points.
The effective fee for asset is min(global_fee_bps, cap).
Useful for stablecoins or high-value assets where the global rate
would otherwise be too aggressive.
§Arguments
asset(Address) — The token contract whose fee is being capped.max_fee_bps(u32) — Cap in basis points. Must be ≤ 1 000.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::FeeTooHigh—max_fee_bpsexceeds 1 000.BridgeError::DuplicateNonce—noncemismatch.
§Examples
// Cap USDC fees at 0.5 % regardless of global rate:
// bridge.set_asset_fee_cap(&usdc, &50u32, &None);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_asset_fee_cap() -> [u8; 396]
pub const fn spec_xdr_query_asset_fee_cap() -> [u8; 396]
Returns the fee cap configured for asset.
Returns the contract-wide MAX_FEE_BPS (1 000) if no cap has been set,
meaning the global rate applies uncapped.
§Arguments
asset(Address) — The token contract to query.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_fee_collector() -> [u8; 836]
pub const fn spec_xdr_set_fee_collector() -> [u8; 836]
Changes the address that is entitled to call withdraw_fees.
§Arguments
new_fee_collector(Address) — Replacement fee collector.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::DuplicateNonce—noncemismatch.
§Events
("FeeCollectorChanged", old_collector, new_fee_collector)— data:(admin,)
§Examples
// bridge.set_fee_collector(&new_collector, &None);
// assert_eq!(bridge.query_fee_collector(), new_collector);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_propose_new_fee_collector() -> [u8; 372]
pub const fn spec_xdr_propose_new_fee_collector() -> [u8; 372]
Proposes a fee-collector handoff that must be accepted by new_collector.
This two-step path is preferred over set_fee_collector for normal
operations because it proves the proposed collector controls the key
before the role is moved.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_accept_fee_collector() -> [u8; 112]
pub const fn spec_xdr_accept_fee_collector() -> [u8; 112]
Accepts a pending fee-collector handoff.
Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_query_pending_fee_collector() -> [u8; 56]
Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_set_admin() -> [u8; 112]
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_propose_new_admin() -> [u8; 332]
pub const fn spec_xdr_propose_new_admin() -> [u8; 332]
Proposes an admin handoff that must be accepted by new_admin.
This two-step path is preferred over set_admin for normal operations
because it prevents accidentally transferring control to an unusable key.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_accept_admin() -> [u8; 96]
pub const fn spec_xdr_accept_admin() -> [u8; 96]
Accepts a pending admin handoff.
Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_query_pending_admin() -> [u8; 48]
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_clear_pending_admin() -> [u8; 128]
pub const fn spec_xdr_clear_pending_admin() -> [u8; 128]
Cancels a pending admin handoff.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_clear_pending_fee_collector() -> [u8; 144]
pub const fn spec_xdr_clear_pending_fee_collector() -> [u8; 144]
Cancels a pending fee-collector handoff.
Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_set_minimum_amount() -> [u8; 116]
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_minimum_amount() -> [u8; 412]
pub const fn spec_xdr_query_minimum_amount() -> [u8; 412]
Returns the configured minimum transfer amount.
Returns the value last stored by set_minimum_amount, or 0 (no
minimum) if it has never been called. The minimum is enforced by
fund_c_address, which rejects amounts below it with
BridgeError::InvalidAmount.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_withdraw_fees() -> [u8; 1156]
pub const fn spec_xdr_withdraw_fees() -> [u8; 1156]
Withdraws accrued protocol fees to the fee collector.
Transfers amount of asset from the contract to the fee collector and
decrements the on-chain accrued-fees counter.
§Arguments
asset(Address) — The token contract whose accrued fees are being withdrawn.amount(i128) — Amount to withdraw. Must be > 0 and ≤ accrued balance.nonce(Option<u64>) — Optional sequential nonce for the fee collector.
§Authorization
Requires the current fee collector’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—amount≤ 0.BridgeError::InsufficientReclaimable—amountexceeds the accrued fee balance forasset.BridgeError::DuplicateNonce—noncemismatch.
§Events
("FeesWithdrawn", fee_collector)— data:(amount, asset)
§Security Considerations
Only the fee collector may call this function. Accrued fees are tracked
separately from the contract’s token balance, so this function can never
withdraw tokens that were sent to the contract for other purposes (use
reclaim_tokens for that).
§Examples
// Withdraw all 5 accrued fee tokens:
// bridge.withdraw_fees(&usdc, &5i128, &None);Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_set_max_withdraw_per_tx() -> [u8; 120]
Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_query_max_withdraw_per_tx() -> [u8; 76]
Source§impl OnboardingBridge
impl OnboardingBridge
pub const fn spec_xdr_query_fee_bps() -> [u8; 64]
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_referral_rate() -> [u8; 900]
pub const fn spec_xdr_set_referral_rate() -> [u8; 900]
Sets the referral fee rate as a share of the protocol fee.
When fund_c_address_with_referral is called with a referrer, the
referrer receives fee × referral_rate / 10_000 of the protocol fee,
and the remainder accrues to the contract.
§Arguments
bps(u32) — Referral share in basis points relative to the fee (0–10 000). E.g.2000means the referrer gets 20 % of the fee.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::FeeTooHigh—bpsexceeds 10 000.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ReferralRateChanged", bps)— no additional data.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_referral_rate() -> [u8; 276]
pub const fn spec_xdr_query_referral_rate() -> [u8; 276]
Returns the current referral rate in basis points.
Returns 0 (no referral split) if set_referral_rate has never been called.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_fund_c_address_with_referral() -> [u8; 1256]
pub const fn spec_xdr_fund_c_address_with_referral() -> [u8; 1256]
Funds a C-address with an optional referrer that receives a share of the fee.
Behaves identically to fund_c_address except that when referrer is
Some(addr), the referral portion of the protocol fee is transferred
directly to that address immediately. The remainder accrues in the
contract as usual.
fee = floor(amount × effective_fee_bps / 10_000)
referral_fee = floor(fee × referral_rate / 10_000) (0 if referrer is None)
protocol_fee = fee − referral_fee
net = amount − fee§Arguments
source(Address) — The account providing the tokens. Must authorise.target(Address) — The C-address receivingnettokens.asset(Address) — The whitelisted token contract.amount(i128) — Gross amount. Must be > 0.referrer(Option<Address>) — Address to receive the referral cut, orNonefor no referral.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp cutoff.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—amount≤ 0.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::DailyLimitExceeded— Daily limit exceeded for(source, asset).BridgeError::InvalidReferrer—referrerissourceortarget.
§Events
("ReferralPaid", source, referrer)— Emitted only whenreferrerisSomeandreferral_fee > 0; data:(rf, asset).("CAddressFunded", asset, source, target)— data:(amount, fee).
§Security Considerations
As with the other funding entry points, callers may provide a nonce
and deadline for ordered replay protection and expiry.
§Examples
// bridge.fund_c_address_with_referral(
// &source, &target, &usdc, &1000i128, &Some(referrer), &None, &None,
// );Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_fee_collector() -> [u8; 192]
pub const fn spec_xdr_query_fee_collector() -> [u8; 192]
Returns the current fee collector address.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_admin() -> [u8; 176]
pub const fn spec_xdr_query_admin() -> [u8; 176]
Returns the current admin address.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_balance() -> [u8; 412]
pub const fn spec_xdr_query_balance() -> [u8; 412]
Returns the token balance of c_address for asset.
This is a pure read-through to the token contract; it does not require the contract to be initialised and has no access-control checks.
§Arguments
c_address(Address) — The address whose balance is queried.asset(Address) — The token contract address.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_all_balances() -> [u8; 768]
pub const fn spec_xdr_query_all_balances() -> [u8; 768]
Returns the bridge contract’s own balance for each asset in assets.
Useful for monitoring the contract’s total holdings across multiple tokens in a single call.
§Arguments
assets(Vec<Address>) — List of token contract addresses to query. Must contain at mostMAX_BATCH_SIZE(100) entries.
§Returns
A Map<Address, i128> mapping each asset address to the contract’s balance.
Assets with a zero balance are included.
§Errors
BridgeError::BatchTooLarge—assets.len()exceedsMAX_BATCH_SIZE(100).
§Examples
// let assets = Vec::from_array(&env, [usdc, xlm]);
// let balances = bridge.query_all_balances(&assets);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_contract_balance() -> [u8; 376]
pub const fn spec_xdr_query_contract_balance() -> [u8; 376]
Returns the contract’s total token balance for asset.
This includes both accrued fees and any tokens held for other purposes
(e.g. timelocked funds). Use query_accrued_fees to isolate the fee
portion.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_fee_balance() -> [u8; 152]
pub const fn spec_xdr_query_fee_balance() -> [u8; 152]
Deprecated compatibility alias for [query_contract_balance].
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_initialized() -> [u8; 96]
pub const fn spec_xdr_query_is_initialized() -> [u8; 96]
Returns true if the contract has been initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_nonce() -> [u8; 364]
pub const fn spec_xdr_query_nonce() -> [u8; 364]
Returns the current sequential nonce value for caller.
The returned value is the next nonce that must be passed to succeed if
the caller chooses to enforce nonce checking. Returns 0 for addresses
that have never used a nonce.
§Arguments
caller(Address) — The address whose nonce is queried.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_calculate_fee() -> [u8; 644]
pub const fn spec_xdr_query_calculate_fee() -> [u8; 644]
Simulates the fee and net amount for a given gross amount at the current global fee rate.
Does not account for per-asset caps or volume tiers; use this for a quick estimate only.
§Arguments
gross_amount(i128) — The hypothetical gross transfer amount.
§Returns
(fee, net) where fee = floor(gross × fee_bps / 10_000) and
net = gross − fee.
§Examples
// At fee_bps = 100 (1 %):
// let (fee, net) = bridge.query_calculate_fee(&1000i128);
// assert_eq!(fee, 10i128);
// assert_eq!(net, 990i128);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_effective_fee() -> [u8; 1168]
pub const fn spec_xdr_query_effective_fee() -> [u8; 1168]
Returns the real effective fee that would be charged for a specific
(source, asset, amount) combination, taking into account the global
fee rate, any volume-based tier applicable to source, and the
per-asset fee cap.
Unlike Self::query_calculate_fee, which only uses the flat global
fee rate, this function runs the full fee-resolution pipeline:
- Global rate — the contract-wide fee_bps.
- Volume tier — if
source's cumulative bridged volume falls within a configuredFeeTier, its rate provides an additional upper bound. - Asset cap — the per-asset maximum fee cap provides another upper bound.
The referral fee split does not affect the gross fee amount; it only determines how the fee is distributed between the fee collector and the referrer.
§Arguments
source— The address that would provide the tokens.asset— The whitelisted token contract address.amount— The hypothetical gross transfer amount.
§Returns
(effective_fee_bps, fee, net) where:
effective_fee_bpsis the resolved basis-point rate after cap + tier.fee = floor(amount × effective_fee_bps / 10_000).net = amount − fee.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::Overflow— Arithmetic overflow in fee calculation.
§Examples
// At fee_bps = 100 (1 %), no asset cap, no tiers:
// let (bps, fee, net) = bridge.query_effective_fee(&source, &usdc, &1000i128);
// assert_eq!(bps, 100u32);
// assert_eq!(fee, 10i128);
// assert_eq!(net, 990i128);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_total_bridged() -> [u8; 372]
pub const fn spec_xdr_query_total_bridged() -> [u8; 372]
Returns the cumulative net amount of asset that has been delivered to
recipients since deployment.
“Total bridged” counts only the net portion (gross minus fee), not the gross transferred by sources.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_total_fees_collected() -> [u8; 416]
pub const fn spec_xdr_query_total_fees_collected() -> [u8; 416]
Returns the cumulative gross fees collected for asset since deployment.
This counter only increases and is not decremented when fees are
withdrawn. To see the currently pending (not yet withdrawn) fee
balance, use query_accrued_fees.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_pause() -> [u8; 980]
pub const fn spec_xdr_pause() -> [u8; 980]
Pauses the contract, disabling all mutating operations.
While paused, calls to fund_c_address, batch_fund_c_address,
withdraw_fees, set_fee_bps, set_fee_collector, set_admin, and
several other state-modifying functions return
BridgeError::ContractPaused. Read-only query_* functions are
unaffected.
§Arguments
nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractPaused",)— data:(admin,)
§Security Considerations
Pausing is an emergency mechanism. It does not prevent the admin from scheduling or executing upgrades, which are intentionally not pause-gated so that an upgrade can fix whatever condition required the pause.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_unpause() -> [u8; 476]
pub const fn spec_xdr_unpause() -> [u8; 476]
Resumes normal contract operation after a pause.
§Arguments
nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractUnpaused",)— data:(admin,)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_paused() -> [u8; 92]
pub const fn spec_xdr_query_is_paused() -> [u8; 92]
Returns true if the contract is currently paused.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_upgrade() -> [u8; 1140]
pub const fn spec_xdr_upgrade() -> [u8; 1140]
Immediately upgrades the contract WASM to new_wasm_hash.
This is the untimelocked upgrade path retained for development,
testnet, and controlled break-glass operations. For production
deployments, prefer schedule_upgrade + execute_upgrade which
enforces a ~24-hour delay, giving users time to react.
§Arguments
new_wasm_hash(BytesN<32>) — The hash of the new WASM blob, which must already have been uploaded to the network viaDeployer::upload_contract_wasm.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractUpgraded",)— data:(old_hash, new_wasm_hash, admin)
§Security Considerations
After this call the contract executes new code in the same transaction.
The old_hash in the event lets off-chain monitors detect unexpected
upgrades. Consider using the timelocked path for mainnet deployments.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_schedule_upgrade() -> [u8; 1144]
pub const fn spec_xdr_schedule_upgrade() -> [u8; 1144]
Schedules a WASM upgrade that becomes executable after a ~24-hour timelock.
The upgrade is executable once
env.ledger().sequence() ≥ current_sequence + UPGRADE_TIMELOCK_LEDGERS
(17 280 ledgers at 5 s/ledger ≈ 24 hours), and remains executable for
one additional timelock-length window.
Only one pending upgrade may exist at a time. Call cancel_upgrade
first if you need to replace a pending upgrade.
§Arguments
new_wasm_hash(BytesN<32>) — Hash of the new WASM blob to apply after the timelock elapses.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Returns
The ledger sequence number at or after which execute_upgrade may be
called (executable_after_ledger).
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
§Events
("UpgradeScheduled",)— data:(new_wasm_hash, executable_after_ledger, admin)
§Security Considerations
Off-chain monitoring tools should watch for UpgradeScheduled events
and alert stakeholders so they can review the proposed WASM before the
timelock expires. Use cancel_upgrade to abort if the scheduled hash
turns out to be malicious.
§Examples
// let unlock_ledger = bridge.schedule_upgrade(&new_wasm_hash, &None);
// // wait until env.ledger().sequence() >= unlock_ledger, then:
// bridge.execute_upgrade(&new_wasm_hash, &None);Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_execute_upgrade() -> [u8; 1148]
pub const fn spec_xdr_execute_upgrade() -> [u8; 1148]
Executes a previously scheduled upgrade once its timelock has elapsed.
expected_hash must match the hash that was passed to schedule_upgrade.
This prevents a race condition where the admin could change the pending
hash between scheduling and execution by requiring the caller to commit
to the exact hash they are applying.
The pending upgrade record is cleared before calling
update_current_contract_wasm to prevent re-entrant replay.
§Arguments
expected_hash(BytesN<32>) — Must matchPendingUpgrade::new_wasm_hash.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::UpgradeNotScheduled— No pending upgrade exists.BridgeError::UpgradeHashMismatch—expected_hashdoes not match the scheduled hash.BridgeError::UpgradeTimelockActive— The timelock has not yet elapsed.BridgeError::UpgradeTimelockExpired— The execution window has elapsed.BridgeError::DuplicateNonce—noncemismatch.
§Events
("ContractUpgraded",)— data:(old_hash, new_wasm_hash, admin)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_cancel_upgrade() -> [u8; 688]
pub const fn spec_xdr_cancel_upgrade() -> [u8; 688]
Cancels a pending scheduled upgrade.
After cancellation, execute_upgrade will return
BridgeError::UpgradeNotScheduled until a new upgrade is scheduled.
§Arguments
nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::UpgradeNotScheduled— No pending upgrade to cancel.BridgeError::DuplicateNonce—noncemismatch.
§Events
("UpgradeCancelled",)— data:(cancelled_wasm_hash, admin)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_pending_upgrade() -> [u8; 232]
pub const fn spec_xdr_query_pending_upgrade() -> [u8; 232]
Returns the pending scheduled upgrade, if any.
Returns None if no upgrade has been scheduled or if a previous
upgrade has already been executed or cancelled.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_emergency_migrate() -> [u8; 564]
pub const fn spec_xdr_emergency_migrate() -> [u8; 564]
Migrates the contract state to a new contract address in case of emergency.
§Arguments
new_contract(Address) — The address of the new contract.migrate_data(bool) — If true, emits a complete snapshot of the supported configuration and accounting state as events.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_add_to_blocklist() -> [u8; 776]
pub const fn spec_xdr_add_to_blocklist() -> [u8; 776]
Adds address to the blocklist.
Blocked addresses cannot be used as target in any funding call.
Existing timelocked entries for a blocked address are not affected
retroactively; however, claim_timelocked itself is not blocked (the
recipient calls it directly). Blocking takes effect immediately for all
new funding calls.
§Arguments
address(Address) — The address to block.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_remove_from_blocklist() -> [u8; 536]
pub const fn spec_xdr_remove_from_blocklist() -> [u8; 536]
Removes address from the blocklist, restoring its ability to receive funds.
§Arguments
address(Address) — The address to unblock.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_add_to_allowlist() -> [u8; 652]
pub const fn spec_xdr_add_to_allowlist() -> [u8; 652]
Adds address to the allowlist.
Only relevant when the contract is in allowlist mode
(set_allowlist_mode(true)). In that mode, only allowlisted addresses
may be used as target in funding calls.
§Arguments
address(Address) — The address to allowlist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_remove_from_allowlist() -> [u8; 620]
pub const fn spec_xdr_remove_from_allowlist() -> [u8; 620]
Removes address from the allowlist.
If the contract is in allowlist mode, the address will no longer be able to receive funds until re-added.
§Arguments
address(Address) — The address to remove from the allowlist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_allowlist_mode() -> [u8; 772]
pub const fn spec_xdr_set_allowlist_mode() -> [u8; 772]
Enables or disables allowlist mode.
When enabled is true, only addresses that have been explicitly added
via add_to_allowlist may receive tokens. When false (the default),
any non-blocked address may receive tokens.
The blocklist is always enforced regardless of this setting.
§Arguments
enabled(bool) —trueto enable allowlist mode,falseto disable.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_blocked() -> [u8; 108]
pub const fn spec_xdr_query_is_blocked() -> [u8; 108]
Returns true if address is on the blocklist.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_allowlisted() -> [u8; 112]
pub const fn spec_xdr_query_is_allowlisted() -> [u8; 112]
Returns true if address is on the allowlist.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_allowlist_mode() -> [u8; 100]
pub const fn spec_xdr_query_allowlist_mode() -> [u8; 100]
Returns true if allowlist mode is currently enabled.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_reclaim_tokens() -> [u8; 1180]
pub const fn spec_xdr_reclaim_tokens() -> [u8; 1180]
Allows the admin to recover tokens that were accidentally sent to the contract and are not owed as fees.
While active, the reclaimable amount is
contract_token_balance − accrued_fees − locked_timelock. After an
emergency migration deactivates the contract, the full remaining
balance is reclaimable so the old contract cannot strand funds.
§Arguments
asset(Address) — The token to reclaim.amount(i128) — Amount to recover. Must be > 0 and ≤ reclaimable.destination(Address) — Address to send the recovered tokens to.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidAmount—amount≤ 0.BridgeError::InsufficientReclaimable—amountexceedscontract_balance − accrued_fees.BridgeError::DuplicateNonce—noncemismatch.
§Events
("TokensReclaimed", admin, asset)— data:(amount, destination)
§Security Considerations
While active, the check
reclaimable = balance − accrued_fees − locked_timelock ensures that
fee reserves and unclaimed TimelockEntry deposits are ring-fenced.
After emergency_migrate, recovery intentionally takes precedence over
those reservations because the old contract is permanently disabled.
Unrevealed CommitmentEntry records never hold contract balance in the
first place.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_add_asset() -> [u8; 668]
pub const fn spec_xdr_add_asset() -> [u8; 668]
Adds asset to the token whitelist.
Only whitelisted assets may be used in fund_c_address,
batch_fund_c_address, and related funding functions. Adding an asset
that is already whitelisted is idempotent.
§Arguments
asset(Address) — The token contract address to whitelist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_remove_asset() -> [u8; 696]
pub const fn spec_xdr_remove_asset() -> [u8; 696]
Removes asset from the token whitelist.
After removal, any funding call that references this asset returns
BridgeError::AssetNotWhitelisted. Existing accrued fee counters
and historical stats for the asset are retained in storage.
§Arguments
asset(Address) — The token contract address to remove.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_asset_whitelisted() -> [u8; 232]
pub const fn spec_xdr_query_is_asset_whitelisted() -> [u8; 232]
Returns true if asset is currently on the whitelist.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_whitelisted_assets() -> [u8; 600]
pub const fn spec_xdr_query_whitelisted_assets() -> [u8; 600]
Returns a page of currently whitelisted asset addresses.
The whitelist can grow without bound over the contract’s lifetime, so results are paginated rather than returned in a single unbounded call.
§Arguments
offset(u32) — Number of whitelist entries to skip.limit(u32) — Maximum number of entries to return. Capped atMAX_BATCH_SIZE(100); values above that are silently clamped.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_add_swap_pool() -> [u8; 664]
pub const fn spec_xdr_add_swap_pool() -> [u8; 664]
Adds pool to the DEX swap-pool whitelist.
Only whitelisted pool addresses may appear in the swap_route passed
to fund_c_address_with_swap. Adding a pool that is already
whitelisted is idempotent.
§Arguments
pool(Address) — The DEX pool contract address to whitelist.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_remove_swap_pool() -> [u8; 644]
pub const fn spec_xdr_remove_swap_pool() -> [u8; 644]
Removes pool from the DEX swap-pool whitelist.
After removal, any fund_c_address_with_swap call whose swap_route
references this pool returns BridgeError::PoolNotWhitelisted.
§Arguments
pool(Address) — The DEX pool contract address to remove.nonce(Option<u64>) — Optional sequential nonce for the admin.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncemismatch.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_pool_whitelisted() -> [u8; 236]
pub const fn spec_xdr_query_is_pool_whitelisted() -> [u8; 236]
Returns true if pool is currently on the swap-pool whitelist.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_loyalty_token() -> [u8; 940]
pub const fn spec_xdr_set_loyalty_token() -> [u8; 940]
Configures the loyalty token and the fixed reward minted to the source
on every successful fund_c_address call.
The contract must already hold a balance of token equal to or greater
than the rewards it intends to distribute. There is no automatic minting;
the contract transfers from its own balance.
§Arguments
token(Address) — The loyalty token contract address.amount_per_fund(i128) — Fixed amount transferred tosourceon eachfund_c_addresscall. Use0to effectively disable rewards. Must be ≥ 0.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidAmount—amount_per_fund< 0.
§Events
("LoyaltyTokenSet", admin)— data:(token, amount_per_fund)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_loyalty_token() -> [u8; 348]
pub const fn spec_xdr_query_loyalty_token() -> [u8; 348]
Returns the loyalty token address and reward amount per fund.
§Returns
(token_address, amount_per_fund).
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::LoyaltyTokenNotSet— No loyalty token has been configured.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_fee_tiers() -> [u8; 1128]
pub const fn spec_xdr_set_fee_tiers() -> [u8; 1128]
Configures volume-based fee tiers for the bridge.
Once tiers are set, the fee applied to a fund_c_address call is
determined by the source address’s cumulative bridged volume:
for each tier in tiers:
if source_volume ∈ [tier.min_volume, tier.max_volume]:
effective_fee_bps = min(global_fee_bps, tier.fee_bps)
break
else:
effective_fee_bps = global_fee_bps (fallback)The per-asset cap still applies on top of the tiered rate.
§Arguments
tiers(Vec<FeeTier>) — Ordered list of fee tiers. Must contain at mostMAX_FEE_TIERS(50) entries. Each tier’sfee_bpsmust be ≤ 1 000.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.- [
BridgeError::TooManyFeeTiers] —tiers.len()exceedsMAX_FEE_TIERS(50). BridgeError::FeeTooHigh— Any tier’sfee_bpsexceeds 1 000.
§Events
("FeeTiersSet", admin)— data:(tiers.len(),)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_fee_tiers() -> [u8; 332]
pub const fn spec_xdr_query_fee_tiers() -> [u8; 332]
Returns the configured fee tiers.
If no tiers have been set, returns a single synthetic tier covering the
full volume range [0, i128::MAX] at the current global fee rate.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_current_tier() -> [u8; 456]
pub const fn spec_xdr_query_current_tier() -> [u8; 456]
Returns the fee tier that currently applies to source, based on their
cumulative bridged volume.
If no tier matches, returns a synthetic default tier using the global fee rate, covering the full volume range.
§Arguments
source(Address) — The address to look up.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_fund_c_address_crosschain() -> [u8; 1244]
pub const fn spec_xdr_fund_c_address_crosschain() -> [u8; 1244]
Credits a C-address from a cross-chain event, verified by M-of-N relayer signatures.
This function allows off-chain relayers to bridge tokens that arrived
on another chain (e.g. Ethereum, Solana) to a Soroban C-address.
The contract must already hold a sufficient balance of asset to pay
out net_amount to the target.
§Payload Construction
Relayers must sign sha256(payload) where:
nonce = sha256(chain_id_be4 || tx_hash)
payload = chain_id_be4
|| tx_hash
|| sha256(target_strkey_bytes)
|| sha256(asset_strkey_bytes)
|| amount_be16
|| nonce§Parameters
chain_id(u32) — Numeric source-chain ID (e.g. 1 = Ethereum mainnet, 101 = Solana mainnet).tx_hash(BytesN<32>) — The 32-byte hash of the source-chain transaction.target(Address) — The Soroban C-address to credit.asset(Address) — Whitelisted token contract address.amount(i128) — Gross amount (fee is deducted before creditingtarget).sigs(Vec<RelayerSig>) — At leastthresholddistinct relayer Ed25519 signatures over the payload hash (see above).
§Authorization
No Soroban require_auth — authentication is via Ed25519 signatures from
registered relayers. The caller may be any account.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—amount≤ 0.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::ReplayedNonce— This(chain_id, tx_hash)combination has already been processed.BridgeError::NotRelayer— A signature’s pubkey is not a registered relayer.BridgeError::DuplicateRelayerSignature— The same relayer pubkey appears more than once insigs.BridgeError::BelowThreshold— Fewer thanthresholdvalid signatures.BridgeError::InvalidAddress— A strkey exceedsMAX_STRKEY_LEN(64) bytes.
§Events
("CrossChainFunded", target)— data:(chain_id, tx_hash, amount, fee, asset)
§Security Considerations
The nonce is derived deterministically from (chain_id, tx_hash) and
marked used before the token transfer, preventing replay attacks. An
invalid Ed25519 signature causes a host-level trap (panic) rather than
returning an error code, so callers should pre-validate signatures
off-chain. The contract verifies that sigs contains distinct
pubkeys — a relayer submitting the same signature twice only counts
once toward the threshold; duplicates are rejected with
BridgeError::DuplicateRelayerSignature.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_add_relayer() -> [u8; 724]
pub const fn spec_xdr_add_relayer() -> [u8; 724]
Registers an Ed25519 public key as a trusted relayer.
Registered relayers may contribute signatures to fund_c_address_crosschain.
Adding the same public key twice is idempotent.
§Arguments
pubkey(BytesN<32>) — Ed25519 public key of the relayer.nonce(Option<u64>) — Optional sequential admin nonce.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::DuplicateNonce—noncedoes not match the stored admin nonce.
§Events
("RelayerAdded",)— data:(admin, pubkey)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_remove_relayer() -> [u8; 624]
pub const fn spec_xdr_remove_relayer() -> [u8; 624]
Removes a relayer from the trusted set.
The removal is rejected if it would reduce the active relayer count below the current threshold, which would make cross-chain funding impossible.
§Arguments
pubkey(BytesN<32>) — Ed25519 public key of the relayer to remove.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::BelowThreshold— Removing this relayer would drop the count below the required threshold.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_relayer_threshold() -> [u8; 544]
pub const fn spec_xdr_set_relayer_threshold() -> [u8; 544]
Sets the minimum number of relayer signatures required to process a cross-chain funding event.
§Arguments
threshold(u32) — Must be ≤ the current number of registered relayers.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ThresholdExceedsRelayers—thresholdis greater than the number of registered relayers.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_relayer_threshold() -> [u8; 208]
pub const fn spec_xdr_query_relayer_threshold() -> [u8; 208]
Returns the current M-of-N relayer signature threshold.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_is_relayer() -> [u8; 292]
pub const fn spec_xdr_query_is_relayer() -> [u8; 292]
Returns true if pubkey is a registered relayer.
§Arguments
pubkey(BytesN<32>) — Ed25519 public key to check.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_fund_c_address_timelocked() -> [u8; 1276]
pub const fn spec_xdr_fund_c_address_timelocked() -> [u8; 1276]
Creates a time-gated funding record.
Transfers amount from source into the contract immediately. The
tokens remain locked until release_time, at which point target may
call claim_timelocked to receive the net amount (after fee deduction).
§Arguments
source(Address) — The address depositing the tokens. Must authorise.target(Address) — The address that may claim the tokens afterrelease_time.asset(Address) — The whitelisted token contract.amount(i128) — Gross amount to lock. Must be > 0.release_time(u64) — Unix timestamp (seconds) after which the tokens may be claimed. Must be strictly in the future.cliff_time(u64) — Optional cliff timestamp. If > 0 it must be ≤release_time. Currently informational only; not enforced byclaim_timelocked.nonce(Option<u64>) — Optional sequential nonce forsource.deadline(Option<u64>) — Optional Unix timestamp (seconds) after which the call is rejected. PassNonefor no expiry.
§Authorization
Requires source.require_auth().
§Returns
The numeric ID of the newly created timelock entry. Use this ID with
claim_timelocked and query_timelocked.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TransactionExpired—deadlineis in the past.BridgeError::InvalidAmount—amount≤ 0.BridgeError::InvalidReleaseTime—release_time≤ current timestamp, orcliff_time > release_time.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.BridgeError::DuplicateNonce—noncemismatch.
§Events
("TimelockCreated", source, target)— data:(id, amount, asset, release_time, cliff_time)
§Security Considerations
The fee rate applied is the rate at claim time, not deposit time.
If the global fee rate changes between deposit and claim, the net amount
received by target may differ from the amount at deposit time.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_claim_timelocked() -> [u8; 1108]
pub const fn spec_xdr_claim_timelocked() -> [u8; 1108]
Claims a matured timelock entry, releasing the net tokens to target.
The effective fee at the time of claiming is deducted from amount and
the net is transferred to target. The timelock entry is marked
claimed = true to prevent double-claims.
§Arguments
id(u64) — The timelock entry ID returned byfund_c_address_timelocked.
§Authorization
Requires target.require_auth() (the recipient of the timelock entry).
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TimelockNotFound— No entry exists forid.BridgeError::TimelockNotMatured—release_timehas not passed yet.BridgeError::Unauthorized— The entry has already been claimed.
§Events
("TimelockClaimed", target)— data:(id, net_amount, fee, asset)
§Security Considerations
The claimed flag is persisted before the token transfer. Because
Soroban execution is single-threaded within a ledger, this effectively
prevents re-entrancy. The fee rate is the current global rate at
claim time, which may differ from the rate at deposit time.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_timelocked() -> [u8; 272]
pub const fn spec_xdr_query_timelocked() -> [u8; 272]
Returns the timelock entry for id.
§Arguments
id(u64) — The timelock entry ID.
§Errors
BridgeError::TimelockNotFound— No entry exists forid.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_extend_instance_ttl() -> [u8; 668]
pub const fn spec_xdr_extend_instance_ttl() -> [u8; 668]
Extends the instance-storage TTL to ensure contract state does not expire.
ttl is capped at MAX_ALLOWED_TTL (3 110 400 ledgers, ~1 year).
The threshold used to trigger extension is ttl / 4.
§Arguments
ttl(u32) — Desired TTL in ledgers. Values belowMIN_ALLOWED_TTLare rejected; larger values are capped at the configuredMaxPersistentTtl.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
§Events
("InstanceTtlExtended",)— data:(admin, actual_ttl)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_extend_persistent_ttl() -> [u8; 840]
pub const fn spec_xdr_extend_persistent_ttl() -> [u8; 840]
Extends the persistent-storage TTL for the three per-asset counter keys
(AccruedFees, TotalBridged, TotalFeesCollected) of key_asset.
Only keys that already exist in storage are extended; missing keys are silently skipped.
§Arguments
key_asset(Address) — The asset whose persistent counters should have their TTL extended.ttl(u32) — Desired TTL in ledgers. Values belowMIN_ALLOWED_TTLare rejected; larger values are capped at the configuredMaxPersistentTtl.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
§Events
("PersistentTtlExtended",)— data:(admin, key_asset, actual_ttl)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_extend_timelock_ttl() -> [u8; 1104]
pub const fn spec_xdr_extend_timelock_ttl() -> [u8; 1104]
Extends the persistent-storage TTL for a specific timelock entry.
extend_persistent_ttl only ever covered per-asset counter keys, so a
long-dated TimelockEntry (see fund_c_address_timelocked) had no way
to have its TTL refreshed and could be archived by the network before
release_time, making it permanently unclaimable. Call this
periodically for any timelock whose release_time is far in the future.
§Arguments
id(u64) — The timelock entry ID, as returned byfund_c_address_timelocked.ttl(u32) — Desired TTL in ledgers. Values belowMIN_ALLOWED_TTLare rejected; larger values are capped at the configuredMaxPersistentTtl.
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::TimelockNotFound— No entry exists forid.
§Events
("TimelockTtlExtended",)— data:(id, actual_ttl)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_extend_commitment_ttl() -> [u8; 676]
pub const fn spec_xdr_extend_commitment_ttl() -> [u8; 676]
Extends the persistent-storage TTL for a specific commit-reveal entry.
§Arguments
id(u64) — The commitment ID, as returned bycommit_fund.ttl(u32) — Desired TTL in ledgers (capped atMAX_ALLOWED_TTL).
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::CommitmentNotFound— No entry exists forid.
§Events
("CommitmentTtlExtended",)— data:(id, actual_ttl)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_extend_relayer_ttl() -> [u8; 676]
pub const fn spec_xdr_extend_relayer_ttl() -> [u8; 676]
Extends the persistent-storage TTL for a registered relayer’s entry.
§Arguments
pubkey(BytesN<32>) — The relayer’s Ed25519 public key.ttl(u32) — Desired TTL in ledgers (capped atMAX_ALLOWED_TTL).
§Authorization
No authorization is required because this operation only pays rent and does not change contract state.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::NotRelayer—pubkeyis not a registered relayer.
§Events
("RelayerTtlExtended",)— data:(pubkey, actual_ttl)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_extend_source_persistent_ttl() -> [u8; 1028]
pub const fn spec_xdr_extend_source_persistent_ttl() -> [u8; 1028]
Extends the persistent-storage TTL for all per-(source, asset) and
per-source keys used by the core funding paths: SourceDailyLimit,
the current day’s DailyUsage, UserDeposit, SourceBridgedVolume,
the sequential Nonce, and the auth-entry AuthNonce counter.
Only keys that already exist in storage are extended; missing keys are
silently skipped, mirroring extend_persistent_ttl.
§Arguments
source(Address) — The address whose per-source entries should have their TTL extended.asset(Address) — The asset half of the(source, asset)keys.ttl(u32) — Desired TTL in ledgers (capped atMAX_ALLOWED_TTL).
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
§Events
("SourcePersistentTtlExtended",)— data:(admin, source, asset, actual_ttl)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_max_instance_ttl() -> [u8; 860]
pub const fn spec_xdr_set_max_instance_ttl() -> [u8; 860]
Overrides the maximum instance-storage TTL used by the internal
extend_instance_ttl helper called on every mutating operation.
Values above MAX_ALLOWED_TTL are silently capped. Values below
MIN_ALLOWED_TTL are rejected outright: extend_instance_ttl derives
its extension threshold as max_ttl / 4, so a max of 0 (or any very
small value) would make the threshold 0 too, effectively disabling
TTL extension and risking near-term expiry of all instance data.
§Arguments
ttl(u32) — New maximum in ledgers. Must be ≥MIN_ALLOWED_TTL.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidTtl—ttlis belowMIN_ALLOWED_TTL.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_max_persistent_ttl() -> [u8; 716]
pub const fn spec_xdr_set_max_persistent_ttl() -> [u8; 716]
Overrides the maximum persistent-storage TTL used by extend_persistent_ttl.
Values above MAX_ALLOWED_TTL are silently capped. Values below
MIN_ALLOWED_TTL are rejected for the same reason as
set_max_instance_ttl: a near-zero max would collapse the extension
threshold to zero and disable TTL extension entirely.
§Arguments
ttl(u32) — New maximum in ledgers. Must be ≥MIN_ALLOWED_TTL.
§Authorization
Requires the current admin’s require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::InvalidTtl—ttlis belowMIN_ALLOWED_TTL.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_ttl_config() -> [u8; 592]
pub const fn spec_xdr_query_ttl_config() -> [u8; 592]
Returns the four TTL configuration values.
§Returns
(max_instance_ttl, max_persistent_ttl, hard_ceiling, critical_threshold)
where:
max_instance_ttl— current configurable max for instance storagemax_persistent_ttl— current configurable max for persistent storagehard_ceiling—MAX_ALLOWED_TTLconstant (3 110 400 ledgers)critical_threshold—CRITICAL_ENTRY_TTL_THRESHOLD(100 000 ledgers)
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_verify_auth_entry() -> [u8; 1200]
pub const fn spec_xdr_verify_auth_entry() -> [u8; 1200]
Validates and permanently consumes a Soroban authorization-entry nonce.
This prevents Soroban authorization-entry reuse attacks by:
- Requiring the current ledger sequence to be within
[valid_after_ledger, valid_before_ledger). - Checking that
(source, nonce)has not been used before. - Permanently marking the pair as used in persistent storage.
- Emitting
AuthUsed(source, nonce)for off-chain tracking.
The nonce is scoped to this contract’s own persistent storage, so the same numeric nonce may be used with a different contract without conflict.
§Arguments
source(Address) — The address whose authorization entry is consumed.nonce(u64) — The nonce to burn. Must not have been used before.valid_after_ledger(u32) — Inclusive lower bound on the current ledger sequence number.valid_before_ledger(u32) — Exclusive upper bound on the current ledger sequence number.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::AuthNonceExpired— Current ledger sequence is outside the[valid_after_ledger, valid_before_ledger)window.BridgeError::AuthNonceAlreadyUsed— This(source, nonce)pair has already been consumed.
§Events
("AuthUsed", source)— data:(nonce,)
§Security Considerations
The window [valid_after_ledger, valid_before_ledger) should be kept
narrow (e.g. current ledger ± a few hundred blocks) to minimise the
replay window. Once consumed, a (source, nonce) pair can never be
re-used regardless of how much time passes.
§Examples
// let nonce = bridge.query_auth_nonce(&source);
// let seq = env.ledger().sequence();
// bridge.verify_auth_entry(&source, &nonce, &seq, &(seq + 100));Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_auth_nonce() -> [u8; 360]
pub const fn spec_xdr_query_auth_nonce() -> [u8; 360]
Returns the next unused auth nonce for source.
This is the lowest nonce value that has not yet been consumed for this
address. Callers should use this value when constructing a new
authorization entry to pass to verify_auth_entry.
§Arguments
source(Address) — The address to query.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_auth_nonce_used() -> [u8; 272]
pub const fn spec_xdr_query_auth_nonce_used() -> [u8; 272]
Returns true if a specific auth nonce has already been consumed for source.
§Arguments
source(Address) — The address to query.nonce(u64) — The nonce to check.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_accrued_fees() -> [u8; 500]
pub const fn spec_xdr_query_accrued_fees() -> [u8; 500]
Returns the accrued (pending, not yet withdrawn) fee balance for asset.
Accrued fees accumulate on every fund_c_address call and are
decremented when withdraw_fees is called. This value is always
≤ query_contract_balance (the contract’s actual token balance).
§Arguments
asset(Address) — The token to query.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_commit_fund() -> [u8; 1192]
pub const fn spec_xdr_commit_fund() -> [u8; 1192]
Stores a blinded funding commitment without revealing the amount.
The caller commits to a specific (source, target, asset, amount) by
providing a domain-separated amount_hash. The actual amount stays
hidden until reveal_fund is called, preventing
front-runners from observing the value before the commitment is settled.
§Arguments
source(Address) — The account that will supply the tokens.target(Address) — The C-address that will receive the net amount.asset(Address) — Whitelisted token contract address.amount_hash(BytesN<32>) — hash of the commitment domain, contract, network, source, target, asset, amount, and nonce.deadline(u64) — Unix timestamp;reveal_fundmust be called before this time.
§Authorization
Requires source.require_auth().
§Returns
A numeric commitment ID used to reference this entry in reveal_fund
and query_commitment.
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::TransactionExpired—deadlineis in the past.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode on andtargetis not allowlisted.BridgeError::AssetNotWhitelisted—assethas not been added.
§Events
("CommitFund", source, target)— data:(id, amount_hash, asset, deadline)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_reveal_fund() -> [u8; 1216]
pub const fn spec_xdr_reveal_fund() -> [u8; 1216]
Executes a previously committed fund transfer after the minimum delay.
Verifies the domain-separated commitment hash before transferring tokens, ensuring the caller cannot substitute a different amount or commitment context.
§Arguments
commitment_id(u64) — ID returned bycommit_fund.source(Address) — Must match the committed source.target(Address) — Must match the committed target.asset(Address) — Must match the committed asset.amount(i128) — Actual gross amount; must satisfy the hash.nonce(u64) — Blinding nonce used when computingamount_hash.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::CommitmentNotFound— No entry forcommitment_id.BridgeError::CommitmentAlreadyRevealed— Already revealed.BridgeError::CommitmentExpired— Past the reveal deadline.BridgeError::CommitmentNotMatured— Minimum delay not yet elapsed.BridgeError::Unauthorized—source,target, orassetdo not match the commitment.BridgeError::CommitmentHashMismatch— Hash does not match.BridgeError::InvalidAmount—amount≤ 0.BridgeError::Overflow— Fee arithmetic overflowed.
§Events
("CommitRevealFunded", asset, source, target)— data:(commitment_id, amount, fee)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_cancel_commitment() -> [u8; 348]
pub const fn spec_xdr_cancel_commitment() -> [u8; 348]
Cancels an unrevealed commitment and removes its persistent entry.
The committed source may cancel at any time, including after the reveal deadline has passed. This lets abandoned commitments be cleaned up without waiting for storage TTL expiry.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_commitment() -> [u8; 208]
pub const fn spec_xdr_query_commitment() -> [u8; 208]
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_fund_c_address_with_swap() -> [u8; 1324]
pub const fn spec_xdr_fund_c_address_with_swap() -> [u8; 1324]
Fund a C-address by swapping source_asset into target_asset first.
Flow:
- Pull
source_amountofsource_assetfromsourceinto the contract. - Invoke the single whitelisted pool in
swap_routeusing the standard two-tokenswap(min_amount_out, to)interface. - Verify the increase in the bridge’s
target_assetbalance is ≥min_target_amount. - Deduct the fee (in
target_asset) and transfer the net amount totarget.
§Arguments
source— Account providingsource_asset. Must authorise.target— Destination C-address to receivetarget_asset.source_asset— Token contract the source holds (e.g. USDC).target_asset— Token contract the target should receive (e.g. XLM).source_amount— Gross amount ofsource_assetto pull from source.min_target_amount— Slippage guard: revert if the swap yields less.swap_route— Must contain exactly one DEX pool contract address, and that address must be on the swap-pool whitelist (seeadd_swap_pool). The pool must implement:swap(min_amount_out: i128, to: Address) -> i128.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::InvalidAmount—source_amountormin_target_amount≤ 0, orsource_amountis below the configured minimum.BridgeError::AddressBlocked—targetis on the blocklist.BridgeError::AddressNotAllowlisted— Allowlist mode is on andtargetis not listed.BridgeError::AssetNotWhitelisted—source_assetortarget_assetis not whitelisted.BridgeError::MultiHopNotSupported—swap_routedoes not contain exactly one pool.BridgeError::PoolNotWhitelisted— The pool inswap_routeis not on the swap-pool whitelist.BridgeError::DailyLimitExceeded—source_amountexceeds the source’s daily limit forsource_asset.BridgeError::SwapFailed— The pool returned zero tokens out or transferred no target tokens.BridgeError::SlippageExceeded— Swap output <min_target_amount.
§Security Considerations
swap_route addresses are invoked with env.invoke_contract after
the contract has already transferred tokens to them. Without a
whitelist, a malicious or unvetted pool could keep the transferred
tokens and return a fabricated amount_out, effectively stealing
from the source. Every address in swap_route is therefore required
to be present in the admin-managed pool whitelist (add_swap_pool /
remove_swap_pool), which mirrors the existing asset whitelist
pattern. Only the admin should whitelist pools, and only after
auditing their swap implementation and token-return behavior.
Multi-hop routes are intentionally out of scope: the contract cannot
generally know which token an intermediate pool actually returns, and
assuming it equals target_asset mid-route can cause the contract to
transfer the wrong token into the next pool. swap_route is therefore
restricted to exactly one hop; longer routes return
BridgeError::MultiHopNotSupported rather than silently
miscomputing the swap.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_register_meta_signer() -> [u8; 1076]
pub const fn spec_xdr_register_meta_signer() -> [u8; 1076]
Binds an Ed25519 public key to source for use with execute_meta_fund.
execute_meta_fund authenticates purely via an Ed25519 signature check —
it never calls require_auth. Without this registry, verifying that
some keypair signed the payload proves nothing about whose funds are
being moved: any keypair holder could submit a validly-signed meta-tx
naming an arbitrary params.source. Calling this once (authorised by
source itself) establishes the binding that execute_meta_fund then
enforces on every subsequent call.
§Arguments
source(Address) — The account this pubkey is allowed to sign for.pubkey(BytesN<32>) — The Ed25519 public key to bind tosource.
§Authorization
Requires source.require_auth().
§Errors
BridgeError::NotInitialized— Contract not yet initialised.BridgeError::ContractPaused— Contract is paused.
§Events
("MetaSignerRegistered", source)— data:(pubkey,)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_unregister_meta_signer() -> [u8; 280]
pub const fn spec_xdr_unregister_meta_signer() -> [u8; 280]
Removes the meta-transaction signing key bound to source.
Requires source.require_auth(). Subsequent meta-transactions for the
source are rejected until a new key is registered.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_meta_signer() -> [u8; 264]
pub const fn spec_xdr_query_meta_signer() -> [u8; 264]
Returns the Ed25519 public key currently bound to source via
register_meta_signer, or None if no key has been registered.
§Arguments
source(Address) — The address to query.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_set_meta_tx_network_id() -> [u8; 276]
pub const fn spec_xdr_set_meta_tx_network_id() -> [u8; 276]
Configures the network identifier included in every meta-transaction signature. The value should be the canonical identifier of the network where this contract is deployed.
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_execute_meta_fund() -> [u8; 1188]
pub const fn spec_xdr_execute_meta_fund() -> [u8; 1188]
Execute a fund_c_address on behalf of a user who signed the parameters off-chain.
Pattern (EIP-712-style adapted for Stellar / Soroban):
- The user constructs a
MetaFundParamsstruct, serialises it as:payload = sha256( "meta_fund" (8 bytes, ASCII) || network_id (32 bytes) || current_contract_address (address bytes) || source_strkey (sha256 of strkey bytes, 32 bytes) || target_strkey (sha256 of strkey bytes, 32 bytes) || asset_strkey (sha256 of strkey bytes, 32 bytes) || amount_be16 (i128 big-endian, 16 bytes) || nonce_be8 (u64 big-endian, 8 bytes) || deadline_be8 (u64 big-endian, 8 bytes) ) - The user signs
payloadwith their Ed25519 key and gives(signature, pubkey, params)to a relayer. - The relayer calls
execute_meta_fund— it verifies the signature, checks the deadline and nonce, then performs the same token-transfer flow asfund_c_address.
The source must approve this bridge contract to spend the requested
asset amount before signing the meta-transaction. This allowance lets
the bridge use transfer_from without requiring a Soroban
authorization entry from the source in the relayer’s transaction.
§Arguments
params— Funding parameters signed by the user.pubkey— The user’s Ed25519 public key (BytesN<32>). Must already be bound toparams.sourceviaregister_meta_signer.signature— Ed25519 signature over the canonical payload hash.
§Authorization
No require_auth() — authentication is via the registered Ed25519
signature, and token spending is authorized by the source’s allowance
to this bridge contract.
§Errors
BridgeError::NotInitialized— Contract not initialised.BridgeError::ContractPaused— Contract is paused.BridgeError::MetaTxExpired—params.deadlineis in the past.BridgeError::MetaTxNonceAlreadyUsed— Nonce already consumed.BridgeError::MetaTxInvalidSignature— Signature verification failed.BridgeError::InvalidAmount—params.amount≤ 0.BridgeError::AddressBlocked—params.targetis blocked.BridgeError::AddressNotAllowlisted— Allowlist mode and target not listed.BridgeError::AssetNotWhitelisted— Asset not whitelisted.BridgeError::DailyLimitExceeded— Daily limit exceeded.BridgeError::InvalidAddress— A strkey exceedsMAX_STRKEY_LEN(64) bytes.BridgeError::MetaTxPubkeySourceMismatch—pubkeyis not the keyparams.sourceregistered viaregister_meta_signer.
§Events
("MetaFundExecuted", asset, source, target)— data:(amount, fee, nonce)
Source§impl OnboardingBridge
impl OnboardingBridge
Sourcepub const fn spec_xdr_query_meta_tx_nonce_used() -> [u8; 400]
pub const fn spec_xdr_query_meta_tx_nonce_used() -> [u8; 400]
Returns true if the given meta-transaction nonce has already been used
for source.
Call this before constructing a MetaFundParams to get the next safe nonce,
or to verify a pending meta-tx has not been replayed.
§Arguments
source— The user’s Stellar address.nonce— The nonce to check.
Auto Trait Implementations§
impl Freeze for OnboardingBridge
impl RefUnwindSafe for OnboardingBridge
impl Send for OnboardingBridge
impl Sync for OnboardingBridge
impl Unpin for OnboardingBridge
impl UnsafeUnpin for OnboardingBridge
impl UnwindSafe for OnboardingBridge
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T, U, V, E, C> Compare<(T, U, V)> for Cwhere
C: Compare<T, Error = E, Error = E, Error = E> + Compare<U> + Compare<V>,
impl<T, U, V, E, C> Compare<(T, U, V)> for Cwhere
C: Compare<T, Error = E, Error = E, Error = E> + Compare<U> + Compare<V>,
§impl<T, U, V, W, E, C> Compare<(T, U, V, W)> for Cwhere
C: Compare<T, Error = E, Error = E, Error = E, Error = E> + Compare<U> + Compare<V> + Compare<W>,
impl<T, U, V, W, E, C> Compare<(T, U, V, W)> for Cwhere
C: Compare<T, Error = E, Error = E, Error = E, Error = E> + Compare<U> + Compare<V> + Compare<W>,
type Error = E
fn compare( &self, a: &(T, U, V, W), b: &(T, U, V, W), ) -> Result<Ordering, <C as Compare<(T, U, V, W)>>::Error>
§impl<T, U, V, W, X, E, C> Compare<(T, U, V, W, X)> for Cwhere
C: Compare<T, Error = E, Error = E, Error = E, Error = E, Error = E> + Compare<U> + Compare<V> + Compare<W> + Compare<X>,
impl<T, U, V, W, X, E, C> Compare<(T, U, V, W, X)> for Cwhere
C: Compare<T, Error = E, Error = E, Error = E, Error = E, Error = E> + Compare<U> + Compare<V> + Compare<W> + Compare<X>,
type Error = E
fn compare( &self, a: &(T, U, V, W, X), b: &(T, U, V, W, X), ) -> Result<Ordering, <C as Compare<(T, U, V, W, X)>>::Error>
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more