Skip to main content

Cross-Contract ABI Stability Matrix

Canonical reference for the public surface area of all five Grainlify Soroban contracts. Every integrator, facade binding author, and contract upgrader must consult this document before changing a public function signature, adding or removing a struct field, or reordering enum variants.

Last updated: 2026-07-27
Covers contracts at commit referenced in contracts/VERSIONS.md.


Table of Contents

  1. Stability Classifications
  2. Breaking vs Additive Changes
  3. Synchronization-Risk Types
  4. Contract: program-escrow
  5. Contract: bounty-escrow
  6. Contract: grainlify-core
  7. Contract: view-facade
  8. Contract: escrow-view-facade
  9. Cross-Contract Dependency Graph
  10. Upgrade Checklist

1. Stability Classifications

Each public entry-point and exported type carries one of three labels:

LabelMeaning
STABLEFrozen interface. Breaking changes require a semver-major bump, a migration path, and updates to all facade bindings that mirror the type. Announce changes at least one release cycle in advance.
EVOLVINGUnder active development. Signature may change across minor releases. Downstream callers should pin to a specific contract hash and re-test on upgrade.
INTERNALExposed pub for Rust crate-level visibility or test harnesses only. Not part of the on-chain ABI from an integrator's perspective; may change without notice.

2. Breaking vs Additive Changes

2.1 Breaking Changes (require coordinated update across all affected bindings)

  • Removing a pub fn entrypoint
  • Renaming a pub fn entrypoint
  • Changing parameter types, order, or count of any pub fn
  • Changing the return type of any pub fn
  • Removing a field from a #[contracttype] struct
  • Reordering fields in a #[contracttype] struct (XDR serialization is field-order-dependent)
  • Removing a variant from a #[contracttype] enum
  • Reordering variants in a #[contracttype] enum
  • Changing a field type in a #[contracttype] struct

2.2 Additive Changes (non-breaking, but require facade-binding review)

  • Adding a new pub fn entrypoint
  • Adding a new #[contracttype] struct or enum (new storage key is required)
  • Appending a new field to a #[contracttype] struct with a corresponding schema-version bump so old and new storage layouts can coexist
  • Appending a new variant to a #[contracttype] enum (only safe if all match arms in all callers use a wildcard/catch-all)
  • Adding a new optional Option<T> field to a struct that is versioned

2.3 Facade Binding Synchronization Rule

Any type listed in §3 Synchronization-Risk Types that changes in its canonical contract must be updated in every facade binding file that copies it in the same PR. Partial updates will cause silent XDR decode errors at runtime — there is no compile-time guard between contracts on Soroban.


3. Synchronization-Risk Types

These types are defined in a canonical contract and duplicated verbatim in one or more facade binding files. Any field addition, removal, reorder, or type change in the canonical definition is a breaking synchronization risk.

TypeCanonical SourceDuplicated InRisk Level
PayoutRecordprogram-escrow/src/lib.rsview-facade/src/lib.rs (local copy)🔴 HIGH — different field sets; view-facade omits payout_type
ProgramDelegateInfoprogram-escrow/src/lib.rsescrow-view-facade/src/program_escrow_bindings.rs🔴 HIGH — exact mirror; field reorder will silently corrupt
EscrowStatus (enum)bounty-escrow/contracts/escrow/src/lib.rsescrow-view-facade/src/bounty_escrow_bindings.rs, escrow-view-facade/src/lib.rs (re-exported as local EscrowStatus)🔴 HIGH — variant order must match exactly
EscrowMetadatabounty-escrow/contracts/escrow/src/lib.rsescrow-view-facade/src/bounty_escrow_bindings.rs🔴 HIGH — binding uses subset of fields
PauseFlagsbounty-escrow/contracts/escrow/src/lib.rsescrow-view-facade/src/bounty_escrow_bindings.rs🔴 HIGH — binding copies all fields; pause_reason is Option<String>
Escrowbounty-escrow/contracts/escrow/src/lib.rsescrow-view-facade/src/bounty_escrow_bindings.rs🟡 MEDIUM — binding omits optional fields present in canonical
ProgramMetadata / ProgramMetadataFieldprogram-escrow/src/lib.rsNot yet in any binding — risk emerges if facades add metadata queries🟢 LOW (today) — becomes HIGH if view-facade adds metadata endpoint

3.1 Detailed Field Diff: PayoutRecord

Canonical (program-escrow) view-facade local copy
───────────────────────────── ───────────────────────
pub recipient: Address pub recipient: Address ✓
pub amount: i128 pub amount: i128 ✓
pub timestamp: u64 pub timestamp: u64 ✓
pub payout_type: PayoutType (MISSING) ⚠ DRIFT

The view-facade's PayoutRecord is a subset of the canonical struct. Any consumer receiving the facade's PayoutRecord will not have payout_type. If the canonical struct reorders or removes timestamp, XDR decoding in the facade silently produces wrong values.

3.2 Detailed Field Diff: EscrowStatus

Canonical (bounty-escrow) bounty_escrow_bindings.rs escrow-view-facade local
───────────────────────── ───────────────────────── ────────────────────────
Locked = 0 Locked = 0 Locked = 0
Released = 1 Released = 1 Released = 1
Refunded = 2 Refunded = 2 Refunded = 2
PartiallyRefunded = 3 PartiallyRefunded = 3 PartiallyRefunded = 3

Currently in sync. Adding a new variant to canonical (e.g. Disputed) is an additive change only if the facade match arms include a catch-all. Currently they do not — they exhaustively match all four variants. Adding a fifth variant to the canonical enum is therefore breaking for both facade copies until they are updated simultaneously.

3.3 Detailed Field Diff: ProgramDelegateInfo

Canonical (program-escrow) program_escrow_bindings.rs
───────────────────────── ──────────────────────────
pub program_id: String pub program_id: String ✓
pub delegate: Option<Address> pub delegate: Option<Address> ✓
pub permissions: u32 pub permissions: u32 ✓

Currently in sync. Any field addition to the canonical struct must be mirrored in the binding within the same PR, or the query_all_delegates call will return garbage for the new field.


4. Contract: program-escrow

Crate: contracts/program-escrow
Contract struct: ProgramEscrowContract
Purpose: Manages hackathon and grant prize pools — fund locking, batch/single payouts, release schedules, and delegate authorization.

4.1 Key Exported Types

TypeStabilityNotes
ProgramDataSTABLECore program state; field reorder is breaking. circuit_breaker_threshold field has an unresolved merge conflict between Option<u8> and Option<u32> — must be resolved before next deployment.
PayoutRecordSTABLEMirrored in view-facade (with drift — see §3.1).
ProgramMetadata / ProgramMetadataFieldEVOLVINGcustom_fields vector; length now capped at MAX_CUSTOM_FIELDS=20.
ProgramDelegateInfoSTABLEMirrored in escrow-view-facade binding (see §3.3).
PauseFlagsSTABLEAll four fields are load-bearing; pause_reason is Option<String>.
ProgramStatus (enum)STABLEDraft → Active → Drained; variant removal is breaking.
FeeConfigEVOLVINGFee configuration; fee_waivers bitmask may gain new bits.
RateLimitConfigINTERNALGlobal rate-limit for anti-abuse; not exposed to downstream integrators.
DelegateMetaRateLimitStateINTERNALPer-program rolling window counter; opaque to callers.
SplitConfig / BeneficiarySplitEVOLVINGPayout-split configuration; schema version tracked.
BatchReceiptEVOLVINGIdempotency receipts; schema version tracked.
ClaimRecord / ClaimStatusEVOLVINGPending-claim workflow; under active development.
DisputeRecord / DisputeStateEVOLVINGDispute resolution; single active dispute per contract.
AllowedTokenEntryEVOLVINGToken allowlist with decimals; V2 schema.
ProgramReleaseSchedule / ProgramReleaseHistorySTABLERelease schedule system; adding fields requires schema bump.
AnonymousResolverEVOLVINGPrivacy feature; may gain additional fields.

4.2 Public Entry-Points

Initialization & Program Lifecycle

FunctionSignatureStabilityNotes
initialize_contract(env: Env, admin: Address)STABLEOne-time init; idempotent guard.
init_program(env, program_id, authorized_payout_key, token_address, creator, initial_liquidity, reference_hash) -> ProgramDataSTABLECreates a program in Draft state.
init_program_with_metadata(env, program_id, authorized_payout_key, token_address, organizer, metadata) -> ProgramDataSTABLEInit with optional metadata.
initialize_program(env, ...) -> ProgramDataINTERNALAlias/internal variant; do not rely on externally.
batch_initialize_programs(env, items: Vec<...>) -> Result<u32, BatchError>EVOLVINGBatch init; error semantics may change.
publish_program(env, program_id, caller) -> ProgramDataSTABLETransitions Draft → Active.
lock_program_funds(env, amount: i128) -> ProgramDataSTABLELegacy single-program lock.
lock_program_funds_v2(env, program_id, amount) -> ProgramDataSTABLEMulti-program lock; preferred path.
batch_lock(env, items: Vec<LockItem>) -> Result<u32, BatchError>EVOLVINGBatch lock; may gain idempotency key.
archive_program(env, program_id)STABLEAdmin-only soft delete.
get_archived_programs(env) -> Vec<String>STABLEReturns archived program IDs.

Payout Operations

FunctionSignatureStabilityNotes
single_payout(env, program_id, recipient, amount) -> ProgramDataSTABLESingle transfer; circuit-breaker checked.
single_payout_by(env, program_id, caller, recipient, amount) -> ProgramDataSTABLECaller-explicit variant.
single_payout_v2(env, program_id, caller, recipient, amount, ...) -> ProgramDataEVOLVINGV2 with extended options.
single_payout_idempotent(env, program_id, recipient, amount, idempotency_key) -> ProgramDataEVOLVINGIdempotency-keyed single payout.
single_payout_idempotent_by(env, ..., caller, ...) -> ProgramDataEVOLVINGCaller-explicit idempotent.
batch_payout(env, recipients: Vec<Address>, amounts: Vec<i128>) -> ProgramDataSTABLELegacy batch; circuit-breaker checked.
batch_payout_by(env, program_id, caller, recipients, amounts) -> ProgramDataSTABLEMulti-program batch.
batch_payout_v2(env, ...) -> ProgramDataEVOLVINGV2 batch with extended options.
batch_payout_idempotent(env, ..., idempotency_key) -> ProgramDataEVOLVINGIdempotency-keyed batch.
batch_payout_idempotent_by(env, ...) -> ProgramDataEVOLVINGCaller-explicit idempotent batch.
batch_payout_with_receipt(env, ...) -> ProgramDataEVOLVINGReturns receipt ID.
batch_release(env, items: Vec<ReleaseItem>) -> Result<u32, BatchError>EVOLVINGBatch release variant.
execute_split_payout(env, program_id, caller, amount) -> SplitPayoutResultEVOLVINGSplits amount per SplitConfig.
preview_split(env, program_id, amount) -> SplitPayoutResultEVOLVINGDry-run; no state change.

Release Schedules

FunctionSignatureStabilityNotes
create_program_release_schedule(env, program_id, recipient, amount, release_at)STABLECreates a time-locked schedule.
create_prog_release_schedule_by(env, program_id, caller, ...)STABLECaller-explicit variant.
trigger_program_releases(env) -> u32STABLEProcesses all due schedules.
trigger_program_releases_by(env, caller) -> u32STABLECaller-explicit trigger.
get_program_release_schedules(env) -> Vec<ProgramReleaseSchedule>STABLELegacy single-program query.
get_release_schedules(env) -> Vec<ProgramReleaseSchedule>STABLEAlias.
get_program_release_history(env) -> Vec<ProgramReleaseHistory>STABLEHistory of triggered releases.

Delegate & Access Control

FunctionSignatureStabilityNotes
set_program_delegate(env, program_id, caller, delegate, permissions: u32) -> ProgramDataSTABLESets delegate with permission bitmask.
revoke_program_delegate(env, program_id, caller) -> ProgramDataSTABLEClears delegate.
emergency_revoke_delegate(env, program_id) -> ProgramDataSTABLEAdmin emergency revoke.
propose_controller(env, program_id, caller, proposed) -> Result<ProgramData, ContractError>EVOLVINGTwo-step controller rotation step 1.
accept_controller(env, program_id) -> Result<ProgramData, ContractError>EVOLVINGTwo-step controller rotation step 2.
cancel_controller_rotation(env, program_id, caller) -> Result<ProgramData, ContractError>EVOLVINGCancel pending rotation.
rotate_payout_key(env, program_id, caller, new_key, nonce) -> ProgramDataEVOLVINGNonce-protected key rotation.
get_rotation_nonce(env, program_id) -> u64STABLERead rotation nonce.
propose_admin(env, proposed_admin) -> Result<(), ContractError>STABLETwo-step admin rotation step 1.
accept_admin(env) -> Result<(), ContractError>STABLETwo-step admin rotation step 2.
cancel_admin_rotation(env) -> Result<(), ContractError>STABLECancel pending admin rotation.

Metadata & Risk

FunctionSignatureStabilityNotes
update_program_metadata(env, program_id, caller, metadata: ProgramMetadata) -> ProgramDataSTABLEDelegate-invoked calls are rate-limited (≤10/hr). custom_fields capped at 20 entries.
update_program_metadata_by(env, program_id, caller, metadata) -> ProgramDataSTABLEAlias; same rate-limit applies.
get_program_metadata(env, program_id) -> Option<ProgramMetadata>STABLERead metadata.
set_program_risk_flags(env, program_id, flags: u32) -> ProgramDataSTABLEAdmin-only; bitmask.
clear_program_risk_flags(env, program_id, flags: u32) -> ProgramDataSTABLEAdmin-only; clears specified bits.

Pause, Emergency & Maintenance

FunctionSignatureStabilityNotes
set_paused(env, lock_paused, release_paused, refund_paused, reason) -> PauseFlagsSTABLEGranular pause flags.
emergency_withdraw(env, target: Address)STABLEAdmin-only; requires lock_paused=true.
set_maintenance_mode(env, enabled: bool)STABLEBlocks lock only.
is_maintenance_mode(env) -> boolSTABLERead maintenance flag.
set_read_only_mode(env, enabled, reason)STABLEBlocks all writes.
is_read_only(env) -> boolSTABLE

Fee Configuration

FunctionSignatureStabilityNotes
get_fee_config(env) -> FeeConfigSTABLE
update_fee_config(env, ...)STABLEAdmin-only.
set_program_spending_limit(env, program_id, ...)EVOLVINGPer-program spend cap.
get_program_spending_limit(env, program_id) -> i128EVOLVING
set_program_spend_threshold(env, program_id, threshold_amount)EVOLVINGAlert threshold (separate from hard cap).
update_rate_limit_config(env, window_size, max_operations, cooldown_period)INTERNALGlobal anti-abuse rate limit; not for integrators.
get_rate_limit_config(env) -> RateLimitConfigINTERNAL

Token Allowlist

FunctionSignatureStabilityNotes
add_allowed_token(env, token: Address)STABLEAdds to V1 allowlist.
add_allowed_token_with_decimals(env, token, decimals: u32)STABLEV2 allowlist with decimal normalization.
remove_allowed_token(env, token: Address)STABLE
is_token_allowed(env, token: Address) -> boolSTABLE
get_allowed_tokens(env) -> Vec<Address>STABLEV1 list.
get_allowed_tokens_with_decimals(env) -> Vec<AllowedTokenEntry>STABLEV2 list.

Split Configuration

FunctionSignatureStabilityNotes
set_split_config(env, program_id, config: SplitConfig)EVOLVING
get_split_config(env, program_id) -> Option<SplitConfig>EVOLVING
disable_split_config(env, program_id)EVOLVING

Circuit Breaker

FunctionSignatureStabilityNotes
set_circuit_admin(env, new_admin, caller)INTERNALCircuit breaker admin management.
get_circuit_admin(env) -> Option<Address>INTERNAL
get_circuit_breaker_status(env) -> CircuitBreakerStatusINTERNAL
reset_circuit_breaker(env, caller)INTERNAL
configure_circuit_breaker(env, ...)INTERNAL
emergency_open_circuit(env, admin)INTERNAL
set_program_cb_threshold(env, program_id, threshold)EVOLVINGPer-program circuit breaker override.

Query & Analytics

FunctionSignatureStabilityNotes
get_program_info(env) -> ProgramDataSTABLELegacy single-program read.
get_program_info_v2(env, program_id) -> ProgramDataSTABLEMulti-program read.
get_remaining_balance(env) -> i128STABLE
get_analytics(env) -> AnalyticsEVOLVING
get_program_analytics(env) -> AnalyticsEVOLVING
query_payouts_by_recipient(env, program_id, recipient, offset, limit) -> Vec<PayoutRecord>STABLEPaginated payout index.
query_recipient_history(env, program_id, recipient) -> Vec<PayoutRecord>STABLEFull history; used by view-facade.
query_payouts_by_amount(env, program_id, min, max, offset, limit) -> Vec<PayoutRecord>EVOLVING
get_batch_receipt(env, receipt_id) -> Option<BatchReceipt>EVOLVING
is_payout_processed(env, idempotency_key) -> boolSTABLE
get_program_metadata(env, program_id) -> Option<ProgramMetadata>STABLE
program_exists(env) -> boolSTABLELegacy.
program_exists_by_id(env, program_id) -> boolSTABLE

5. Contract: bounty-escrow

Crate: contracts/bounty_escrow/contracts/escrow
Contract struct: BountyEscrowContract
Purpose: Manages individual bounty escrows — fund locking per bounty, contributor release, refund workflows, capability-based authorization, and multi-token support.

5.1 Key Exported Types

TypeStabilityNotes
EscrowSTABLECore bounty state. Mirrored in bounty_escrow_bindings.rs. Field reorder is breaking.
EscrowStatus (enum)STABLE4 variants; exhaustive matches in facade — adding variant is breaking until facades updated.
EscrowMetadataSTABLEMirrored in bounty_escrow_bindings.rs (partial copy).
PauseFlagsSTABLEMirrored in bounty_escrow_bindings.rs. pause_reason: Option<String> is load-bearing.
EscrowWithIdEVOLVINGWrapper used by query_escrows_by_depositor; mirrored in binding.
FeeConfigEVOLVINGFee configuration; separate from program-escrow's FeeConfig — different type.
ClaimRecord / ClaimTicketEVOLVINGClaim workflow types.
Capability / CapabilityActionEVOLVINGCapability-based auth; may gain new action variants.
SimulationResultEVOLVINGDry-run result.
ParticipantFilterModeEVOLVINGWhitelist/blocklist mode enum.
AdminRotationStatus / AdminRotationConfigSTABLETwo-step admin rotation state.
RefundEligibilityViewEVOLVINGRefund eligibility check result.
FreezeRecordEVOLVINGPer-escrow and per-address freeze state.
TreasuryDestination / PerBountyFeeRoutingEVOLVINGMulti-region treasury routing.
AnonymousParty (enum)EVOLVINGAddress(Address) or Commitment(BytesN<32>); mirrored in binding.

5.2 Public Entry-Points

Initialization

FunctionSignatureStabilityNotes
init(env, admin: Address, token: Address) -> Result<(), Error>STABLEOne-time init.
init_with_network(env, admin, token, chain_id, network_id) -> Result<(), Error>EVOLVINGNetwork-aware init.

Fund Locking

FunctionSignatureStabilityNotes
lock_funds(env, bounty_id, depositor, amount, deadline, ...) -> Result<Escrow, Error>STABLECore lock operation.
lock_funds_anonymous(env, bounty_id, commitment, amount, deadline, ...) -> Result<AnonymousEscrow, Error>EVOLVINGPrivacy-preserving lock.
dry_run_lock(env, bounty_id, ...) -> SimulationResultEVOLVINGDry-run; no state change.
publish(env, bounty_id) -> Result<(), Error>STABLEMakes escrow visible.

Release & Refund

FunctionSignatureStabilityNotes
release_funds(env, bounty_id, contributor) -> Result<(), Error>STABLEStandard release to contributor.
release_with_conversion(env, bounty_id, contributor, ...) -> Result<(), Error>EVOLVINGRelease with AMM conversion.
release_with_capability(env, bounty_id, contributor, capability_id) -> Result<(), Error>EVOLVINGCapability-gated release.
partial_release(env, bounty_id, contributor, amount) -> Result<(), Error>EVOLVINGPartial amount release.
dry_run_release(env, bounty_id, contributor) -> SimulationResultEVOLVINGDry-run release.
refund(env, bounty_id) -> Result<(), Error>STABLEStandard refund to depositor.
dry_run_refund(env, bounty_id) -> SimulationResultEVOLVINGDry-run refund.
approve_refund(env, bounty_id, ...) -> Result<(), Error>EVOLVINGAdmin-approved refund path.
approve_large_release(env, bounty_id, ...) -> Result<(), Error>EVOLVINGMultisig-approved large release.
archive_escrow(env, bounty_id) -> Result<(), Error>STABLESoft-delete a settled escrow.

Claim Workflow

FunctionSignatureStabilityNotes
authorize_claim(env, bounty_id, contributor, amount, ...) -> Result<(), Error>EVOLVINGCreates a pending claim.
claim(env, bounty_id) -> Result<(), Error>EVOLVINGContributor executes claim.
claim_with_capability(env, bounty_id, capability_id) -> Result<(), Error>EVOLVINGCapability-gated claim.
cancel_pending_claim(env, bounty_id) -> Result<(), Error>EVOLVINGAdmin cancels pending claim.
get_pending_claim(env, bounty_id) -> Result<ClaimRecord, Error>EVOLVINGRead pending claim.

Capability System

FunctionSignatureStabilityNotes
issue_capability(env, bounty_id, action, ...) -> Result<BytesN<32>, Error>EVOLVINGIssues a one-time capability token.
revoke_capability(env, capability_id) -> Result<(), Error>EVOLVINGAdmin revokes a capability.
get_capability(env, capability_id) -> Result<Capability, Error>EVOLVINGRead capability state.

Participant Filtering

FunctionSignatureStabilityNotes
set_whitelist(env, address, whitelisted) -> Result<(), Error>STABLE
set_whitelist_entry(env, address, whitelisted) -> Result<(), Error>STABLEPaginated variant.
set_blocklist(env, address, blocked) -> Result<(), Error>STABLE
set_blocklist_entry(env, address, blocked) -> Result<(), Error>STABLE
set_filter_mode(env, mode: ParticipantFilterMode) -> Result<(), Error>EVOLVING
get_filter_mode(env) -> ParticipantFilterModeEVOLVING
query_whitelist(env, offset, limit) -> ParticipantListPageEVOLVINGPaginated whitelist read.
query_blocklist(env, offset, limit) -> ParticipantListPageEVOLVING

Pause, Freeze & Emergency

FunctionSignatureStabilityNotes
set_paused(env, lock_paused, release_paused, refund_paused, reason) -> PauseFlagsSTABLEMirrored in binding.
emergency_withdraw(env, target) -> Result<(), Error>STABLEAdmin-only; requires lock_paused.
freeze_escrow(env, bounty_id, reason) -> Result<(), Error>EVOLVINGFreeze a specific escrow.
unfreeze_escrow(env, bounty_id) -> Result<(), Error>EVOLVING
freeze_address(env, address, reason)EVOLVINGFreeze a participant address.
unfreeze_address(env, address) -> Result<(), Error>EVOLVING

Admin Rotation

FunctionSignatureStabilityNotes
propose_admin(env, new_admin)STABLEStep 1 of two-step rotation.
accept_admin(env)STABLEStep 2 — new admin accepts.
cancel_admin_transfer(env)STABLECancel pending rotation.
propose_admin_rotation(env, new_admin) -> Result<u64, Error>EVOLVINGTimelock-enforced variant.
accept_admin_rotation(env) -> Result<Address, Error>EVOLVING
cancel_admin_rotation(env) -> Result<(), Error>EVOLVING
set_rotation_timelock_duration(env, duration) -> Result<(), Error>EVOLVING

Queries

FunctionSignatureStabilityNotes
get_escrow_info(env, bounty_id) -> Result<Escrow, Error>STABLEUsed by escrow-view-facade binding.
get_metadata(env, bounty_id) -> EscrowMetadataSTABLEUsed by escrow-view-facade binding.
get_pause_flags(env) -> PauseFlagsSTABLEUsed by escrow-view-facade binding.
get_balance(env) -> i128STABLEContract-level token balance.
get_archived_escrows(env) -> Vec<u64>STABLE
get_fee_config(env) -> FeeConfigEVOLVING
get_refund_eligibility(env, bounty_id) -> ...EVOLVING
get_refund_eligibility_view(env, bounty_id) -> RefundEligibilityViewEVOLVING
get_admin_rotation_status(env) -> Option<AdminRotationStatus>EVOLVING

6. Contract: grainlify-core

Crate: contracts/grainlify-core
Contract struct: GrainlifyContract
Purpose: Contract upgrade management with timelocked proposals, version tracking, governance, config snapshots/rollback, contract registry, and liveness watchdog.

6.1 Key Exported Types

TypeStabilityNotes
ContractErrorSTABLEError discriminants; removing or renumbering variants is breaking.
CoreConfigSnapshotEVOLVINGSnapshot of on-chain config; fields may grow with new governance features.
SnapshotDiffEVOLVINGResult of compare_snapshots; format may change.
RollbackInfoEVOLVINGState before last rollback.
MigrationStateEVOLVINGTracks in-progress migration.
UpgradeProposalRecordEVOLVINGGovernance upgrade proposal.
DeployedContractEVOLVINGRegistry entry; ContractKind enum may gain new variants.
ContractKind (enum)EVOLVINGAdding a variant is additive only if consumers handle unknown kinds.
LivenessStatusEVOLVINGWatchdog output.
GovernanceConfigEVOLVINGMultisig/governance parameters.

6.2 Public Entry-Points

Initialization

FunctionSignatureStabilityNotes
init_admin(env, admin: Address)STABLESingle-admin init path.
init(env, signers: Vec<Address>, threshold: u32)STABLEMultisig init path.
init_with_network(env, admin, chain_id, network_id)EVOLVINGNetwork-aware init.
init_governance(env, admin, config: GovernanceConfig)EVOLVINGGovernance init path.

Upgrade Management

FunctionSignatureStabilityNotes
propose_upgrade(env, proposer, wasm_hash, expiry) -> u64STABLEReturns proposal_id.
approve_upgrade(env, proposal_id, signer)STABLEMultisig approval.
cancel_upgrade(env, proposal_id, canceller)STABLE
execute_upgrade(env, proposal_id)STABLEExecutes after timelock.
upgrade(env, new_wasm_hash: BytesN<32>)STABLEDirect upgrade (admin-only, no timelock).
get_upgrade_proposal(env, proposal_id) -> Option<UpgradeProposalRecord>STABLE
get_timelock_delay(env) -> u64STABLEDefault 24 h; min 1 h; max 30 d.
set_timelock_delay(env, delay_seconds)STABLEAdmin-only; bounded 1 h–30 d.
get_timelock_status(env, proposal_id) -> Option<u64>STABLESeconds remaining.
can_execute(env, proposal_id) -> boolSTABLE

Migration

FunctionSignatureStabilityNotes
commit_migration(env, target_version, hash, expires_at)STABLEHash commitment step.
migrate(env, target_version, migration_hash)STABLEExecutes migration after commitment.
get_migration_state(env) -> Option<MigrationState>STABLE
get_previous_version(env) -> Option<u32>STABLE

Config Snapshots & Rollback

FunctionSignatureStabilityNotes
create_config_snapshot(env) -> u64EVOLVINGReturns snapshot_id.
list_config_snapshots(env) -> Vec<CoreConfigSnapshot>EVOLVING
get_config_snapshot(env, snapshot_id) -> Option<CoreConfigSnapshot>EVOLVING
get_latest_config_snapshot(env) -> Option<CoreConfigSnapshot>EVOLVING
compare_snapshots(env, from_id, to_id) -> SnapshotDiffEVOLVING
restore_config_snapshot(env, snapshot_id)EVOLVINGAdmin-only.
propose_config_snapshot_restore(env, snapshot_id) -> u64EVOLVINGTimelocked restore.
execute_config_snapshot_restore(env, proposal_id)EVOLVING
cancel_config_change(env, proposal_id)EVOLVING
confirm_admin_restore(env, snapshot_id)EVOLVINGTwo-step admin confirmation.
get_rollback_info(env) -> RollbackInfoEVOLVING

Version & Read-Only

FunctionSignatureStabilityNotes
get_version(env) -> u32STABLECurrent numeric version.
get_version_semver_string(env) -> StringEVOLVINGHuman-readable semver.
get_version_numeric_encoded(env) -> u32EVOLVINGPacked major/minor/patch.
set_version(env, new_version: u32)STABLEAdmin-only.
require_min_version(env, min_numeric)EVOLVINGPanics if version < minimum.
is_read_only(env) -> boolSTABLE
set_read_only_mode(env, enabled)STABLEAdmin-only.
verify_storage_layout(env) -> boolINTERNALStorage schema validation; test use.

Contract Registry

FunctionSignatureStabilityNotes
register_deployed_contract(env, address, kind, version, ...)EVOLVINGRegisters a contract in the core registry.
deregister_deployed_contract(env, address)EVOLVING
get_deployed_contract(env, address) -> Option<DeployedContract>EVOLVING
deployed_contract_count(env) -> u32EVOLVING
list_deployed_contracts(env, offset, limit) -> Vec<DeployedContract>EVOLVING

Liveness & Pause

FunctionSignatureStabilityNotes
pause(env, signer)STABLE
unpause(env, signer)STABLE
is_paused(env) -> boolSTABLE
ping_watchdog(env)EVOLVINGKeeps liveness watchdog alive.
liveness_watchdog(env) -> LivenessStatusEVOLVINGReturns current liveness state.

Governance

FunctionSignatureStabilityNotes
init_governance(env, admin, config)EVOLVING
propose_upgrade(env, proposer, wasm_hash, expiry) -> u64STABLE
approve_upgrade(env, proposal_id, signer)STABLE

Admin

FunctionSignatureStabilityNotes
get_admin(env) -> Option<Address>STABLE
get_chain_id(env) -> Option<String>EVOLVING
get_network_id(env) -> Option<String>EVOLVING
get_network_info(env) -> (Option<String>, Option<String>)EVOLVING

7. Contract: view-facade

Crate: contracts/view-facade
Contract struct: ViewFacade
Purpose: Read-only registry of Grainlify contract deployments; aggregates cross-contract queries for dashboards, indexers, and wallets.

7.1 Key Exported Types

TypeStabilityNotes
PayoutRecordSTABLE⚠ DRIFT — local copy omits payout_type field present in canonical program-escrow. See §3.1.
RegisteredContractSTABLERegistry entry; ContractKind enum variant addition is additive.
ContractKind (enum)STABLEBountyEscrow, ProgramEscrow, SorobanEscrow, GrainlifyCore. Adding variant is additive only.
FacadeErrorSTABLE4 error codes; removing or renumbering is breaking.

7.2 Public Entry-Points

FunctionSignatureStabilityNotes
init(env, admin: Address) -> Result<(), FacadeError>STABLEOne-time init; stores admin and empty registry.
get_admin(env) -> Option<Address>STABLE
register(env, address, kind, version, ...) -> Result<(), FacadeError>STABLEUpsert semantics (duplicate = update).
deregister(env, address) -> Result<(), FacadeError>STABLEAdmin-only removal.
list_contracts(env, offset: u32, limit: u32) -> Vec<RegisteredContract>STABLEPaginated registry.
list_contracts_all(env) -> Vec<RegisteredContract>STABLEBounded by MAX_REGISTRY_SIZE=1000.
contract_count(env) -> u32STABLE
get_contract(env, address) -> Option<RegisteredContract>STABLESingle-contract lookup.
query_recipient_history(env, escrow_contract, program_id, recipient) -> Vec<PayoutRecord>STABLECross-contract call; returns local PayoutRecord (drift from canonical).

8. Contract: escrow-view-facade

Crate: contracts/escrow-view-facade
Contract struct: EscrowViewFacade
Purpose: Read-only aggregation of bounty-escrow data for frontend consumption; also proxies delegate queries to program-escrow.

8.1 Key Exported Types

TypeStabilityNotes
EscrowStatus (enum)STABLELocal copy of bounty-escrow::EscrowStatus. Must stay in sync with canonical (see §3.2).
EscrowSummarySTABLEAggregated view of a single bounty; used by get_escrow_summary.
UserPortfolioEVOLVINGas_beneficiary field is always empty today — placeholder for future use.

8.2 Binding Files

Binding FileMirrorsStatus
src/bounty_escrow_bindings.rsbounty-escrow: EscrowStatus, EscrowMetadata, PauseFlags, Escrow, EscrowWithId, AnonymousParty🔴 Must update in same PR as canonical
src/program_escrow_bindings.rsprogram-escrow: ProgramDelegateInfo🔴 Must update in same PR as canonical

8.3 Public Entry-Points

FunctionSignatureStabilityNotes
get_escrow_summary(env, escrow_contract: Address, bounty_id: u64) -> Option<EscrowSummary>STABLEReturns None instead of trapping.
get_escrow_summaries(env, escrow_contract, bounty_ids: Vec<u64>) -> Vec<EscrowSummary>STABLEBatch; missing bounties omitted.
get_user_portfolio(env, escrow_contract, user: Address) -> UserPortfolioEVOLVINGas_beneficiary currently always empty.
query_all_delegates(env, program_contract: Address, program_id: String) -> Vec<ProgramDelegateInfo>STABLEReturns empty vec on error rather than trapping.

9. Cross-Contract Dependency Graph

┌─────────────────────┐
│ grainlify-core │
│ (upgrade mgmt, │
│ registry) │
└──────────┬──────────┘
│ (no direct calls; contracts
│ register themselves here)
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ program- │ │ bounty- │ │ (future) │
│ escrow │ │ escrow │ │ │
└──────┬───────┘ └──────┬───────┘ └──────────────────┘
│ │
│ query_recipient_ │ get_escrow_info
│ history │ get_metadata
▼ │ get_pause_flags
┌──────────────┐ │ query_escrows_by_depositor
│ view-facade │ │
└──────────────┘ ▼
┌──────────────────────┐
│ escrow-view-facade │
│ also calls │
│ program-escrow: │
│ query_all_delegates │
└──────────────────────┘

Binding dependency table:

CallerCalleeBinding FileFunctions Called
view-facadeprogram-escrowinline ProgramEscrowTraitquery_recipient_history
escrow-view-facadebounty-escrowbounty_escrow_bindings.rsget_escrow_info, get_metadata, get_pause_flags, query_escrows_by_depositor
escrow-view-facadeprogram-escrowprogram_escrow_bindings.rsquery_all_delegates

10. Upgrade Checklist

Use this checklist whenever modifying a type listed in §3 or a function used across contract boundaries (§9).

Before making a change

  • Identify all entries in §3 that reference the type or function being changed.
  • Identify all binding files from §9 that call the function or mirror the type.
  • Determine if the change is breaking or additive per §2.

For breaking changes

  • Bump the storage schema version constant for affected contracts.
  • Update the binding file in the same PR as the canonical contract change.
  • Update the local copy in any facade that re-declares the type (e.g. escrow-view-facade's EscrowStatus, view-facade's PayoutRecord).
  • Update this matrix document to reflect the new signature and sync status.
  • Add or update a serialization golden test to catch future regressions.
  • Run cargo test -p program-escrow, cargo test -p grainlify-core, and cargo test -p bounty-escrow — all must pass.

For additive changes

  • If a new field is appended to a #[contracttype] struct, verify that old storage entries are still readable (add a deserialization test with the old layout).
  • If a new enum variant is added to a shared enum, update all exhaustive match arms in facade bindings.
  • Update this matrix document.

Known Outstanding Issues

IssueLocationRisk
PayoutRecord field drift — payout_type missing in view-facadeview-facade/src/lib.rs🔴 HIGH — returned data is incomplete
ProgramData.circuit_breaker_threshold has unresolved merge conflict (Option<u8> vs Option<u32>)program-escrow/src/lib.rs line ~743🔴 CRITICAL — must resolve before deployment
UserPortfolio.as_beneficiary is always emptyescrow-view-facade/src/lib.rs🟡 MEDIUM — documented placeholder

This document is automatically linked from each contract's crate-level doc comment.
See: program-escrow/src/lib.rs, bounty-escrow/.../lib.rs, grainlify-core/src/lib.rs,
view-facade/src/lib.rs, escrow-view-facade/src/lib.rs.