Skip to main content

MTransactionCard

A card component that displays transaction details including amount, fiat value, and price change.

Props

fiat
SupportedFiatCurrencies
required
The fiat currency to display
transaction
Transaction
required
Transaction object containing type, sats, timestamp, note, and fiatSnapshot
currentFiatPrice
number
required
Current fiat price per satoshi for calculating price change
first
boolean
default:"false"
Whether this is the first card in a list (adds top border radius)
last
boolean
default:"false"
Whether this is the last card in a list (adds bottom border radius)

Transaction Type

type Transaction = {
  type: 'in' | 'out'
  sats: number
  timestamp: number
  note?: string
  fiatSnapshot?: Record<SupportedFiatCurrencies, number>
}

Usage

import MTransactionCard from '@/components/MTransactionCard'

const transaction = {
  type: 'in',
  sats: 50000,
  timestamp: 1709481600000,
  note: 'Payment received',
  fiatSnapshot: { USD: 15.00 }
}

<MTransactionCard
  fiat="USD"
  transaction={transaction}
  currentFiatPrice={15.50}
  first={true}
  onPress={() => showTransactionDetails(transaction)}
/>

Features

  • Inbound/Outbound icons based on transaction type
  • Amount display with +/- prefix
  • Fiat snapshot with current price comparison
  • Percentage change calculation
  • Relative time display
  • Optional transaction notes
  • Configurable low-value percentage display
  • Border separators except on last item
The component calculates and displays the percentage change between the fiat snapshot price and current price:
const percentageChange = number.getPercentageChange(
  transaction.fiatSnapshot[fiat],
  currentFiatPrice
)
Low-value changes (< 0.01) can be hidden based on user settings via displayLowValuePercentage.

MQRCode

A QR code component with loading states, placeholder, and clipboard copy functionality.

Props

value
string
required
The data to encode in the QR code (e.g., Lightning invoice)
size
number
default:"280"
Size of the QR code in pixels (width and height)
ecl
'H' | 'Q' | 'M' | 'L'
default:"'M'"
Error correction level. Higher levels provide more error tolerance but increase QR code density.
loading
boolean
Whether to display loading animation
placeholder
boolean
Whether to display placeholder state with prompt message

Usage

import MQRCode from '@/components/MQRCode'

// Active QR code
<MQRCode 
  value="lnbc50n1..." 
  size={300}
  ecl="M"
/>

// Loading state
<MQRCode 
  value="" 
  loading={true}
/>

// Placeholder state
<MQRCode 
  value="" 
  placeholder={true}
/>

Features

  • Tap to copy QR code value to clipboard
  • Animated loading state with pulsing effect
  • Placeholder state with decorative corners
  • Embedded Medusa logo in QR code center
  • Configurable error correction level
  • Dark theme optimized colors
The component handles three states:
  1. Active: Displays QR code with logo, allows clipboard copy
  2. Loading: Pulsing gray rectangle using Reanimated
  3. Placeholder: Decorative border with “Add amount to generate” message
The loading animation uses react-native-reanimated for smooth 60fps performance:
opacity.value = withRepeat(
  withTiming(0.4, { duration: 1000, easing: Easing.linear }),
  -1,
  true
)

MAmountDisplay

A component for displaying amounts with Bitcoin/fiat toggle and optional max button.

Props

type
'btc' | 'fiat'
default:"'btc'"
The current display type
sats
number
required
Amount in satoshis
fiat
number
Optional fiat amount (calculated from rate if not provided)
fiatCurrency
SupportedFiatCurrencies
required
The fiat currency to display
rate
number
required
Exchange rate for converting sats to fiat
withMax
boolean
default:"false"
Whether to show the MAX button
onPressMax
() => void
Callback when MAX button is pressed
onChangeType
(type: MAmountDisplayType) => void
Callback when the display type is toggled

Usage

import MAmountDisplay from '@/components/MAmountDisplay'

const [displayType, setDisplayType] = useState<'btc' | 'fiat'>('btc')

<MAmountDisplay
  type={displayType}
  sats={100000}
  fiatCurrency="USD"
  rate={30000}
  withMax={true}
  onPressMax={() => setAmount(walletBalance)}
  onChangeType={setDisplayType}
/>

Features

  • Toggle between Bitcoin and fiat display
  • Large primary amount with smaller secondary amount
  • Optional MAX button for quick balance input
  • Swap icon button for toggling display type
  • Respects user’s Bitcoin unit preference (sats/BTC)
  • Automatic fiat conversion
The component shows different layouts based on the type:BTC Mode:
1,000,000 sats (large)
$30.00 (small)
Fiat Mode:
$30.00 (large)
1,000,000 sats (small)
The swap button allows users to toggle between modes, useful during payment flows.

MButton

The primary button component with multiple variants and loading states.

Props

text
string
required
Button label text
variant
'default' | 'danger' | 'muted' | 'ghost'
default:"'default'"
Button style variant
size
'default' | 'small'
default:"'default'"
Button size
textSize
'default' | 'small'
default:"'default'"
Text size within the button
loading
boolean
Whether to show loading spinner
disabled
boolean
Whether the button is disabled

Usage

import MButton from '@/components/MButton'

// Default button
<MButton 
  text="Send Payment" 
  onPress={handleSend}
/>

// Loading state
<MButton 
  text="Processing..." 
  loading={true}
/>

// Danger variant
<MButton 
  text="Delete Wallet" 
  variant="danger"
  onPress={handleDelete}
/>

// Small muted button
<MButton 
  text="Cancel" 
  variant="muted"
  size="small"
  onPress={handleCancel}
/>

Variants

  • default: Bitcoin orange background (Colors.bitcoin)
  • muted: Gray background for secondary actions
  • ghost: Subtle background with gray text
  • danger: Red text for destructive actions

Features

  • Loading state with activity indicator
  • Disabled state with reduced opacity
  • Multiple size options
  • Haptic feedback on press
  • Memoized for performance
  • Inherits all TouchableHighlight props
When loading is true:
  1. Text becomes invisible but maintains layout space
  2. Activity indicator appears in absolute position
  3. activeOpacity is set to 0 to prevent visual feedback
  4. Button remains in “pressed” state visually
This prevents layout shift during async operations.

MPinInput

A visual PIN input display with animated icons for each digit.

Props

filledCharacter
number | null
required
Number of filled PIN digits (0-4)

Usage

import MPinInput from '@/components/MPinInput'

const [pinLength, setPinLength] = useState(0)

<MPinInput filledCharacter={pinLength} />

Features

  • 4 icon slots (Jellyfish, Fishbone, Speedboat, Bitcoin)
  • Visual feedback as PIN is entered
  • No actual text display for security
  • Consistent with PIN_SIZE configuration
  • Color changes based on filled state
The component displays icons instead of dots or numbers for enhanced privacy. Each icon represents a filled digit without revealing the actual PIN value.The PIN size is configured via PIN_SIZE constant from @/config/auth.

MNumPad

A numeric keypad with support for different input types and biometric authentication.

Props

type
'btc' | 'fiat' | 'sats' | 'auth' | 'pin'
default:"'sats'"
The type of input, determines available keys
onKeyPress
(key: Keys) => void
required
Callback when a key is pressed

Keys Type

type Keys = 
  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 
  | 'DEL' 
  | ',' 
  | 'BIOMETRIC'

Usage

import MNumPad from '@/components/MNumPad'

// Amount input
<MNumPad 
  type="btc"
  onKeyPress={(key) => handleKeyPress(key)}
/>

// PIN entry with biometric
<MNumPad 
  type="auth"
  onKeyPress={(key) => handleAuthKey(key)}
/>

Type Behavior

  • btc/fiat: Includes comma key for decimal input
  • sats: Numbers 0-9 and DEL only
  • pin: Numbers 0-9 and DEL only
  • auth: Numbers 0-9, DEL, and biometric button (if available)

Features

  • Automatic biometric detection (FaceID/Fingerprint)
  • Haptic feedback on key press
  • Consistent 3x4 grid layout
  • Conditional bottom row based on type
  • Support for multiple authentication types
The component automatically detects available biometric authentication:
const supportedBiometricTypes = 
  await LocalAuthentication.supportedAuthenticationTypesAsync()
const enrolledLevel = 
  await LocalAuthentication.getEnrolledLevelAsync()
Biometric button only appears for auth type when biometrics are enrolled.

MActionButton

A button component for primary actions with icon and text layout.

Props

text
string
required
Button label text
children
React.ReactNode
required
Icon or other content to display alongside text

Usage

import MActionButton from '@/components/MActionButton'
import Send from '@/components/icons/Send'

<MActionButton 
  text="Send"
  onPress={() => navigation.navigate('Send')}
>
  <Send />
</MActionButton>

Features

  • Icon and text horizontal layout
  • Flexible sizing (flex: 1)
  • Consistent padding and border radius
  • Touch feedback
  • Supports any icon component as children
Typically used for wallet action buttons:
<MHStack gap="md">
  <MActionButton text="Send">
    <Send />
  </MActionButton>
  <MActionButton text="Receive">
    <Receive />
  </MActionButton>
</MHStack>

MIconButton

A simple icon button with optional press state enhancement.

Props

center
boolean
default:"true"
Whether to center the icon content
noEnhanced
boolean
default:"false"
Disable automatic stroke color change on press
children
React.ReactNode
required
Icon component to display

Usage

import MIconButton from '@/components/MIconButton'
import Settings from '@/components/icons/Settings'

<MIconButton onPress={() => navigation.navigate('Settings')}>
  <Settings />
</MIconButton>

Features

  • 32x32 default size
  • Automatic icon color enhancement on press
  • Optional centering
  • Clones children to inject press state
  • Memoized for performance
By default, the component clones icon children and changes their stroke color when pressed:
return cloneElement(child, {
  stroke: isPressed ? Colors.grayLightest : Colors.white
})
Set noEnhanced={true} to disable this behavior for custom icons.

Build docs developers (and LLMs) love