diff --git a/app/components/AmountInput/AmountInput.js b/app/components/AmountInput/AmountInput.js
new file mode 100644
index 00000000..b9efb05e
--- /dev/null
+++ b/app/components/AmountInput/AmountInput.js
@@ -0,0 +1,164 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+class AmountInput extends React.Component {
+  constructor(props) {
+    super(props)
+    this.handleChange = this.handleChange.bind(this)
+    this.handleBlur = this.handleBlur.bind(this)
+    this.handleKeyDown = this.handleKeyDown.bind(this)
+  }
+
+  setRules() {
+    const { currency } = this.props
+    switch (currency) {
+      case 'btc':
+        this.rules = {
+          precision: 8,
+          placeholder: '0.00000000',
+          pattern: '[0-9.]*'
+        }
+        break
+      case 'bits':
+        this.rules = {
+          precision: 2,
+          placeholder: '0.00',
+          pattern: '[0-9.]*'
+        }
+        break
+      case 'sats':
+        this.rules = {
+          precision: 0,
+          placeholder: '00000000',
+          pattern: '[0-9]*'
+        }
+        break
+      default:
+        this.rules = {
+          precision: 2,
+          pattern: '[0-9]*'
+        }
+    }
+  }
+
+  parseNumber(_value) {
+    let value = _value || ''
+    if (typeof _value === 'string') {
+      value = _value.replace(/[^0-9.]/g, '')
+    }
+    let integer = null
+    let fractional = null
+    if (value * 1.0 < 0) {
+      value = '0.0'
+    }
+    // pearse integer and fractional value so that we can reproduce the same string value afterwards
+    // [0, 0] === 0.0
+    // [0, ''] === 0.
+    // [0, null] === 0
+    if (value.match(/^[0-9]*\.[0-9]*$/)) {
+      ;[integer, fractional] = value.toString().split(/\./)
+      if (!fractional) {
+        fractional = ''
+      }
+    } else {
+      integer = value
+    }
+    // Limit fractional precision to the correct number of decimal places.
+    if (fractional && fractional.length > this.rules.precision) {
+      fractional = fractional.substring(0, this.rules.precision)
+    }
+
+    return [integer, fractional]
+  }
+
+  formatValue(integer, fractional) {
+    let value
+    if (fractional && fractional.length > 0) {
+      value = `${integer}.${fractional}`
+    } else {
+      // Empty string means `XYZ.` instead of just plain `XYZ`.
+      if (fractional === '') {
+        value = `${integer}.`
+      } else {
+        value = `${integer}`
+      }
+    }
+    return value
+  }
+
+  handleChange(e) {
+    let { value } = e.target
+    const [integer, fractional] = this.parseNumber(value)
+    value = this.formatValue(integer, fractional)
+
+    const { onChangeEvent } = this.props
+    if (onChangeEvent) {
+      onChangeEvent(value)
+    }
+  }
+
+  handleBlur(e) {
+    let { value } = e.target
+    const [integer, fractional] = this.parseNumber(value)
+    value = this.formatValue(integer, fractional)
+
+    const { onBlurEvent } = this.props
+    if (onBlurEvent) {
+      onBlurEvent(value)
+    }
+  }
+
+  handleKeyDown(e) {
+    // Do nothing if the user did select all key combo.
+    if (e.metaKey && e.key === 'a') {
+      return
+    }
+
+    // Do not allow multiple dots.
+    let { value } = e.target
+    if (e.key === '.') {
+      if (value.search(/\./) >= 0) {
+        e.preventDefault()
+      }
+      return
+    }
+
+    if (e.key.length === 1 && !e.key.match(/^[0-9.]$/)) {
+      e.preventDefault()
+      return
+    }
+  }
+
+  render() {
+    let { amount = '', readOnly } = this.props
+    if (amount === null) {
+      amount = ''
+    }
+    this.setRules()
+
+    return (
+      <input
+        id="amount"
+        onChange={this.handleChange}
+        onBlur={this.handleBlur}
+        onKeyDown={this.handleKeyDown}
+        readOnly={readOnly}
+        type="text"
+        required
+        value={amount}
+        pattern={this.rules.pattern}
+        placeholder={this.rules.placeholder}
+      />
+    )
+  }
+}
+
+AmountInput.propTypes = {
+  amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+  currency: PropTypes.string.isRequired,
+  onChangeEvent: PropTypes.func,
+  onBlurEvent: PropTypes.func,
+  readOnly: PropTypes.bool
+}
+
+export default AmountInput
diff --git a/app/components/AmountInput/AmountInput.scss b/app/components/AmountInput/AmountInput.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/app/components/AmountInput/index.js b/app/components/AmountInput/index.js
new file mode 100644
index 00000000..37d612c0
--- /dev/null
+++ b/app/components/AmountInput/index.js
@@ -0,0 +1,3 @@
+import AmountInput from './AmountInput'
+
+export default AmountInput
diff --git a/app/components/Contacts/SubmitChannelForm.js b/app/components/Contacts/SubmitChannelForm.js
index 207472c4..d3d27e93 100644
--- a/app/components/Contacts/SubmitChannelForm.js
+++ b/app/components/Contacts/SubmitChannelForm.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
 
 import FaAngleDown from 'react-icons/lib/fa/angle-down'
 
+import AmountInput from 'components/AmountInput'
 import styles from './SubmitChannelForm.scss'
 
 class SubmitChannelForm extends React.Component {
@@ -16,6 +17,8 @@ class SubmitChannelForm extends React.Component {
       updateContactCapacity,
       openChannel,
 
+      ticker,
+
       toggleCurrencyProps: {
         setContactsCurrencyFilters,
         showCurrencyFilters,
@@ -73,14 +76,11 @@ class SubmitChannelForm extends React.Component {
 
         <section className={styles.amount}>
           <div className={styles.input}>
-            <input
-              type="number"
-              min="0"
-              size=""
-              placeholder="0.00000000"
-              value={contactCapacity || ''}
-              onChange={event => updateContactCapacity(event.target.value)}
+            <AmountInput
               id="amount"
+              amount={contactCapacity}
+              currency={ticker.currency}
+              onChangeEvent={updateContactCapacity}
             />
             <div className={styles.currency}>
               <section
@@ -127,6 +127,8 @@ SubmitChannelForm.propTypes = {
   updateContactCapacity: PropTypes.func.isRequired,
   openChannel: PropTypes.func.isRequired,
 
+  ticker: PropTypes.object.isRequired,
+
   toggleCurrencyProps: PropTypes.object.isRequired
 }
 
diff --git a/app/components/Form/Pay.js b/app/components/Form/Pay.js
index 9ad81b6d..76085c9d 100644
--- a/app/components/Form/Pay.js
+++ b/app/components/Form/Pay.js
@@ -7,6 +7,7 @@ import link from 'icons/link.svg'
 import FaAngleDown from 'react-icons/lib/fa/angle-down'
 
 import { btc } from 'lib/utils'
+import AmountInput from 'components/AmountInput'
 
 import styles from './Pay.scss'
 
@@ -132,18 +133,12 @@ class Pay extends Component {
               <span />
             </div>
             <div className={styles.bottom}>
-              <input
-                type="number"
-                min="0"
-                ref={input => {
-                  this.amountInput = input
-                }}
-                size=""
-                placeholder="0.00000000"
-                value={currentAmount || ''}
-                onChange={event => setPayAmount(event.target.value)}
-                onBlur={onPayAmountBlur}
+              <AmountInput
                 id="amount"
+                amount={currentAmount}
+                currency={ticker.currency}
+                onChangeEvent={setPayAmount}
+                onBlurEvent={onPayAmountBlur}
                 readOnly={isLn}
               />
               <div className={styles.currency}>
diff --git a/app/components/Form/Request.js b/app/components/Form/Request.js
index ed0128dc..5f61c28a 100644
--- a/app/components/Form/Request.js
+++ b/app/components/Form/Request.js
@@ -6,6 +6,7 @@ import hand from 'icons/hand.svg'
 import FaAngleDown from 'react-icons/lib/fa/angle-down'
 
 import { btc } from 'lib/utils'
+import AmountInput from 'components/AmountInput'
 import styles from './Request.scss'
 
 const Request = ({
@@ -45,12 +46,11 @@ const Request = ({
             <span />
           </div>
           <div className={styles.bottom}>
-            <input
-              type="number"
-              value={amount || ''}
-              onChange={event => setRequestAmount(event.target.value)}
+            <AmountInput
               id="amount"
-              placeholder="0.00000000"
+              amount={amount}
+              currency={ticker.currency}
+              onChangeEvent={setRequestAmount}
             />
             <div className={styles.currency}>
               <section
diff --git a/app/routes/app/containers/AppContainer.js b/app/routes/app/containers/AppContainer.js
index b00c4806..dd58dd4e 100644
--- a/app/routes/app/containers/AppContainer.js
+++ b/app/routes/app/containers/AppContainer.js
@@ -409,6 +409,8 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
 
     openChannel: dispatchProps.openChannel,
 
+    ticker: stateProps.ticker,
+
     toggleCurrencyProps: {
       currentCurrencyFilters: stateProps.currentCurrencyFilters,
       currencyName: stateProps.currencyName,
diff --git a/test/unit/components/AmountInput.spec.js b/test/unit/components/AmountInput.spec.js
new file mode 100644
index 00000000..db6d0789
--- /dev/null
+++ b/test/unit/components/AmountInput.spec.js
@@ -0,0 +1,251 @@
+import React from 'react'
+import { configure, mount } from 'enzyme'
+import Adapter from 'enzyme-adapter-react-16'
+
+import AmountInput from 'components/AmountInput'
+
+configure({ adapter: new Adapter() })
+
+let component
+let wrapped
+let input
+
+const defaultProps = {
+  amount: '',
+  currency: '',
+
+  onChange: () => {}
+}
+
+const mountInput = props => {
+  wrapped = mount(<AmountInput {...defaultProps} {...props} />)
+  input = wrapped.find('input')
+  component = wrapped.instance()
+}
+
+describe('AmountInput', () => {
+  describe('render', () => {
+    it('renders amount', () => {
+      mountInput({ amount: '0.1234' })
+      expect(input.props().value).toEqual('0.1234')
+    })
+  })
+
+  describe('input properties', () => {
+    it('passes readOnly to input', () => {
+      mountInput({ readOnly: true })
+      expect(input.props().readOnly).toEqual(true)
+    })
+  })
+
+  describe('parseNumber', () => {
+    const test = (from, to) => {
+      expect(component.parseNumber(from)).toEqual(to)
+    }
+    beforeEach(() => mountInput())
+
+    it('splits number into integer and fraction', () => {
+      test(null, ['', null])
+      test('', ['', null])
+      test('0', ['0', null])
+      test('00', ['00', null])
+      test('0.', ['0', ''])
+
+      test('1', ['1', null])
+      test('01', ['01', null])
+      test('1.', ['1', ''])
+
+      test('0.0', ['0', '0'])
+      test('0.1', ['0', '1'])
+
+      test('0.01', ['0', '01'])
+      test('0.10', ['0', '10'])
+
+      test('1.0', ['1', '0'])
+      test('01.0', ['01', '0'])
+    })
+
+    describe('bitcoin', () => {
+      beforeEach(() => {
+        let props = { ...defaultProps, currency: 'btc' }
+        component = mount(<AmountInput {...props} />).instance()
+      })
+      it('has a precision of 8', () => {
+        test('1.1', ['1', '1'])
+        test('1.12', ['1', '12'])
+        test('1.123', ['1', '123'])
+        test('1.1234', ['1', '1234'])
+        test('1.12345', ['1', '12345'])
+        test('1.123456', ['1', '123456'])
+        test('1.1234567', ['1', '1234567'])
+        test('1.12345678', ['1', '12345678'])
+        test('1.123456789', ['1', '12345678'])
+        test('1.1234567890', ['1', '12345678'])
+      })
+    })
+
+    describe('bits', () => {
+      beforeEach(() => {
+        let props = { ...defaultProps, currency: 'bits' }
+        component = mount(<AmountInput {...props} />).instance()
+      })
+      it('has a precision of 2', () => {
+        test('1.1', ['1', '1'])
+        test('1.12', ['1', '12'])
+        test('1.123', ['1', '12'])
+        test('1.1234', ['1', '12'])
+      })
+    })
+
+    describe('sats', () => {
+      beforeEach(() => {
+        let props = { ...defaultProps, currency: 'sats' }
+        component = mount(<AmountInput {...props} />).instance()
+      })
+      it('has a precision of 0', () => {
+        test('1.1', ['1', ''])
+        test('1.12', ['1', ''])
+      })
+    })
+  })
+
+  describe('formatValue', () => {
+    const component = mount(<AmountInput {...defaultProps} />).instance()
+    const test = (from, to) => {
+      expect(component.formatValue(from[0], from[1])).toEqual(to)
+    }
+
+    it('turns parsed number into a string', () => {
+      test(['0', null], '0')
+      test(['00', null], '00')
+      test(['0', ''], '0.')
+
+      test(['1', null], '1')
+      test(['01', null], '01')
+      test(['1', ''], '1.')
+
+      test(['0', '0'], '0.0')
+      test(['0', '1'], '0.1')
+
+      test(['0', '01'], '0.01')
+      test(['0', '10'], '0.10')
+
+      test(['1', '0'], '1.0')
+      test(['01', '0'], '01.0')
+    })
+  })
+
+  describe('handleChange', () => {
+    let onChangeEvent
+    let component
+    beforeEach(() => {
+      onChangeEvent = jest.fn()
+      let props = { ...defaultProps, onChangeEvent }
+      component = mount(<AmountInput {...props} />).instance()
+    })
+
+    it('sends value to parseNumber', () => {
+      jest.spyOn(component, 'parseNumber')
+
+      component.handleChange({ target: { value: '123.0' } })
+      expect(component.parseNumber).toHaveBeenCalledTimes(1)
+      expect(component.parseNumber).toHaveBeenCalledWith('123.0')
+    })
+
+    it('calls formatValue with presult from parseNumber', () => {
+      jest.spyOn(component, 'formatValue')
+
+      component.handleChange({ target: { value: '123.0' } })
+      expect(component.formatValue).toHaveBeenCalledTimes(1)
+      expect(component.formatValue).toHaveBeenCalledWith('123', '0')
+    })
+
+    it('calls props.onChange with formatValue result', () => {
+      jest.spyOn(component, 'formatValue').mockReturnValue('456')
+
+      component.handleChange({ target: { value: '123.0' } })
+      expect(onChangeEvent).toHaveBeenCalledWith('456')
+    })
+  })
+
+  describe('handleBlur', () => {
+    let onBlurEvent
+    let component
+    beforeEach(() => {
+      onBlurEvent = jest.fn()
+      let props = { ...defaultProps, onBlurEvent }
+      component = mount(<AmountInput {...props} />).instance()
+    })
+
+    it('calls props.onBlurEvent with formatValue result', () => {
+      jest.spyOn(component, 'formatValue').mockReturnValue('456')
+
+      component.handleBlur({ target: { value: '123.0' } })
+      expect(onBlurEvent).toHaveBeenCalledWith('456')
+    })
+  })
+
+  describe('handleKeyDown', () => {
+    let event
+    let preventDefault
+
+    beforeEach(() => {
+      wrapped = mount(<AmountInput {...defaultProps} />)
+      component = wrapped.instance()
+
+      preventDefault = jest.fn()
+    })
+
+    describe('dot', () => {
+      beforeEach(() => {
+        wrapped = mount(<AmountInput {...defaultProps} />)
+        component = wrapped.instance()
+        preventDefault = jest.fn()
+
+        event = {
+          key: '.',
+          target: { value: '123' },
+          preventDefault
+        }
+      })
+
+      it('allows adding dot to a number that has no dot', () => {
+        component.handleKeyDown({ ...event, target: { value: '123' } }) // no dot
+        expect(preventDefault).not.toHaveBeenCalled()
+      })
+
+      it('does not allow multiple dots in the number', () => {
+        component.handleKeyDown({ ...event, target: { value: '123.' } }) // has a dot
+        expect(preventDefault).toHaveBeenCalled()
+      })
+    })
+
+    describe('Non-supported keys', () => {
+      beforeEach(() => {
+        wrapped = mount(<AmountInput {...defaultProps} />)
+        component = wrapped.instance()
+        preventDefault = jest.fn()
+
+        event = {
+          key: '.',
+          target: { value: '123' },
+          preventDefault
+        }
+      })
+
+      it('prevents event', () => {
+        component.handleKeyDown({ ...event, key: 'b' })
+        expect(preventDefault).toHaveBeenCalled()
+
+        component.handleKeyDown({ ...event, key: 'B' })
+        expect(preventDefault).toHaveBeenCalled()
+
+        component.handleKeyDown({ ...event, key: '-' })
+        expect(preventDefault).toHaveBeenCalled()
+
+        component.handleKeyDown({ ...event, key: '?' })
+        expect(preventDefault).toHaveBeenCalled()
+      })
+    })
+  })
+})
diff --git a/test/unit/components/Form.spec.js b/test/unit/components/Form.spec.js
index f4f8a219..ef2cf33a 100644
--- a/test/unit/components/Form.spec.js
+++ b/test/unit/components/Form.spec.js
@@ -15,10 +15,12 @@ const payFormProps = {
     invoice: {},
     showErrors: {}
   },
-  currency: {},
-  crypto: {},
+  currency: '',
+  crypto: '',
   nodes: [],
-  ticker: {},
+  ticker: {
+    currency: 'btc'
+  },
 
   isOnchain: false,
   isLn: true,
diff --git a/test/unit/components/Form/Pay.spec.js b/test/unit/components/Form/Pay.spec.js
index 8781090f..ef1503e1 100644
--- a/test/unit/components/Form/Pay.spec.js
+++ b/test/unit/components/Form/Pay.spec.js
@@ -13,10 +13,12 @@ const defaultProps = {
     invoice: {},
     showErrors: {}
   },
-  currency: {},
-  crypto: {},
+  currency: '',
+  crypto: '',
   nodes: [],
-  ticker: {},
+  ticker: {
+    currency: 'btc'
+  },
 
   isOnchain: false,
   isLn: true,
diff --git a/test/unit/components/Form/Request.spec.js b/test/unit/components/Form/Request.spec.js
index 1c639701..59da8ee5 100644
--- a/test/unit/components/Form/Request.spec.js
+++ b/test/unit/components/Form/Request.spec.js
@@ -8,7 +8,9 @@ configure({ adapter: new Adapter() })
 
 const defaultProps = {
   requestform: {},
-  ticker: {},
+  ticker: {
+    currency: 'btc'
+  },
 
   currentCurrencyFilters: [],
   showCurrencyFilters: true,