This guide explains how third-party providers can deploy a compatible payer-verification oracle and register it with the Invoice Liquidity Network.
The ILN payer-verification oracle is an optional on-chain component. When registered, the ILN contract calls it to check whether a payer's identity or creditworthiness has been verified off-chain before allowing invoice funding. See oracle-design.md for the full design specification.
Any oracle contract must expose two Soroban entry-points:
// Returns the verification record for `payer`
fn get_verification(env: Env, payer: Address) -> VerificationResult
// Updates the verification status for `payer` (oracle-operator only)
fn update_verification(env: Env, payer: Address, verified: bool)Where VerificationResult is a #[contracttype] struct:
#[contracttype]
pub struct VerificationResult {
pub verified: bool, // true = payer is verified
pub timestamp: u64, // Unix epoch seconds of last update
}The get_verification method must never panic under normal conditions. If no record exists for a payer, return VerificationResult { verified: false, timestamp: 0 }.
The update_verification method should enforce access control (e.g., require_auth() for the oracle operator address). The ILN contract never calls this method directly.
ILN treats oracle data older than 7 days (604 800 seconds) as unverified. Oracle operators must refresh records at least every 7 days to keep payers active.
The threshold is defined in oracle_interface.rs as ORACLE_STALENESS_THRESHOLD_SECS.
Create a Soroban contract that implements get_verification and update_verification as shown above. A minimal reference implementation is available in contracts/tests/mocks/mock_oracle.rs.
stellar contract deploy \
--wasm oracle.wasm \
--source <operator-keypair> \
--network <testnet|mainnet>Note the deployed contract address.
The ILN admin must call set_price_oracle with the oracle contract address. This is a separate transaction after initialize():
// Using the Soroban TypeScript SDK
await ilnClient.set_price_oracle({
oracle: oracleContractAddress,
}, { fee: 100 });Once registered, all subsequent payer-verification checks use the new oracle. There is no delay — the oracle takes effect immediately.
The oracle operator calls update_verification for each verified payer:
await oracleClient.update_verification({
payer: payerAddress,
verified: true,
}, { fee: 100 });Refresh records at least every 7 days to stay within the staleness threshold.
| Situation | ILN Response |
|---|---|
| No oracle registered | All payers pass (fail-open) |
Oracle returns verified = false |
Payer check fails |
| Oracle timestamp > 7 days old | Treated as unverified |
| Oracle contract panics | Treated as unverified |
The mock oracle in contracts/tests/mocks/mock_oracle.rs lets you test oracle-dependent behaviour deterministically.
// In a test:
#[path = "path/to/mocks/mock_oracle.rs"]
mod mock_oracle;
use mock_oracle::{MockOracle, MockOracleClient};
let env = Env::default();
env.mock_all_auths();
let oracle_id = env.register(MockOracle, ());
let oracle = MockOracleClient::new(&env, &oracle_id);
// Mark a payer as verified
oracle.set_verified(&payer_address, &true);
// Set the oracle timestamp (simulate fresh data)
oracle.set_timestamp(&env.ledger().timestamp());// Oracle last updated 30 days ago
oracle.set_timestamp(&(env.ledger().timestamp() - 30 * 24 * 60 * 60));
oracle.set_verified(&payer, &true);
// ILN will treat this as unverified because timestamp is stale
let result = iln_client.check_something(&payer);// Arm a one-shot panic
oracle.set_should_panic();
// The next get_verification call panics; ILN treats payer as unverifiedSee contracts/tests/oracle_integration_test.rs for complete test examples.
- Oracle
update_verificationenforcesrequire_auth()for the operator - The ILN admin key that calls
set_price_oracleis protected (hardware wallet / multisig) - Consider a governance timelock on oracle registration changes (see governance.md)
- Refresh verification records before they exceed 7 days
- Monitor oracle contract for unexpected upgrades or admin key changes
- The oracle is a single trust point — evaluate whether multi-oracle quorum is needed for your use case