Skip to content

Commit 7262d61

Browse files
authored
Add mobile vault multi lock list (#380)
* Closes #149(Add mobile failed payment recovery guidance) * Closes #261(Add mobile vault multi-lock list)
1 parent 861d6fa commit 7262d61

8 files changed

Lines changed: 502 additions & 4 deletions

File tree

app/(tabs)/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useWalletStore } from '../../src/store/walletStore';
55
import { SIZES, RADIUS, ThemeColors } from '../../src/constants/theme';
66
import { useTheme } from '../../src/hooks/useTheme';
77
import { Button } from '../../src/components/Button';
8+
import { FundButton } from '../../src/components/FundButton';
89
import { TransactionListItem } from '../../src/components/TransactionListItem';
910
import { NetworkStateBanner } from '../../src/components/NetworkStateBanner';
1011
import { WalletEmptyState } from '../../src/components/WalletEmptyState';

app/(tabs)/vault.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ export default function VaultScreen() {
102102
checkIntro();
103103
}, []);
104104

105+
// ---- Multi-lock state (placeholder data until contract integration) ----
106+
const [locks, setLocks] = useState<VaultLock[]>([]);
107+
const [isLoadingLocks, setIsLoadingLocks] = useState(true);
108+
const [locksError, setLocksError] = useState<string | null>(null);
109+
105110
useEffect(() => {
106111
if (isAvailable && publicKey) {
107112
loadBalance(publicKey);

docs/multi-lock.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Multi-Lock Vault Feature
2+
3+
## Overview
4+
5+
The multi-lock list displays all time-locked deposits associated with the user's vault account. Each lock represents an independent deposit with its own amount, unlock date, and status. This aligns with the future Soroban contract direction supporting multiple locks per user.
6+
7+
## Component: `MultiLockList`
8+
9+
**Location:** `src/components/MultiLockList.tsx`
10+
11+
### Props
12+
13+
| Prop | Type | Description |
14+
|---|---|---|
15+
| `locks` | `VaultLock[]` | Array of lock objects to render |
16+
| `isLoading` | `boolean` | Shows an `ActivityIndicator` spinner |
17+
| `error` | `string \| null` | Displays error message with retry button |
18+
| `onWithdraw` | `(lock: VaultLock) => void` | Called when user taps withdraw on a matured lock |
19+
| `onRetry` | `() => void` | Called when user taps retry after an error |
20+
| `isWithdrawing` | `boolean` | Disables withdraw buttons during submission |
21+
22+
### States Handled
23+
24+
| State | Visual |
25+
|---|---|
26+
| **Loading** | Spinner + "Loading locks…" |
27+
| **Error** | Error icon + message + retry button |
28+
| **Empty** | Lock icon + "No Locks Yet" + guidance text |
29+
| **Populated** | List of lock cards |
30+
31+
### Lock Status Display
32+
33+
| Status | Icon | Color | Action |
34+
|---|---|---|---|
35+
| `locked` | `Clock` | Warning (amber) | None (immature) |
36+
| `matured` | `Unlock` | Success (green) | "Withdraw" button |
37+
| `withdrawn` | `CheckCircle2` | Muted (grey) | None (completed) |
38+
39+
## Type: `VaultLock`
40+
41+
**Location:** `src/types/vault.ts`
42+
43+
```ts
44+
interface VaultLock {
45+
id: string; // Unique lock identifier
46+
amount: string; // XLM amount, 7-decimal string (e.g. "500.0000000")
47+
unlockDate: string; // ISO-8601 date when lock matures
48+
status: LockStatus; // 'locked' | 'matured' | 'withdrawn'
49+
txHash?: string; // Optional creation transaction hash
50+
}
51+
```
52+
53+
## Placeholder Data
54+
55+
**Location:** `tests/fixtures/vault.ts`
56+
57+
Until the Soroban contract's multi-lock interface is ready, the vault screen uses hardcoded fixtures:
58+
59+
```ts
60+
vaultLockFixtures.all // 3 locks: locked, matured, withdrawn
61+
vaultLockFixtures.locked // 2 immature locks
62+
vaultLockFixtures.matured // 2 matured locks
63+
vaultLockFixtures.empty // []
64+
```
65+
66+
The unlock dates are computed dynamically (e.g., 30 days from now, 7 days ago) so the fixtures always show a realistic mix of statuses.
67+
68+
## Integration Roadmap
69+
70+
1. **Done**`MultiLockList` component with loading/empty/error states
71+
2. **Done** — Placeholder fixtures for UI development
72+
3. **TODO** — Add `fetchLocks(publicKey)` to `src/services/vault.ts` that calls the Soroban contract's lock registry
73+
4. **TODO** — Add lock state to `src/store/vaultStore.ts` (Zustand slice)
74+
5. **TODO** — Implement `withdrawLock(secretKey, lockId)` contract call
75+
6. **TODO** — Replace fixture data in `app/(tabs)/vault.tsx` with real store data
76+
77+
## Contract Interface (Expected)
78+
79+
The Soroban vault contract is expected to expose:
80+
81+
```
82+
get_locks(owner: Address) -> Vec<Lock>
83+
withdraw_lock(owner: Address, lock_index: u32) -> i128
84+
```
85+
86+
Where `Lock` is a struct containing `amount: i128`, `unlock_time: u64`, and `withdrawn: bool`.

docs/screen-inventory.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ This document provides a quick map of the main screens and routes in the Stellar
4747

4848
## `app/(tabs)/vault.tsx`
4949
* **Route Name:** `/(tabs)/vault`
50-
* **Purpose:** A placeholder UI for a Soroban Savings Vault integration. Simulates depositing and withdrawing.
51-
* **Key Components:** `Input`, `Button`
52-
* **State Dependencies:** `useWalletStore` (publicKey, getSecretKey)
50+
* **Purpose:** Soroban Savings Vault UI. Shows vault balance, deposit/withdraw form, and a multi-lock list of time-locked positions.
51+
* **Key Components:** `Input`, `Button`, `MultiLockList`
52+
* **State Dependencies:** `useWalletStore` (publicKey, getSecretKey), `useVaultStore` (balance, isConfigured, contractId, loadBalance, deposit, withdraw)
5353
* **Related SDK Calls:** `mockFetchVaultBalance`, `mockDepositToVault`, `mockWithdrawFromVault` (from `src/services/stellar`)
54+
* **Placeholder Data:** Lock fixtures from `tests/fixtures/vault.ts` simulate three lock statuses (locked, matured, withdrawn). See `docs/multi-lock.md` for integration roadmap.

0 commit comments

Comments
 (0)