Skip to content

[Smart Contracts] Oracle-Triggered Conditional Streaming & KPI-Gated Milestone Releases #1482

Description

@blurbeast

Overview & Background

Fixed-rate or strictly time-dependent streams cannot adapt to dynamic real-world conditions. Grants, bounty programs, and venture vesting often require condition-gated releases—such as unlocking a tranche only when a token reaches a specific price ceiling, or when a trusted third-party oracle attests that a development milestone, GitHub release, or KPI metric has been achieved. Integrating Soroban oracle interfaces (such as SEP-0040 or Reflector Oracle) allows FlowFi to pioneer dynamic condition-driven streaming.


Detailed Problem Statement

  1. Inability to Peg Streams to Real-World Value:
    • In volatile cryptocurrency markets, paying contractors in native tokens (e.g. XLM) at a fixed token-per-second rate results in unfair wage inflation or deflation as price fluctuates against USD.
  2. Manual Milestone Approvals:
    • Grant committees must manually execute contract cancellations or deployments when grantees meet or fail milestones, introducing latency and administrative overhead.
  3. No Standardized Soroban Oracle Hook:
    • FlowFi's contract has no extensible callback or read interface to query on-chain price feeds or verifiable attestations.

Technical Specification & Architecture

1. Oracle Interface Integration (SEP-0040 / Reflector Interface)

#[contractclient(name = "OracleClient")]
pub trait PriceOracle {
    fn lastprice(env: Env, asset: Asset) -> Option<PriceData>;
}

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PriceData {
    pub price: i128,
    pub timestamp: u64,
}

2. Condition-Gated Vesting Model (types.rs)

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnlockCondition {
    TimeOnly { unlock_time: u64 },
    PriceTarget { oracle_address: Address, target_price: i128, is_above: bool },
    OracleAttestation { oracle_signer: Address, attestation_id: BytesN<32> },
}

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ConditionalMilestone {
    pub milestone_id: u32,
    pub amount: i128,
    pub condition: UnlockCondition,
    pub is_unlocked: bool,
}

3. Verification & Trigger Entrypoint

pub fn verify_and_unlock_milestone(
    env: Env,
    caller: Address,
    stream_id: u64,
    milestone_id: u32,
) -> Result<i128, StreamError>
  • Query oracle contract via cross-contract call.
  • Verify price timestamp freshness (revert if stale > 3600 seconds).
  • If condition evaluated to true:
    • Mark milestone.is_unlocked = true.
    • Add milestone.amount to claimable balance.
    • Emit MilestoneConditionUnlocked event.

Target Files

  • contracts/stream_contract/src/types.rs
  • contracts/stream_contract/src/lib.rs
  • contracts/stream_contract/src/oracle.rs
  • contracts/stream_contract/src/events.rs
  • contracts/stream_contract/src/test.rs

Acceptance Criteria

  • Contract can query external SEP-0040 price oracle and verify target price condition.
  • Stale oracle data older than max threshold is rejected with StreamError::OracleDataStale.
  • Successful condition verification transitions milestone to unlocked state and enables recipient claim.
  • Unauthorized or false conditions cannot trigger premature milestone unlock.
  • Unit tests with mock oracle simulate price triggers, timestamp staleness, and attestation verification.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions