Browse Source

Merge pull request #1540 from gre/xrp-check-destination-require-more-funds

Ripple: handle case the destination does not exist
gre-patch-1
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
e425718e5e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .prettierignore
  2. 45
      src/bridge/RippleJSBridge.js
  3. 3
      src/config/errors.js
  4. 3
      static/i18n/en/errors.json
  5. 16
      test-e2e/sync/sync-accounts.spec.js
  6. 6
      test-e2e/sync/wait-sync.js

1
.prettierignore

@ -1 +1,2 @@
package.json
test-e2e/**/*.json

45
src/bridge/RippleJSBridge.js

@ -20,7 +20,7 @@ import {
import FeesRippleKind from 'components/FeesField/RippleKind'
import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { NotEnoughBalance } from 'config/errors'
import { NotEnoughBalance, NotEnoughBalanceBecauseDestinationNotCreated } from 'config/errors'
import type { WalletBridge, EditProps } from './types'
type Transaction = {
@ -114,7 +114,7 @@ async function signAndBroadcast({ a, t, deviceId, isCancelled, onSigned, onOpera
}
}
function isRecipientValid(currency, recipient) {
function isRecipientValid(recipient) {
try {
bs58check.decode(recipient)
return true
@ -241,6 +241,31 @@ const getServerInfo = (map => endpointConfig => {
return f()
})({})
const recipientIsNew = async (endpointConfig, recipient) => {
if (!isRecipientValid(recipient)) return false
const api = apiForEndpointConfig(endpointConfig)
try {
await api.connect()
try {
await api.getAccountInfo(recipient)
return false
} catch (e) {
if (e.message !== 'actNotFound') {
throw e
}
return true
}
} finally {
api.disconnect()
}
}
const cacheRecipientsNew = {}
const cachedRecipientIsNew = (endpointConfig, recipient) => {
if (recipient in cacheRecipientsNew) return cacheRecipientsNew[recipient]
return (cacheRecipientsNew[recipient] = recipientIsNew(endpointConfig, recipient))
}
const RippleJSBridge: WalletBridge<Transaction> = {
scanAccountsOnDevice: (currency, deviceId) =>
Observable.create(o => {
@ -446,7 +471,7 @@ const RippleJSBridge: WalletBridge<Transaction> = {
pullMoreOperations: () => Promise.resolve(a => a), // FIXME not implemented
isRecipientValid: (currency, recipient) => Promise.resolve(isRecipientValid(currency, recipient)),
isRecipientValid: (currency, recipient) => Promise.resolve(isRecipientValid(recipient)),
getRecipientWarning: () => Promise.resolve(null),
createTransaction: () => ({
@ -496,10 +521,21 @@ const RippleJSBridge: WalletBridge<Transaction> = {
checkValidTransaction: async (a, t) => {
const r = await getServerInfo(a.endpointConfig)
const reserveBaseXRP = parseAPIValue(r.validatedLedger.reserveBaseXRP)
if (t.recipient) {
if (await cachedRecipientIsNew(a.endpointConfig, t.recipient)) {
if (t.amount.lt(reserveBaseXRP)) {
const f = formatAPICurrencyXRP(reserveBaseXRP)
throw new NotEnoughBalanceBecauseDestinationNotCreated('', {
minimalAmount: `${f.currency} ${f.value}`,
})
}
}
}
if (
t.amount
.plus(t.fee)
.plus(parseAPIValue(r.validatedLedger.reserveBaseXRP))
.plus(reserveBaseXRP)
.isLessThanOrEqualTo(a.balance)
) {
return true
@ -513,6 +549,7 @@ const RippleJSBridge: WalletBridge<Transaction> = {
signAndBroadcast: (a, t, deviceId) =>
Observable.create(o => {
delete cacheRecipientsNew[t.recipient]
let cancelled = false
const isCancelled = () => cancelled
const onSigned = () => {

3
src/config/errors.js

@ -30,6 +30,9 @@ export const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBT
export const NetworkDown = createCustomErrorClass('NetworkDown')
export const NoAddressesFound = createCustomErrorClass('NoAddressesFound')
export const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
export const NotEnoughBalanceBecauseDestinationNotCreated = createCustomErrorClass(
'NotEnoughBalanceBecauseDestinationNotCreated',
)
export const PasswordsDontMatchError = createCustomErrorClass('PasswordsDontMatch')
export const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
export const TimeoutTagged = createCustomErrorClass('TimeoutTagged')

3
static/i18n/en/errors.json

@ -99,6 +99,9 @@
"title": "Oops, insufficient balance",
"description": "Make sure the account to debit has sufficient balance"
},
"NotEnoughBalanceBecauseDestinationNotCreated": {
"title": "Recipient address is inactive. Send at least {{minimalAmount}} to activate it"
},
"PasswordsDontMatch": {
"title": "Passwords don't match",
"description": "Please try again"

16
test-e2e/sync/sync-accounts.spec.js

@ -46,15 +46,13 @@ function getSanitized(filePath) {
const data = require(`${filePath}`) // eslint-disable-line import/no-dynamic-require
const accounts = data.data.accounts.map(a => a.data)
accounts.sort(ACCOUNT_SORT)
return accounts
.map(a => pick(a, ACCOUNTS_FIELDS))
.map(a => {
a.operations.sort(OP_SORT)
return {
...a,
operations: a.operations.map(o => pick(o, OPS_FIELDS)),
}
})
return accounts.map(a => pick(a, ACCOUNTS_FIELDS)).map(a => {
a.operations.sort(OP_SORT)
return {
...a,
operations: a.operations.map(o => pick(o, OPS_FIELDS)),
}
})
}
function getOpHash(op) {

6
test-e2e/sync/wait-sync.js

@ -27,7 +27,11 @@ async function waitForSync() {
const areAllSync = mapped.every(account => {
const diff = now - new Date(account.lastSyncDate).getTime()
if (diff <= MIN_TIME_DIFF) return true
console.log(`[${account.name}] synced ${moment(account.lastSyncDate).fromNow()} (${moment(account.lastSyncDate).format('YYYY-MM-DD HH:mm:ss')})`)
console.log(
`[${account.name}] synced ${moment(account.lastSyncDate).fromNow()} (${moment(
account.lastSyncDate,
).format('YYYY-MM-DD HH:mm:ss')})`,
)
return false
})
return areAllSync

Loading…
Cancel
Save