diff --git a/Aframp/README.md b/Aframp/README.md
index a607a02..428ea30 100644
--- a/Aframp/README.md
+++ b/Aframp/README.md
@@ -37,6 +37,61 @@ Aframp/
---
+## 🧩 Components
+
+### TransactionStatusCard
+
+A reusable React component for displaying transaction states across the AFRAMP application.
+
+#### Props
+
+```typescript
+interface TransactionStatusCardProps {
+ type: 'onramp' | 'offramp' | 'payment' | 'swap';
+ status: 'pending' | 'confirming' | 'completed' | 'failed';
+ amount: string;
+ asset: string;
+ timestamp: Date;
+ txHash?: string;
+ chain?: 'stellar' | 'ethereum' | 'polygon' | 'base';
+ confirmations?: number;
+ errorMessage?: string;
+ onRetry?: () => void;
+}
+```
+
+#### Usage
+
+```tsx
+import { TransactionStatusCard } from '@/components/TransactionStatusCard';
+
+
+```
+
+#### Features
+
+- **Status Visualization**: Color-coded badges with icons for pending (yellow), confirming (blue), completed (green), failed (red)
+- **Transaction Types**: Icons for onramp (⬇️), offramp (⬆️), payment (💳), swap (🔄)
+- **Blockchain Links**: Direct links to explorers for Stellar, Ethereum, Polygon, and Base networks
+- **Responsive Design**: Mobile-first design that works on all screen sizes
+- **Accessibility**: Proper ARIA labels and keyboard navigation
+- **Error Handling**: Displays error messages and retry buttons for failed transactions
+
+#### Demo
+
+View examples of all transaction states at `/examples/transaction-status`.
+
+---
+
## 🚀 Development Setup
Follow these instructions to get a local copy of the AFRAMP frontend up and running.
diff --git a/Aframp/app/examples/transaction-status/page.tsx b/Aframp/app/examples/transaction-status/page.tsx
new file mode 100644
index 0000000..56c5e18
--- /dev/null
+++ b/Aframp/app/examples/transaction-status/page.tsx
@@ -0,0 +1,78 @@
+'use client';
+
+import React from 'react';
+import { TransactionStatusCard } from '@/components/TransactionStatusCard';
+
+const examples = [
+ {
+ type: 'swap' as const,
+ status: 'confirming' as const,
+ amount: '1,500',
+ asset: 'cNGN → 2.5 USDC',
+ timestamp: new Date(Date.now() - 3 * 60 * 1000), // 3 minutes ago
+ txHash: 'example-tx-hash',
+ chain: 'stellar' as const,
+ confirmations: 8,
+ },
+ {
+ type: 'onramp' as const,
+ status: 'pending' as const,
+ amount: '500',
+ asset: 'USDC',
+ timestamp: new Date(Date.now() - 2 * 60 * 1000),
+ },
+ {
+ type: 'offramp' as const,
+ status: 'completed' as const,
+ amount: '1,000',
+ asset: 'cNGN',
+ timestamp: new Date(Date.now() - 10 * 60 * 1000),
+ txHash: 'completed-tx-hash',
+ chain: 'ethereum' as const,
+ },
+ {
+ type: 'payment' as const,
+ status: 'failed' as const,
+ amount: '250',
+ asset: 'USDC',
+ timestamp: new Date(Date.now() - 5 * 60 * 1000),
+ errorMessage: 'Insufficient funds',
+ onRetry: () => alert('Retry clicked'),
+ },
+ {
+ type: 'swap' as const,
+ status: 'confirming' as const,
+ amount: '0.5',
+ asset: 'ETH → 1,800 USDC',
+ timestamp: new Date(Date.now() - 1 * 60 * 1000),
+ txHash: 'polygon-tx-hash',
+ chain: 'polygon' as const,
+ confirmations: 5,
+ },
+ {
+ type: 'onramp' as const,
+ status: 'completed' as const,
+ amount: '2,000',
+ asset: 'cNGN',
+ timestamp: new Date(Date.now() - 15 * 60 * 1000),
+ txHash: 'base-tx-hash',
+ chain: 'base' as const,
+ },
+];
+
+export default function TransactionStatusExamples() {
+ return (
+
+
+
+ Transaction Status Card Examples
+
+
+ {examples.map((example, index) => (
+
+ ))}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/Aframp/components/TransactionStatusCard.tsx b/Aframp/components/TransactionStatusCard.tsx
new file mode 100644
index 0000000..ff8f01d
--- /dev/null
+++ b/Aframp/components/TransactionStatusCard.tsx
@@ -0,0 +1,168 @@
+import React from 'react';
+import {
+ ArrowDown,
+ ArrowUp,
+ CreditCard,
+ RefreshCw,
+ Loader,
+ Clock,
+ CheckCircle,
+ XCircle,
+ ExternalLink,
+} from 'lucide-react';
+import { formatDistanceToNow } from 'date-fns';
+import { cn } from '@/lib/utils';
+
+interface TransactionStatusCardProps {
+ type: 'onramp' | 'offramp' | 'payment' | 'swap';
+ status: 'pending' | 'confirming' | 'completed' | 'failed';
+ amount: string;
+ asset: string;
+ timestamp: Date;
+ txHash?: string;
+ chain?: 'stellar' | 'ethereum' | 'polygon' | 'base';
+ confirmations?: number;
+ errorMessage?: string;
+ onRetry?: () => void;
+ key?: React.Key;
+}
+
+const getTypeIcon = (type: TransactionStatusCardProps['type']) => {
+ switch (type) {
+ case 'onramp':
+ return ArrowDown;
+ case 'offramp':
+ return ArrowUp;
+ case 'payment':
+ return CreditCard;
+ case 'swap':
+ return RefreshCw;
+ default:
+ return RefreshCw;
+ }
+};
+
+const getStatusIcon = (status: TransactionStatusCardProps['status']) => {
+ switch (status) {
+ case 'pending':
+ return Loader;
+ case 'confirming':
+ return Clock;
+ case 'completed':
+ return CheckCircle;
+ case 'failed':
+ return XCircle;
+ default:
+ return Clock;
+ }
+};
+
+const getStatusColor = (status: TransactionStatusCardProps['status']) => {
+ switch (status) {
+ case 'pending':
+ return 'text-yellow-500 bg-yellow-50 dark:bg-yellow-950/20';
+ case 'confirming':
+ return 'text-blue-500 bg-blue-50 dark:bg-blue-950/20';
+ case 'completed':
+ return 'text-green-500 bg-green-50 dark:bg-green-950/20';
+ case 'failed':
+ return 'text-red-500 bg-red-50 dark:bg-red-950/20';
+ default:
+ return 'text-gray-500 bg-gray-50 dark:bg-gray-950/20';
+ }
+};
+
+const getExplorerUrl = (chain: TransactionStatusCardProps['chain'], txHash: string) => {
+ switch (chain) {
+ case 'stellar':
+ return `https://stellar.expert/explorer/public/tx/${txHash}`;
+ case 'ethereum':
+ return `https://etherscan.io/tx/${txHash}`;
+ case 'polygon':
+ return `https://polygonscan.com/tx/${txHash}`;
+ case 'base':
+ return `https://basescan.org/tx/${txHash}`;
+ default:
+ return '';
+ }
+};
+
+export const TransactionStatusCard = ({
+ type,
+ status,
+ amount,
+ asset,
+ timestamp,
+ txHash,
+ chain,
+ confirmations,
+ errorMessage,
+ onRetry,
+}: TransactionStatusCardProps) => {
+ const TypeIcon = getTypeIcon(type);
+ const StatusIcon = getStatusIcon(status);
+ const statusColor = getStatusColor(status);
+ const explorerUrl = txHash && chain ? getExplorerUrl(chain, txHash) : '';
+
+ const capitalizedChain = chain ? chain.charAt(0).toUpperCase() + chain.slice(1) : '';
+
+ return (
+
+
+
+
+ {type}
+
+
+
+ {status}
+
+
+
+
+
+ {amount} {asset}
+
+
+ {formatDistanceToNow(timestamp, { addSuffix: true })}
+
+
+
+ {status === 'confirming' && confirmations !== undefined && (
+
+ Confirmations: {confirmations}/12
+
+ )}
+
+ {status === 'failed' && errorMessage && (
+
+ {errorMessage}
+
+ )}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/Aframp/package.json b/Aframp/package.json
index da29be1..b9f9981 100644
--- a/Aframp/package.json
+++ b/Aframp/package.json
@@ -39,6 +39,7 @@
"@radix-ui/react-toggle": "1.1.1",
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
+ "@stellar/freighter-api": "^6.0.1",
"@vercel/analytics": "1.3.1",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.1",
diff --git a/Aframp/pnpm-lock.yaml b/Aframp/pnpm-lock.yaml
index 03ae600..1f426a6 100644
--- a/Aframp/pnpm-lock.yaml
+++ b/Aframp/pnpm-lock.yaml
@@ -98,6 +98,9 @@ importers:
'@radix-ui/react-tooltip':
specifier: 1.1.6
version: 1.1.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@stellar/freighter-api':
+ specifier: ^6.0.1
+ version: 6.0.1
'@vercel/analytics':
specifier: 1.3.1
version: 1.3.1(next@16.0.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)
@@ -1110,6 +1113,9 @@ packages:
'@radix-ui/rect@1.1.0':
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
+ '@stellar/freighter-api@6.0.1':
+ resolution: {integrity: sha512-eqwakEqSg+zoLuPpSbKyrX0pG8DQFzL/J5GtbfuMCmJI+h+oiC9pQ5C6QLc80xopZQKdGt8dUAFCmDMNdAG95w==}
+
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@@ -1267,6 +1273,9 @@ packages:
peerDependencies:
postcss: ^8.1.0
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
baseline-browser-mapping@2.9.14:
resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==}
hasBin: true
@@ -1276,6 +1285,9 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ buffer@6.0.3:
+ resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+
c12@3.3.3:
resolution: {integrity: sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==}
peerDependencies:
@@ -1477,6 +1489,9 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
ignore@7.0.5:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'}
@@ -1800,6 +1815,11 @@ packages:
scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+ semver@7.7.1:
+ resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
@@ -2851,6 +2871,11 @@ snapshots:
'@radix-ui/rect@1.1.0': {}
+ '@stellar/freighter-api@6.0.1':
+ dependencies:
+ buffer: 6.0.3
+ semver: 7.7.1
+
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
@@ -2988,6 +3013,8 @@ snapshots:
postcss: 8.5.0
postcss-value-parser: 4.2.0
+ base64-js@1.5.1: {}
+
baseline-browser-mapping@2.9.14: {}
browserslist@4.28.1:
@@ -2998,6 +3025,11 @@ snapshots:
node-releases: 2.0.27
update-browserslist-db: 1.2.3(browserslist@4.28.1)
+ buffer@6.0.3:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
c12@3.3.3:
dependencies:
chokidar: 5.0.0
@@ -3174,6 +3206,8 @@ snapshots:
graceful-fs@4.2.11: {}
+ ieee754@1.2.1: {}
+
ignore@7.0.5: {}
input-otp@1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
@@ -3461,6 +3495,8 @@ snapshots:
scule@1.3.0: {}
+ semver@7.7.1: {}
+
semver@7.7.3: {}
server-only@0.0.1: {}