Move-based DeFi protocol for on-chain Systematic Investment Plans (SIPs), AI-driven yield routing, and multi-sig emergency fund locking β built on OneChain's sub-second-finality Layer 1.
π Live App Β· π₯ Demo Video Β· π¦ Source Β· π§ Quick Start Β· π‘οΈ Security
- Executive Summary & Impact Metrics
- Architecture & Data Flow
- Technical Deep-Dive
- Tech Stack
- Directory Structure
- Development & Quick Start Guide
- Testing & Verification
- Security Controls & Roadmap
Retail DeFi investors face three compounding technical bottlenecks: manual re-entry friction (no native recurring-investment primitive on most chains), state desynchronization between off-chain scheduling and on-chain execution, and unbounded custodial risk β funds sit in a single-signer wallet with no time-lock or emergency-recovery path if a key is compromised.
OneSIP encodes the entire investment lifecycle as on-chain Move objects rather than off-chain cron jobs: a SIP resource holds its own schedule, balance, and status; a YieldRouter performs risk-adjusted pool allocation via resource-oriented programming; and a LockedVault enforces 3-of-5 multi-sig governance with a mandatory 7-day minimum time-lock before any emergency unlock can execute β moving custody and scheduling logic out of the frontend and into verifiable on-chain state.
| Metric | Value | Mechanism |
|---|---|---|
| Transaction finality | < 5 seconds | OneChain single-slot finality |
| Average transaction cost | ~$0.00001 | OneChain low-fee gas model |
| Network throughput | 1,000+ TPS | OneChain parallel execution |
| Emergency unlock threshold | 3-of-5 approvals | REQUIRED_APPROVALS multi-sig constant |
| Minimum vault lock duration | 7 days (604,800s) | MIN_LOCK_DURATION time-lock constant |
| Yield pool coverage | 20+ DeFi pools | YieldRouter dynamic allocation |
| Smart contract modules | 3 core Move modules | sip_manager, yield_router, lock_vault |
| REST API surface | 8 route groups | SIPs, Portfolio, Vault, Yield, Activity, Notifications, Proposals, Analytics |
graph TB
subgraph Frontend["Frontend Layer β Next.js 15 App Router"]
A["React 19 UI Components"]
B["Tailwind CSS + Radix UI"]
C["Sui dApp Kit β Wallet Adapter"]
end
subgraph Offchain["Off-Chain Backend β Next.js Route Handlers"]
D["/api/sips β SIP CRUD"]
E["/api/portfolio β Aggregation"]
F["/api/yield/pools β Pool Discovery"]
G["/api/vault β Lock/Unlock Proxy"]
H["/api/chat β Gemini AI Agent"]
I["/api/analytics β Platform Metrics"]
end
subgraph Onchain["On-Chain Layer β OneChain (Move VM)"]
J["sip_manager.move<br/>SIP resource + schedule state"]
K["yield_router.move<br/>Portfolio + pool allocation"]
L["lock_vault.move<br/>Multi-sig time-locked vault"]
end
subgraph Storage["State & Indexing"]
M["On-Chain Object State<br/>(UID-owned Move resources)"]
N["Emitted Move Events<br/>(execution/lock/unlock)"]
end
A --> C
B --> A
C -->|"signed tx"| D
C -->|"signed tx"| G
D -->|"create_sip / execute_sip"| J
E -->|"read aggregation"| J
F -->|"get_optimal_pool"| K
G -->|"lock_funds / unlock_vault"| L
H -->|"Gemini API"| A
J --> M
K --> M
L --> M
J -->|"event::emit"| N
K -->|"event::emit"| N
L -->|"event::emit"| N
I --> N
style Onchain fill:#1a0033,color:#fff,stroke:#7D00FF
style Offchain fill:#0f172a,color:#fff,stroke:#00ADD8
style Frontend fill:#111,color:#fff,stroke:#666
sequenceDiagram
participant U as User
participant W as Wallet (dApp Kit)
participant F as Frontend (Next.js)
participant API as Off-Chain API
participant M as Move Contract
U->>W: Connect Wallet
W->>F: Wallet Connected (address)
F->>API: GET /api/portfolio?userAddress=
API->>M: Query on-chain SIP objects
M-->>API: SIP state + balances
API-->>F: Portfolio snapshot
U->>F: Create SIP (amount, frequency, token)
F->>W: Request Signature
W->>U: Confirm Transaction
U->>W: Approve
W->>M: create_sip() entry function
M->>M: Mint SIP resource, set next_execution
M-->>F: Transaction digest + object ID
F->>U: SIP Created β
flowchart LR
A["SIP Created<br/>(create_sip)"] --> B{"Time Check<br/>clock >= next_execution?"}
B -->|"Not Due"| C["Idle β Await Trigger"]
C --> B
B -->|"Due"| D["execute_sip()"]
D --> E["Split Coin<br/>from owner balance"]
E --> F["Route to YieldRouter<br/>get_optimal_pool(max_risk)"]
F --> G["Update SIP State<br/>total_deposits += amount"]
G --> H["event::emit<br/>SIPExecuted"]
H --> I["Reschedule<br/>next_execution += frequency"]
I --> B
D -.->|"pause_sip / cancel_sip"| J["Paused / Cancelled"]
flowchart TB
A["lock_funds()<br/>owner deposits + sets unlock_time"] --> B["Status: LOCKED"]
B --> C{"clock >= unlock_time?"}
C -->|"Yes"| D["unlock_vault()<br/>owner withdraws normally"]
C -->|"No, emergency needed"| E["create_emergency_proposal()<br/>reason + guardian set"]
E --> F["approve_emergency_unlock()<br/>Γ guardian signatures"]
F --> G{"approvals >= REQUIRED_APPROVALS<br/>(3-of-5)?"}
G -->|"No"| F
G -->|"Yes"| H["execute_emergency_unlock()<br/>Status: EMERGENCY_UNLOCK"]
D --> I["Status: UNLOCKED"]
H --> I
style E fill:#3a0000,color:#fff,stroke:#ff4d4d
style G fill:#332200,color:#fff,stroke:#ffaa00
graph LR
subgraph SM["sip_manager.move"]
A1["create_sip"]
A2["execute_sip"]
A3["pause_sip / resume_sip"]
A4["cancel_sip"]
end
subgraph YR["yield_router.move"]
B1["add_pool"]
B2["create_portfolio"]
B3["deposit"]
B4["get_optimal_pool"]
end
subgraph LV["lock_vault.move"]
C1["lock_funds"]
C2["create_emergency_proposal"]
C3["approve_emergency_unlock"]
C4["execute_emergency_unlock"]
end
A2 --> B3
A1 --> B2
B3 --> B4
C1 --> C2
C2 --> C3
C3 --> C4
Each SIP is a Move resource with key, store ability β not a database row β so ownership, transfer, and mutation are enforced by the Move type system itself rather than application logic. create_sip() mints the resource with an amount_per_deposit, frequency, and next_execution timestamp; execute_sip() is a time-gated entry function that reads Clock on-chain, asserts the schedule is due, splits the deposit coin, and advances next_execution atomically in the same transaction β eliminating the off-chain-cron-vs-on-chain-state race condition that plagues most "automated" DeFi schedulers.
get_optimal_pool(router, max_risk) performs on-chain risk-filtered pool selection across a registered VecSet of pools, while the /api/chat route layers a Google Gemini conversational agent on top for natural-language portfolio queries ("move my SIP to a lower-risk pool"). The frontend never allocates funds directly β every reallocation still terminates in a signed deposit() call against the Move contract, keeping the AI layer advisory rather than custodial.
LockedVault enforces a MIN_LOCK_DURATION of 604,800 seconds (7 days) at the type level via E_INVALID_UNLOCK_TIME assertion β no code path can construct a vault with a shorter lock. Emergency access requires create_emergency_proposal() followed by REQUIRED_APPROVALS (3-of-5) independent approve_emergency_unlock() calls from distinct guardian addresses tracked in a VecSet<address>, with E_ALREADY_APPROVED preventing a single guardian from double-counting their signature β a governance quorum pattern implemented natively in Move rather than via an external multi-sig wallet dependency.
Every state-changing entry function (execute_sip, lock_funds, execute_emergency_unlock) emits a structured Move event::emit, giving the off-chain /api/activity and /api/analytics routes an append-only audit log sourced directly from chain state rather than a mutable off-chain database β critical for the transparency guarantees a systematic-investment product needs for user trust.
| Layer | Technology | Architectural Purpose |
|---|---|---|
| Smart Contracts / Core Logic | Move (sip_manager, yield_router, lock_vault) |
Resource-oriented on-chain state; ownership & transfer safety enforced by the type system |
| Blockchain Runtime | OneChain (Sui-derived Move VM) | Sub-5s finality, parallel object-level execution, ~$0.00001 avg tx cost |
| Wallet Integration | @mysten/dapp-kit, @mysten/sui |
Standardized signing, transaction building, and wallet-adapter interface |
| Backend Services | Next.js 15 App Router (Route Handlers) | Off-chain aggregation layer between wallet-signed txs and on-chain reads |
| AI Layer | Google Gemini (@google/generative-ai) |
Conversational SIP/portfolio management via /api/chat |
| Frontend | React 19, Tailwind CSS 4, Radix UI, shadcn/ui primitives |
Accessible component layer, dashboard visualizations (recharts) |
| Forms & Validation | react-hook-form + zod |
Type-safe, schema-validated SIP creation forms |
| Telemetry & Indexing | Move event::emit β /api/activity, /api/analytics |
On-chain event stream as the source of truth for audit/analytics |
| Analytics | @vercel/analytics |
Production usage telemetry |
| Deployment | Vercel (frontend), Netlify-compatible config, Docker, Nginx | Multi-target static/edge deploy with reverse-proxy support |
| Contract Tooling | OneChain CLI (one move build/test/publish) |
Move package compilation, unit testing, and on-chain publishing |
One-SIP/
βββ app/ # Next.js 15 App Router
β βββ api/ # Off-chain backend route handlers
β β βββ sips/ # SIP CRUD (create, list, cancel)
β β βββ portfolio/ # Portfolio aggregation
β β βββ vault/ # Lock/unlock proxy endpoints
β β βββ yield/ # Pool discovery & routing
β β βββ chat/ # Gemini AI conversational agent
β β βββ activity/ # Event-sourced activity feed
β β βββ notifications/ # User notification state
β β βββ proposals/ # Emergency-unlock proposals
β β βββ analytics/ # Platform-wide metrics
β βββ dashboard/ # Portfolio overview page
β βββ sips/ # SIP management UI
β βββ vault/ # Emergency vault UI
β βββ yield/ # Yield pool explorer
β βββ chat/ # AI assistant UI
β βββ security/ # Security & audit-trail page
β βββ settings/ # User settings
βββ contracts/ # On-chain Move package
β βββ Move.toml
β βββ sources/
β βββ sip_manager.move # SIP lifecycle entry functions
β βββ yield_router.move # Pool allocation & routing logic
β βββ lock_vault.move # Multi-sig time-locked vault
βββ components/ # Shared React component library
βββ hooks/ # Custom React hooks
βββ lib/ # Client utilities, chain config
βββ docs/ # Additional technical documentation
βββ public/ # Static assets
βββ .env.example # Required environment variables
βββ docker-compose.yml # Local multi-service orchestration
βββ Dockerfile # Frontend container build
βββ nginx.conf # Reverse-proxy config for containerized deploy
βββ SECURITY.md # Vulnerability disclosure policy
βββ LICENSE # MIT License
| Requirement | Version | Purpose |
|---|---|---|
| Node.js | >= 18.x |
Frontend/backend build & runtime |
| npm | >= 9.x |
Package management |
OneChain CLI (one) |
Latest | Move contract build/test/publish |
| Google Gemini API key | β | Enables the AI chat assistant |
git clone https://github.com/Aaditya1273/One-SIP.git
cd One-SIP
npm installcp .env.example .env.env.example:
# OneChain Configuration
NEXT_PUBLIC_ONECHAIN_NETWORK=testnet
NEXT_PUBLIC_ONECHAIN_RPC_URL=https://rpc-testnet.onelabs.cc:443
NEXT_PUBLIC_ONECHAIN_FAUCET_URL=https://faucet-testnet.onelabs.cc/v1/gas
# Smart Contract Package IDs (update after deployment)
# Deploy with: cd contracts && one client publish --gas-budget 100000000
NEXT_PUBLIC_SIP_MANAGER_PACKAGE_ID=YOUR_DEPLOYED_PACKAGE_ID_HERE
NEXT_PUBLIC_YIELD_ROUTER_PACKAGE_ID=YOUR_DEPLOYED_PACKAGE_ID_HERE
NEXT_PUBLIC_LOCK_VAULT_PACKAGE_ID=YOUR_DEPLOYED_PACKAGE_ID_HERE
# Shared Object IDs (deployed contract instances)
NEXT_PUBLIC_SIP_MANAGER_OBJECT_ID=YOUR_SIP_MANAGER_OBJECT_ID_HERE
NEXT_PUBLIC_YIELD_ROUTER_OBJECT_ID=YOUR_YIELD_ROUTER_OBJECT_ID_HERE
NEXT_PUBLIC_VAULT_MANAGER_OBJECT_ID=YOUR_VAULT_MANAGER_OBJECT_ID_HERE
# AI Configuration
GOOGLE_GEMINI_API_KEY=your_gemini_api_key_here
# Analytics
NEXT_PUBLIC_VERCEL_ANALYTICS_ID=# Install OneChain CLI
cargo install --locked --git https://github.com/one-chain-labs/onechain.git one_chain
mv ~/.cargo/bin/one_chain ~/.cargo/bin/one
# Create wallet & fund from faucet
one client
one client faucet
# Build, test, publish
cd contracts
one move build
one move test
one client publish --gas-budget 100000000Copy the resulting Package ID and Shared Object IDs into .env.
npm run devdocker compose up --build# Move contract unit tests (sip_manager, yield_router, lock_vault)
cd contracts && one move test
# Frontend/backend build verification
npm run build
# Lint checks
npm run lint
# Contract test-only helpers exposed for CI (init_for_testing)
one move test --filter init_for_testingCoverage types implemented:
- β
Unit tests β Move
#[test]functions per module viaone move test, usinginit_for_testing()scaffolds in each contract - β
Build verification β
next buildtype-checks the full TypeScript surface before deploy β οΈ Integration / E2E tests β not yet present in this repo; see roadmap below- β
Invariant checks β enforced at the Move type level:
E_INVALID_UNLOCK_TIME,E_ALREADY_APPROVED,E_INSUFFICIENT_APPROVALSassertions guard vault and SIP state transitions from invalid mutation
- Resource-oriented custody β SIP and vault balances are Move resources with
key, storeability; they cannot be copied, implicitly dropped, or double-spent by construction - Time-lock enforcement at the type level β
MIN_LOCK_DURATION(7 days) is asserted inlock_funds(), not just validated client-side - Multi-sig emergency governance β
REQUIRED_APPROVALS(3-of-5) quorum viaVecSet<address>guardian tracking, withE_ALREADY_APPROVEDpreventing duplicate-signature attacks - Access control on entry functions β
E_NOT_AUTHORIZEDguards restrict vault and SIP mutation to the owning address - Event-sourced audit trail β every execution/lock/unlock emits a Move event, giving
/api/activitya tamper-evident on-chain log rather than a mutable off-chain record - Environment-isolated secrets β Gemini API key and analytics ID are server-only/
NEXT_PUBLIC_-scoped appropriately; package/object IDs are network-specific per.env - See
SECURITY.mdfor the full vulnerability-disclosure policy and reporting process
π Live App Β· π¦ GitHub Β· π‘οΈ Report a Vulnerability
Built with Move, Next.js 15, and OneChain β automated systematic investing, secured on-chain.