Skip to content

Commit

Permalink
Send transaction pending state
Browse files Browse the repository at this point in the history
  • Loading branch information
monokh committed Nov 10, 2020
1 parent 60ac4dc commit 400543b
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 124 deletions.
2 changes: 1 addition & 1 deletion src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ store.subscribe(({ type, payload }, state) => {
store.dispatch('updateBalances', { network: state.activeNetwork, walletId: state.activeWalletId })
store.dispatch('updateFiatRates')
store.dispatch('updateMarketData', { network: state.activeNetwork })
store.dispatch('checkPendingSwaps', { walletId: state.activeWalletId })
store.dispatch('checkPendingActions', { walletId: state.activeWalletId })

if (!balanceInterval) {
balanceInterval = setInterval(() => {
Expand Down
59 changes: 50 additions & 9 deletions src/broker/notification.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
import { prettyBalance } from '../utils/coinFormatter'
import { getAssetIcon } from '../utils/asset'

const SWAP_STATUS_MAP = {
INITIATION_REPORTED () {
return 'Swap initiated'
return {
message: 'Swap initiated'
}
},
CONFIRM_COUNTER_PARTY_INITIATION (item) {
return `Counterparty sent ${prettyBalance(item.toAmount, item.to)} ${item.to} to escrow`
return {
message: `Counterparty sent ${prettyBalance(item.toAmount, item.to)} ${item.to} to escrow`
}
},
READY_TO_CLAIM () {
return 'Claiming funds'
return {
message: 'Claiming funds'
}
},
SUCCESS (item) {
return `Swap completed, ${prettyBalance(item.toAmount, item.to)} ${item.to} ready to use`
return {
message: `Swap completed, ${prettyBalance(item.toAmount, item.to)} ${item.to} ready to use`
}
},
REFUNDED (item) {
return `Swap refunded, ${prettyBalance(item.fromAmount, item.from)} ${item.from} returned`
return {
message: `Swap refunded, ${prettyBalance(item.fromAmount, item.from)} ${item.from} returned`
}
}
}

const SEND_STATUS_MAP = {
WAITING_FOR_CONFIRMATIONS (item) {
return {
title: `New ${item.from} Transaction`,
message: `Sending ${prettyBalance(item.amount, item.from)} ${item.from} to ${item.toAddress}`
}
},
SUCCESS (item) {
return {
title: `${item.from} Transaction Confirmed`,
message: `Sent ${prettyBalance(item.amount, item.from)} ${item.from} to ${item.toAddress}`
}
}
}

Expand All @@ -24,12 +50,27 @@ export const createNotification = config => browser.notifications.create({
...config
})

export const createSwapNotification = item => {
const fn = SWAP_STATUS_MAP[item.status]
if (!fn) return
const createSwapNotification = item => {
if (!(item.status in SWAP_STATUS_MAP)) return
const notification = SWAP_STATUS_MAP[item.status](item)

return createNotification({
title: `${item.from} -> ${item.to}`,
message: fn(item)
...notification
})
}

const createSendNotification = item => {
if (!(item.status in SEND_STATUS_MAP)) return
const notification = SEND_STATUS_MAP[item.status](item)

return createNotification({
iconUrl: getAssetIcon(item.from),
...notification
})
}

export const createHistoryNotification = item => {
if (item.type === 'SEND') return createSendNotification(item)
else if (item.type === 'SWAP') return createSwapNotification(item)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ const COMPLETED_STATES = [
'REFUNDED'
] // TODO: Pull this out so it's being used everywhere else (Transaction icons etc.)

export const checkPendingSwaps = async ({ state, dispatch }, { walletId }) => {
export const checkPendingActions = async ({ state, dispatch }, { walletId }) => {
Networks.forEach(network => {
const history = state.history[network]?.[walletId]
if (!history) return
history.forEach(order => {
if (order.type !== 'SWAP') return
if (order.error) return
history.forEach(item => {
if (item.error) return

if (!COMPLETED_STATES.includes(order.status)) {
dispatch('performNextAction', { network, walletId, id: order.id })
if (!COMPLETED_STATES.includes(item.status)) {
dispatch('performNextAction', { network, walletId, id: item.id })
}
})
})
Expand Down
4 changes: 2 additions & 2 deletions src/store/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { changeActiveWalletId } from './changeActiveWalletId'
import { changeActiveNetwork } from './changeActiveNetwork'
import { changePassword } from './changePassword'
import { checkIfQuoteExpired } from './checkIfQuoteExpired'
import { checkPendingSwaps } from './checkPendingSwaps'
import { checkPendingActions } from './checkPendingActions'
import { clientExec } from './clientExec'
import { getLockForAsset } from './getLockForAsset'
import { getUnusedAddresses } from './getUnusedAddresses'
Expand Down Expand Up @@ -35,7 +35,7 @@ export {
changeActiveNetwork,
changePassword,
checkIfQuoteExpired,
checkPendingSwaps,
checkPendingActions,
clientExec,
getLockForAsset,
getUnusedAddresses,
Expand Down
2 changes: 2 additions & 0 deletions src/store/actions/newSwap.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ export const newSwap = async ({ dispatch, commit }, { network, walletId, agent,
commit('NEW_ORDER', { network, walletId, order })

dispatch('performNextAction', { network, walletId, id: order.id })

return order
}
36 changes: 36 additions & 0 deletions src/store/actions/performNextAction/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { performNextSwapAction } from './swap'
import { performNextTransactionAction } from './send'
import { createHistoryNotification } from '../../../broker/notification'

export const performNextAction = async (store, { network, walletId, id }) => {
const { dispatch, commit, getters } = store
const item = getters.historyItemById(network, walletId, id)
if (!item) return
if (!item.status) return

let updates
if (item.type === 'SWAP') {
updates = await performNextSwapAction(store, { network, walletId, order: item })
}
if (item.type === 'SEND') {
updates = await performNextTransactionAction(store, { network, walletId, transaction: item })
}

if (updates) {
commit('UPDATE_HISTORY', {
network,
walletId,
id,
updates
})

createHistoryNotification({
...item,
...updates
})

if (!updates.error) {
dispatch('performNextAction', { network, walletId, id })
}
}
}
27 changes: 27 additions & 0 deletions src/store/actions/performNextAction/send.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

import { withInterval } from './utils'

async function waitForConfirmations ({ getters, dispatch }, { transaction, network, walletId }) {
const client = getters.client(network, walletId, transaction.from)

const tx = await client.chain.getTransactionByHash(transaction.txHash)

if (tx && tx.confirmations > 0) {
dispatch('updateBalances', { network, walletId, assets: [transaction.from] })

return {
endTime: Date.now(),
status: 'SUCCESS'
}
}
}

export const performNextTransactionAction = async (store, { network, walletId, transaction }) => {
let updates

if (transaction.status === 'WAITING_FOR_CONFIRMATIONS') {
updates = await withInterval(async () => waitForConfirmations(store, { transaction, network, walletId }))
}

return updates
}
Original file line number Diff line number Diff line change
@@ -1,52 +1,6 @@
import { random } from 'lodash-es'
import { sha256 } from '@liquality/crypto'
import { updateOrder, unlockAsset, wait } from '../utils'
import { createSwapNotification } from '../../broker/notification'

async function withLock ({ dispatch }, { order, network, walletId, asset }, func) {
const lock = await dispatch('getLockForAsset', { order, network, walletId, asset })
try {
return await func()
} catch (e) {
return { error: e.toString() }
} finally {
unlockAsset(lock)
}
}

async function withInterval (func) {
const updates = await func()
if (updates) { return updates }
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
const updates = await func()
if (updates) {
clearInterval(interval)
resolve(updates)
}
}, random(15000, 30000))
})
}

async function hasChainTimePassed ({ getters }, { network, walletId, asset, timestamp }) {
const client = getters.client(network, walletId, asset)
const maxTries = 3
let tries = 0
while (tries < maxTries) {
try {
const blockNumber = await client.chain.getBlockHeight()
const latestBlock = await client.chain.getBlockByNumber(blockNumber)
return latestBlock.timestamp > timestamp
} catch (e) {
tries++
if (tries >= maxTries) throw e
else {
console.warn(e)
await wait(2000)
}
}
}
}
import { withLock, withInterval, hasChainTimePassed } from './utils'
import { updateOrder } from '../../utils'

async function canRefund ({ getters }, { network, walletId, order }) {
return hasChainTimePassed({ getters }, { network, walletId, asset: order.from, timestamp: order.swapExpiration })
Expand Down Expand Up @@ -278,12 +232,7 @@ async function sendTo ({ getters, dispatch }, { order, network, walletId }) {
}
}

export const performNextAction = async (store, { network, walletId, id }) => {
const { dispatch, commit, getters } = store
const order = getters.historyItemById(network, walletId, id)
if (!order) return
if (!order.status) return

export const performNextSwapAction = async (store, { network, walletId, order }) => {
let updates

switch (order.status) {
Expand Down Expand Up @@ -340,21 +289,5 @@ export const performNextAction = async (store, { network, walletId, id }) => {
break
}

if (updates) {
commit('UPDATE_HISTORY', {
network,
walletId,
id,
updates
})

createSwapNotification({
...order,
...updates
})

if (!updates.error) {
dispatch('performNextAction', { network, walletId, id })
}
}
return updates
}
48 changes: 48 additions & 0 deletions src/store/actions/performNextAction/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

import { random } from 'lodash-es'
import { unlockAsset, wait } from '../../utils'

export async function withLock ({ dispatch }, { order, network, walletId, asset }, func) {
const lock = await dispatch('getLockForAsset', { order, network, walletId, asset })
try {
return await func()
} catch (e) {
return { error: e.toString() }
} finally {
unlockAsset(lock)
}
}

export async function withInterval (func) {
const updates = await func()
if (updates) { return updates }
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
const updates = await func()
if (updates) {
clearInterval(interval)
resolve(updates)
}
}, random(15000, 30000))
})
}

export async function hasChainTimePassed ({ getters }, { network, walletId, asset, timestamp }) {
const client = getters.client(network, walletId, asset)
const maxTries = 3
let tries = 0
while (tries < maxTries) {
try {
const blockNumber = await client.chain.getBlockHeight()
const latestBlock = await client.chain.getBlockByNumber(blockNumber)
return latestBlock.timestamp > timestamp
} catch (e) {
tries++
if (tries >= maxTries) throw e
else {
console.warn(e)
await wait(2000)
}
}
}
}
28 changes: 9 additions & 19 deletions src/store/actions/sendTransaction.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,31 @@
import { createNotification } from '../../broker/notification'
import { prettyBalance } from '../../utils/coinFormatter'
import { getAssetIcon } from '../../utils/asset'
import { v4 as uuidv4 } from 'uuid'
import { createHistoryNotification } from '../../broker/notification'

export const sendTransaction = async ({ commit, getters }, { network, walletId, asset, to, amount, data, fee }) => {
export const sendTransaction = async ({ dispatch, commit, getters }, { network, walletId, asset, to, amount, data, fee }) => {
const client = getters.client(network, walletId, asset)

const tx = await client.chain.sendTransaction(to, amount, data, fee)

const transaction = {
id: uuidv4(),
type: 'SEND',
network,
walletId,

to: asset,
from: asset,
toAddress: to,

amount,
fee,
tx,
txHash: tx.hash,

startTime: Date.now(),
status: 'SUCCESS'
status: 'WAITING_FOR_CONFIRMATIONS'
}

commit('NEW_TRASACTION', {
network,
walletId,
transaction
})
commit('NEW_TRASACTION', { network, walletId, transaction })

dispatch('performNextAction', { network, walletId, id: transaction.id })

createNotification({
title: `New ${asset} Transaction`,
message: `Sent ${prettyBalance(amount, asset)} ${asset} to ${to}`,
iconUrl: getAssetIcon(asset)
})
createHistoryNotification(transaction)

return tx
}
Loading

0 comments on commit 400543b

Please sign in to comment.