From e545bb0f20318a8da24ad9a5cde24d6669f1d2d9 Mon Sep 17 00:00:00 2001 From: Edoka Isaac Date: Wed, 25 Mar 2026 00:07:07 +0100 Subject: [PATCH 1/2] added calculateAnchorFee function --- .gitignore | 4 +- package.json | 3 +- packages/anchor-service/dist/index.js | 76 ++++++++++++++++++++++++++ packages/anchor-service/dist/index.mjs | 51 +++++++++++++++++ packages/anchor-service/src/index.ts | 73 +++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 256abbb..055addd 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ coverage/ # TypeScript *.tsbuildinfo -target \ No newline at end of file +target + +issue.md \ No newline at end of file diff --git a/package.json b/package.json index a85cc22..8c0dba3 100644 --- a/package.json +++ b/package.json @@ -38,5 +38,6 @@ "turbo": "^1.13.4", "typescript": "^5.9.3", "typescript-eslint": "^8.56.1" - } + }, + "packageManager": "pnpm@10.30.2+sha512.36cdc707e7b7940a988c9c1ecf88d084f8514b5c3f085f53a2e244c2921d3b2545bc20dd4ebe1fc245feec463bb298aecea7a63ed1f7680b877dc6379d8d0cb4" } diff --git a/packages/anchor-service/dist/index.js b/packages/anchor-service/dist/index.js index 3918c74..73b9ec4 100644 --- a/packages/anchor-service/dist/index.js +++ b/packages/anchor-service/dist/index.js @@ -1 +1,77 @@ "use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/index.ts +var index_exports = {}; +__export(index_exports, { + TransactionType: () => TransactionType, + calculateAnchorFee: () => calculateAnchorFee +}); +module.exports = __toCommonJS(index_exports); +var TransactionType = /* @__PURE__ */ ((TransactionType2) => { + TransactionType2["DEPOSIT"] = "deposit"; + TransactionType2["WITHDRAWAL"] = "withdrawal"; + TransactionType2["TRANSFER"] = "transfer"; + return TransactionType2; +})(TransactionType || {}); +var FEE_SCHEDULES = { + USDC: { + ["deposit" /* DEPOSIT */]: { flatFee: 0.5, percentageFee: 1e-3 }, + ["withdrawal" /* WITHDRAWAL */]: { flatFee: 1, percentageFee: 2e-3 }, + ["transfer" /* TRANSFER */]: { flatFee: 0.25, percentageFee: 5e-4 } + }, + XLM: { + ["deposit" /* DEPOSIT */]: { flatFee: 0.01, percentageFee: 5e-4 }, + ["withdrawal" /* WITHDRAWAL */]: { flatFee: 0.05, percentageFee: 1e-3 }, + ["transfer" /* TRANSFER */]: { flatFee: 0.01, percentageFee: 5e-4 } + } +}; +var DEFAULT_FEE_SCHEDULE = { + flatFee: 1, + percentageFee: 5e-3 +}; +function toFixed(value, decimals = 7) { + return value.toFixed(decimals); +} +async function calculateAnchorFee(amount, asset, type) { + const parsedAmount = parseFloat(amount); + if (isNaN(parsedAmount) || parsedAmount < 0) { + throw new Error(`Invalid amount: ${amount}`); + } + const assetSchedules = FEE_SCHEDULES[asset.toUpperCase()]; + const schedule = (assetSchedules && assetSchedules[type]) ?? DEFAULT_FEE_SCHEDULE; + const flatFee = schedule.flatFee; + const percentageFee = parsedAmount * schedule.percentageFee; + const totalFee = flatFee + percentageFee; + const netAmount = Math.max(0, parsedAmount - totalFee); + return { + asset, + transactionType: type, + inputAmount: toFixed(parsedAmount), + flatFee: toFixed(flatFee), + percentageFee: toFixed(percentageFee), + totalFee: toFixed(totalFee), + netAmount: toFixed(netAmount) + }; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + TransactionType, + calculateAnchorFee +}); diff --git a/packages/anchor-service/dist/index.mjs b/packages/anchor-service/dist/index.mjs index e69de29..17e51be 100644 --- a/packages/anchor-service/dist/index.mjs +++ b/packages/anchor-service/dist/index.mjs @@ -0,0 +1,51 @@ +// src/index.ts +var TransactionType = /* @__PURE__ */ ((TransactionType2) => { + TransactionType2["DEPOSIT"] = "deposit"; + TransactionType2["WITHDRAWAL"] = "withdrawal"; + TransactionType2["TRANSFER"] = "transfer"; + return TransactionType2; +})(TransactionType || {}); +var FEE_SCHEDULES = { + USDC: { + ["deposit" /* DEPOSIT */]: { flatFee: 0.5, percentageFee: 1e-3 }, + ["withdrawal" /* WITHDRAWAL */]: { flatFee: 1, percentageFee: 2e-3 }, + ["transfer" /* TRANSFER */]: { flatFee: 0.25, percentageFee: 5e-4 } + }, + XLM: { + ["deposit" /* DEPOSIT */]: { flatFee: 0.01, percentageFee: 5e-4 }, + ["withdrawal" /* WITHDRAWAL */]: { flatFee: 0.05, percentageFee: 1e-3 }, + ["transfer" /* TRANSFER */]: { flatFee: 0.01, percentageFee: 5e-4 } + } +}; +var DEFAULT_FEE_SCHEDULE = { + flatFee: 1, + percentageFee: 5e-3 +}; +function toFixed(value, decimals = 7) { + return value.toFixed(decimals); +} +async function calculateAnchorFee(amount, asset, type) { + const parsedAmount = parseFloat(amount); + if (isNaN(parsedAmount) || parsedAmount < 0) { + throw new Error(`Invalid amount: ${amount}`); + } + const assetSchedules = FEE_SCHEDULES[asset.toUpperCase()]; + const schedule = (assetSchedules && assetSchedules[type]) ?? DEFAULT_FEE_SCHEDULE; + const flatFee = schedule.flatFee; + const percentageFee = parsedAmount * schedule.percentageFee; + const totalFee = flatFee + percentageFee; + const netAmount = Math.max(0, parsedAmount - totalFee); + return { + asset, + transactionType: type, + inputAmount: toFixed(parsedAmount), + flatFee: toFixed(flatFee), + percentageFee: toFixed(percentageFee), + totalFee: toFixed(totalFee), + netAmount: toFixed(netAmount) + }; +} +export { + TransactionType, + calculateAnchorFee +}; diff --git a/packages/anchor-service/src/index.ts b/packages/anchor-service/src/index.ts index e69de29..38e92f0 100644 --- a/packages/anchor-service/src/index.ts +++ b/packages/anchor-service/src/index.ts @@ -0,0 +1,73 @@ +export enum TransactionType { + DEPOSIT = 'deposit', + WITHDRAWAL = 'withdrawal', + TRANSFER = 'transfer', +} + +export interface FeeQuote { + asset: string; + transactionType: TransactionType; + inputAmount: string; + flatFee: string; + percentageFee: string; + totalFee: string; + netAmount: string; +} + +interface FeeSchedule { + flatFee: number; + percentageFee: number; // e.g. 0.01 = 1% +} + +type AssetFeeSchedules = Record>>; + +const FEE_SCHEDULES: AssetFeeSchedules = { + USDC: { + [TransactionType.DEPOSIT]: { flatFee: 0.5, percentageFee: 0.001 }, + [TransactionType.WITHDRAWAL]: { flatFee: 1.0, percentageFee: 0.002 }, + [TransactionType.TRANSFER]: { flatFee: 0.25, percentageFee: 0.0005 }, + }, + XLM: { + [TransactionType.DEPOSIT]: { flatFee: 0.01, percentageFee: 0.0005 }, + [TransactionType.WITHDRAWAL]: { flatFee: 0.05, percentageFee: 0.001 }, + [TransactionType.TRANSFER]: { flatFee: 0.01, percentageFee: 0.0005 }, + }, +}; + +const DEFAULT_FEE_SCHEDULE: FeeSchedule = { + flatFee: 1.0, + percentageFee: 0.005, +}; + +function toFixed(value: number, decimals = 7): string { + return value.toFixed(decimals); +} + +export async function calculateAnchorFee( + amount: string, + asset: string, + type: TransactionType, +): Promise { + const parsedAmount = parseFloat(amount); + if (isNaN(parsedAmount) || parsedAmount < 0) { + throw new Error(`Invalid amount: ${amount}`); + } + + const assetSchedules = FEE_SCHEDULES[asset.toUpperCase()]; + const schedule: FeeSchedule = (assetSchedules && assetSchedules[type]) ?? DEFAULT_FEE_SCHEDULE; + + const flatFee = schedule.flatFee; + const percentageFee = parsedAmount * schedule.percentageFee; + const totalFee = flatFee + percentageFee; + const netAmount = Math.max(0, parsedAmount - totalFee); + + return { + asset, + transactionType: type, + inputAmount: toFixed(parsedAmount), + flatFee: toFixed(flatFee), + percentageFee: toFixed(percentageFee), + totalFee: toFixed(totalFee), + netAmount: toFixed(netAmount), + }; +} From 72d50fc3afda44749313292bbaab2eedf9078a2c Mon Sep 17 00:00:00 2001 From: Edoka Isaac Date: Wed, 25 Mar 2026 00:21:17 +0100 Subject: [PATCH 2/2] ensured CI checks passed --- apps/frontend/src/app/checkout/page.tsx | 4 +- apps/frontend/src/app/dashboard/page.tsx | 156 ++++++++++++----------- 2 files changed, 84 insertions(+), 76 deletions(-) diff --git a/apps/frontend/src/app/checkout/page.tsx b/apps/frontend/src/app/checkout/page.tsx index 98dbd5b..95cb110 100644 --- a/apps/frontend/src/app/checkout/page.tsx +++ b/apps/frontend/src/app/checkout/page.tsx @@ -511,7 +511,7 @@ export default function PaymentCheckout() { onClick={handleBankPayment} className="w-full bg-white hover:bg-zinc-100 text-black h-14 rounded-xl mt-6 text-base transition-all duration-200 shadow-lg shadow-white/10" > - I've Completed the Transfer + I've Completed the Transfer

@@ -611,7 +611,7 @@ export default function PaymentCheckout() { onClick={handleCryptoDetection} className="w-full bg-white hover:bg-zinc-100 text-black h-14 rounded-xl mt-6 text-base transition-all duration-200 shadow-lg shadow-white/10" > - I've Sent the Payment + I've Sent the Payment

diff --git a/apps/frontend/src/app/dashboard/page.tsx b/apps/frontend/src/app/dashboard/page.tsx index 68a7042..2d6cbe7 100644 --- a/apps/frontend/src/app/dashboard/page.tsx +++ b/apps/frontend/src/app/dashboard/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { motion } from "motion/react"; +import { motion } from 'motion/react'; import { TrendingUp, TrendingDown, @@ -10,90 +10,96 @@ import { Clock, CheckCircle2, AlertCircle, -} from "lucide-react"; +} from 'lucide-react'; const stats = [ { - label: "Total Volume (30d)", - value: "$12,847,392.45", - change: "+18.2%", - trend: "up", + label: 'Total Volume (30d)', + value: '$12,847,392.45', + change: '+18.2%', + trend: 'up', icon: DollarSign, }, { - label: "Settlement Balance", - value: "$2,103,482.12", - change: "+5.4%", - trend: "up", + label: 'Settlement Balance', + value: '$2,103,482.12', + change: '+5.4%', + trend: 'up', icon: Activity, }, { - label: "Pending Settlements", - value: "47", - change: "-12.3%", - trend: "down", + label: 'Pending Settlements', + value: '47', + change: '-12.3%', + trend: 'down', icon: Clock, }, { - label: "Reserve Ratio", - value: "127.3%", - change: "+2.1%", - trend: "up", + label: 'Reserve Ratio', + value: '127.3%', + change: '+2.1%', + trend: 'up', icon: CheckCircle2, }, ]; const assets = [ - { symbol: "sUSDC", balance: "1,245,382.45", usd: "1,245,382.45", change: "+2.3%" }, - { symbol: "sBTC", balance: "12.4583", usd: "625,847.92", change: "+5.1%" }, - { symbol: "sETH", balance: "145.2341", usd: "232,251.75", change: "-1.2%" }, + { + symbol: 'sUSDC', + balance: '1,245,382.45', + usd: '1,245,382.45', + change: '+2.3%', + barWidth: '82%', + }, + { symbol: 'sBTC', balance: '12.4583', usd: '625,847.92', change: '+5.1%', barWidth: '73%' }, + { symbol: 'sETH', balance: '145.2341', usd: '232,251.75', change: '-1.2%', barWidth: '67%' }, ]; const transactions = [ { - id: "pay_9k2j3n4k5j6h", - type: "Payment", - asset: "sUSDC", - amount: "+12,450.00", - status: "completed", - time: "2m ago", - hash: "0x7a8f9b2c...4e5d6f1a", + id: 'pay_9k2j3n4k5j6h', + type: 'Payment', + asset: 'sUSDC', + amount: '+12,450.00', + status: 'completed', + time: '2m ago', + hash: '0x7a8f9b2c...4e5d6f1a', }, { - id: "pay_8h1j2k3l4m5n", - type: "Redemption", - asset: "sBTC", - amount: "-0.2341", - status: "completed", - time: "5m ago", - hash: "0x3c4d5e6f...7a8b9c0d", + id: 'pay_8h1j2k3l4m5n', + type: 'Redemption', + asset: 'sBTC', + amount: '-0.2341', + status: 'completed', + time: '5m ago', + hash: '0x3c4d5e6f...7a8b9c0d', }, { - id: "pay_7g8h9i0j1k2l", - type: "Payment", - asset: "sETH", - amount: "+5.4321", - status: "pending", - time: "8m ago", - hash: "0x1a2b3c4d...5e6f7g8h", + id: 'pay_7g8h9i0j1k2l', + type: 'Payment', + asset: 'sETH', + amount: '+5.4321', + status: 'pending', + time: '8m ago', + hash: '0x1a2b3c4d...5e6f7g8h', }, { - id: "pay_6f7g8h9i0j1k", - type: "Settlement", - asset: "sUSDC", - amount: "-8,230.50", - status: "completed", - time: "12m ago", - hash: "0x9h8g7f6e...5d4c3b2a", + id: 'pay_6f7g8h9i0j1k', + type: 'Settlement', + asset: 'sUSDC', + amount: '-8,230.50', + status: 'completed', + time: '12m ago', + hash: '0x9h8g7f6e...5d4c3b2a', }, { - id: "pay_5e6f7g8h9i0j", - type: "Payment", - asset: "sBTC", - amount: "+0.1234", - status: "completed", - time: "15m ago", - hash: "0x2b3c4d5e...6f7g8h9i", + id: 'pay_5e6f7g8h9i0j', + type: 'Payment', + asset: 'sBTC', + amount: '+0.1234', + status: 'completed', + time: '15m ago', + hash: '0x2b3c4d5e...6f7g8h9i', }, ]; @@ -109,9 +115,7 @@ export default function OverviewPage() { > Overview -

- Real-time metrics and settlement status -

+

Real-time metrics and settlement status

{/* Stats Grid */} @@ -136,17 +140,17 @@ export default function OverviewPage() { repeatDelay: 2, }} /> - +
- {stat.trend === "up" ? ( + {stat.trend === 'up' ? ( ) : ( @@ -154,7 +158,7 @@ export default function OverviewPage() { {stat.change}
- +
{stat.value}
{stat.label}
@@ -200,7 +204,9 @@ export default function OverviewPage() {
${asset.usd}
-
+
{asset.change}
@@ -211,7 +217,7 @@ export default function OverviewPage() {
@@ -228,7 +234,7 @@ export default function OverviewPage() { transition={{ delay: 0.3 }} >

Reserve Health

- +
{/* Background circle */} @@ -249,9 +255,9 @@ export default function OverviewPage() { stroke="rgba(255,255,255,0.3)" strokeWidth="12" strokeLinecap="round" - initial={{ strokeDasharray: "0 440" }} - animate={{ strokeDasharray: "350 440" }} - transition={{ duration: 1.5, ease: "easeOut" }} + initial={{ strokeDasharray: '0 440' }} + animate={{ strokeDasharray: '350 440' }} + transition={{ duration: 1.5, ease: 'easeOut' }} />
@@ -336,18 +342,20 @@ export default function OverviewPage() { {tx.asset} - + {tx.amount} - {tx.status === "completed" ? ( + {tx.status === 'completed' ? ( ) : (