diff --git a/apps/machine/electron/main.ts b/apps/machine/electron/main.ts index ac55672..d6ad834 100644 --- a/apps/machine/electron/main.ts +++ b/apps/machine/electron/main.ts @@ -30,6 +30,12 @@ import { initializeHal, type HalInstance } from './hal-service.js' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) +/** Parse fee value: accepts percentage (5.55 → 0.0555) or decimal (0.0555 → 0.0555) */ +function parseFee(val: string): number { + const n = parseFloat(val) + return n >= 1 ? n / 100 : n +} + // Load .env file manually (Electron main process doesn't have Vite's env loading) function loadEnvFile() { const envPath = path.join(__dirname, '..', '.env') @@ -190,6 +196,10 @@ ipcMain.handle('get-config', () => { // Maintenance mode — show "out of service" screen maintenanceMode: process.env.VITE_MAINTENANCE_MODE === 'true', + + // Fee rates — accepts percentage (5.55) or decimal (0.0555), auto-detected + cashInFeeRate: parseFee(process.env.VITE_CASH_IN_FEE || '0.0333'), + cashOutFeeRate: parseFee(process.env.VITE_CASH_OUT_FEE || '0.0777'), } }) diff --git a/apps/machine/src/stores/atm.ts b/apps/machine/src/stores/atm.ts index 3b6ac50..b96b538 100644 --- a/apps/machine/src/stores/atm.ts +++ b/apps/machine/src/stores/atm.ts @@ -260,6 +260,8 @@ export const useAtmStore = defineStore('atm', () => { const allowMockFallback = ref(true) // default true for browser dev const initError = ref(null) // fatal error → maintenance screen const fiatCode = ref('USD') + const cashInFeeRate = ref(0.0333) + const cashOutFeeRate = ref(0.0777) const machineModel = ref('atm') const useLiveServices = ref(false) const connectionStatus = ref<'disconnected' | 'connecting' | 'connected' | 'error'>( @@ -359,7 +361,11 @@ export const useAtmStore = defineStore('atm', () => { // Actions function initialize(services: ATMServices = mockServices) { atmServicesRef = services - const machine = createATMMachine(services, { currency: fiatCode.value }) + const machine = createATMMachine(services, { + currency: fiatCode.value, + cashInFeeRate: cashInFeeRate.value, + cashOutFeeRate: cashOutFeeRate.value, + }) actor.value = createActor(machine) // Track previous state to detect transitions into 'complete' @@ -925,6 +931,9 @@ export const useAtmStore = defineStore('atm', () => { machineModel.value = model const runtimeFiatCode = runtimeConfig.fiatCode || 'USD' fiatCode.value = runtimeFiatCode + if (runtimeConfig.cashInFeeRate !== undefined) cashInFeeRate.value = runtimeConfig.cashInFeeRate + if (runtimeConfig.cashOutFeeRate !== undefined) + cashOutFeeRate.value = runtimeConfig.cashOutFeeRate // Build device config from runtime values const { getDeviceConfig, toHalConfig, MACHINE_PRESETS } = await import('@/config') diff --git a/apps/machine/src/types/electron.d.ts b/apps/machine/src/types/electron.d.ts index c0c8de6..ca1c854 100644 --- a/apps/machine/src/types/electron.d.ts +++ b/apps/machine/src/types/electron.d.ts @@ -16,6 +16,8 @@ export interface RuntimeConfig { allowMockFallback: boolean operatorPubkeys: string maintenanceMode: boolean + cashInFeeRate: number + cashOutFeeRate: number } export interface AtmSecrets { diff --git a/packages/state-machine/src/machine.ts b/packages/state-machine/src/machine.ts index 3f7e438..ebb3916 100644 --- a/packages/state-machine/src/machine.ts +++ b/packages/state-machine/src/machine.ts @@ -20,7 +20,7 @@ import { */ export function createATMMachine( services: Partial = {}, - options?: { currency?: string } + options?: { currency?: string; cashInFeeRate?: number; cashOutFeeRate?: number } ) { return setup({ types: { @@ -401,7 +401,14 @@ export function createATMMachine( }).createMachine({ id: 'atm', initial: 'idle', - context: { ...initialContext, ...(options?.currency ? { currency: options.currency } : {}) }, + context: { + ...initialContext, + ...(options?.currency ? { currency: options.currency } : {}), + ...(options?.cashInFeeRate !== undefined ? { cashInFeePercent: options.cashInFeeRate } : {}), + ...(options?.cashOutFeeRate !== undefined + ? { cashOutFeePercent: options.cashOutFeeRate } + : {}), + }, states: { idle: { entry: 'resetContext',