AutoFi is a Soroban-powered middleware that hooks into open-source payout engines (Drips Waves, GitHub bounties). The moment a developer receives an on-chain token reward (USDC, XLM), AutoFi catches it, applies a configurable split rule stored on-chain, and executes an atomic path payment swap into a local fiat anchor stablecoin (NGNX, USDC, GBPT) β pushing funds directly into local banking infrastructure.
Live on Drips Network: https://docs.drips.network/wave
- On-chain Split Rules β Developer preferences (% to off-ramp vs. hold) are stored in a Soroban smart contract, not a centralised server.
- Atomic Path Payments β One Stellar operation swaps inbound reward tokens to an anchor stablecoin via the native DEX.
- Multi-Currency Off-Ramp β Supports NGN (NIBSS), USD (ACH), GBP (SEPA) via SEP-6/SEP-24 anchor integrations.
- Drips Wave Listener β Monitors Drips Wave payouts and fires the off-ramp pipeline automatically.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β External Triggers β
β Drips Wave Payout β GitHub Bounty β Manual Send β
ββββββββββββ¬βββββββββββ΄βββββββββ¬βββββββββ΄βββββββ¬ββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β dripListener.ts β
β Polls Drips subgraph / webhook β RewardEvent{} β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β index.ts β
β 1. Read RoutePreferences from Soroban contract β
β 2. Calculate off-ramp amount (amount Γ pct / 100) β
β 3. Call executePathPayment() β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β
ββββββββββββββ΄βββββββββββββ
βΌ βΌ
ββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Soroban Contract β β pathPayment.ts β
β reward_router β β pathPaymentStrictSend β
β RoutePreferences β β inbound β anchor asset β
ββββββββββββββββββββ ββββββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Stellar Anchor β
β SEP-6 / SEP-24 β
β NGN / USD / GBP β
ββββββββββββββββββββββββ
autofi-wallet/
βββ .env.example
βββ README.md
βββ package.json
βββ tsconfig.json
βββ contracts/
β βββ reward_router/
β βββ Cargo.toml
β βββ src/
β βββ lib.rs # Soroban contract: stores RoutePreferences per user
βββ src/
βββ index.ts # Orchestrator: listens β reads prefs β executes swap
βββ config/
β βββ anchors.ts # SEP-6/SEP-24 anchor configs (NGNX, USDC, GBPT)
βββ services/
β βββ dripListener.ts # Drips Wave / GitHub bounty event listener
β βββ pathPayment.ts # Stellar pathPaymentStrictSend executor
βββ types/
βββ index.ts # Shared TypeScript interfaces
- Node.js v18+
- Rust + Soroban CLI
- Stellar Testnet or Mainnet account funded with XLM
# 1. Install TypeScript dependencies
yarn install
# 2. Copy env config and fill in your keys
cp .env.example .env
# 3. Build the Soroban contract
cd contracts/reward_router
soroban contract build
# 4. Deploy to testnet (get back a contract ID)
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/reward_router.wasm \
--source <YOUR_SECRET_KEY> \
--rpc-url https://soroban-testnet.stellar.org \
--network-passphrase "Test SDF Network ; September 2015"
# 5. Start the listener
cd ../..
yarn startStores each developer's routing preferences on-chain. The set_preferences call requires the user's Stellar auth signature so only the wallet owner can modify their own rules.
#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RoutePreferences {
pub off_ramp_pct: u32,
pub keep_crypto_pct: u32,
pub anchor_asset_code: String, // "NGNX" | "USDC" | "GBPT"
pub anchor_issuer: Address,
}
#[contract]
pub struct RewardRouter;
#[contractimpl]
impl RewardRouter {
pub fn set_preferences(env: Env, user: Address, prefs: RoutePreferences) {
user.require_auth();
assert!(
prefs.off_ramp_pct + prefs.keep_crypto_pct == 100,
"allocations must sum to 100"
);
env.storage().persistent().set(&user, &prefs);
}
pub fn get_preferences(env: Env, user: Address) -> RoutePreferences {
env.storage()
.persistent()
.get(&user)
.expect("no preferences set for this address")
}
}Single function that builds and submits a pathPaymentStrictSend transaction. The empty path: [] lets Horizon's pathfinding engine auto-route through the Stellar DEX.
import { Horizon, Keypair, Operation, TransactionBuilder, Asset, Networks } from '@stellar/stellar-sdk';
const server = new Horizon.Server(process.env.HORIZON_URL || 'https://horizon-testnet.stellar.org');
export async function executePathPayment({ developerKeypair, sendAmount, sendAsset, destAsset, destMin = '0.0000001' }) {
const account = await server.loadAccount(developerKeypair.publicKey());
const tx = new TransactionBuilder(account, { fee: '100000', networkPassphrase: Networks.TESTNET })
.addOperation(Operation.pathPaymentStrictSend({
sendAsset, sendAmount,
destination: developerKeypair.publicKey(),
destAsset, destMin,
path: [], // Horizon auto-routes via DEX
}))
.setTimeout(30)
.build();
tx.sign(developerKeypair);
const result = await server.submitTransaction(tx);
return result.hash;
}Polls for incoming Drips Wave payouts and emits RewardEvent objects to the handler pipeline. Replace the mock interval with a real Drips subgraph query or webhook.
export async function startDripListener(recipientAddress: string, onReward: RewardHandler, intervalMs = 30_000) {
setInterval(async () => {
// TODO: query Drips subgraph for new splits/squeezes targeting recipientAddress
const event: RewardEvent = { developerPublicKey: recipientAddress, amount: '100.00', assetCode: 'USDC', ... };
await onReward(event);
}, intervalMs);
}Ties the pipeline together: receive event β read on-chain prefs β calculate split β execute swap.
async function handleReward(event: RewardEvent) {
const prefs = await readContractPreferences(event.developerPublicKey); // TODO: soroban-client call
const offRampAmount = ((parseFloat(event.amount) * prefs.off_ramp_pct) / 100).toFixed(7);
const txHash = await executePathPayment({
developerKeypair: Keypair.fromSecret(process.env.DEV_PRIVATE_KEY!),
sendAmount: offRampAmount,
sendAsset: new Asset(event.assetCode, event.assetIssuer),
destAsset: new Asset(anchor.code, anchor.issuer),
});
console.log(`β
Off-ramp complete: ${txHash}`);
// Next: trigger SEP-24 interactive deposit for bank push
}| Variable | Description |
|---|---|
STELLAR_NETWORK |
testnet or mainnet |
HORIZON_URL |
Horizon server endpoint |
DEV_PUBLIC_KEY |
Developer's Stellar public key |
DEV_PRIVATE_KEY |
Developer's Stellar secret key |
NGNX_ISSUER |
NGNX anchor issuer address |
GBPT_ISSUER |
GBPT anchor issuer address |
SOROBAN_RPC_URL |
Soroban RPC endpoint |
REWARD_ROUTER_CONTRACT_ID |
Deployed contract address |
These are the remaining ~50% of the project β create GitHub issues for each:
- Soroban client integration β Replace mock prefs with live
get_preferencescall via@stellar/stellar-sdkcontract client - Drips subgraph query β Implement real Drips Wave payout listener using the GraphQL subgraph
- SEP-24 off-ramp trigger β After path payment, call anchor's
/transactions/deposit/interactiveto initiate bank transfer - SEP-6 polling β Poll anchor transaction status and emit webhook/notification on completion
- GitHub bounty listener β Parse GitHub webhook events for bounty payouts
- Slippage protection β Dynamically calculate
destMinfrom current DEX orderbook before submitting - Multi-account support β Handle multiple developer wallets from a single running instance
- Frontend UI β Simple web interface for setting
RoutePreferencesand viewing transaction history
MIT