Skip to content

Commit

Permalink
show raw unsigned transaction in modal, refactor wallet actions to ta…
Browse files Browse the repository at this point in the history
…ke address first
  • Loading branch information
mirecmrozek authored and refi93 committed Dec 13, 2018
1 parent 4467e82 commit 65a2ea8
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 23 deletions.
23 changes: 22 additions & 1 deletion app/frontend/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const {generateMnemonic} = require('./wallet/mnemonic')
const {ADALITE_CONFIG} = require('./config')
const derivationSchemes = require('./wallet/derivation-schemes')
const FileSaver = require('file-saver')
const cbor = require('cbor')
const {
sendAddressValidator,
sendAmountValidator,
Expand Down Expand Up @@ -287,7 +288,7 @@ module.exports = ({setState, getState}) => {

const address = state.sendAddress.fieldValue
const amount = state.sendAmount.coins
const transactionFee = await wallet.getTxFee(amount, address)
const transactionFee = await wallet.getTxFee(address, amount)

// if we reverted value in the meanwhile, do nothing, otherwise update
const newState = getState()
Expand Down Expand Up @@ -471,6 +472,24 @@ module.exports = ({setState, getState}) => {
})
}

const setRawTransactionOpen = (state, open) => {
setState({
rawTransactionOpen: open,
})
}

const getRawTransaction = async (state, address, coins) => {
const txAux = await wallet.prepareTxAux(address, coins).catch((e) => {
debugLog(e)
throw NamedError('TransactionCorrupted')
})

setState({
rawTransaction: Buffer.from(cbor.encode(txAux)).toString('hex'),
rawTransactionOpen: true,
})
}

return {
loadingAction,
stopLoadingAction,
Expand Down Expand Up @@ -499,5 +518,7 @@ module.exports = ({setState, getState}) => {
confirmGenerateMnemonicDialog,
closeThanksForDonationModal,
setLogoutNotificationOpen,
setRawTransactionOpen,
getRawTransaction,
}
}
2 changes: 1 addition & 1 deletion app/frontend/components/common/addressDetailDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AddressDetailDialogClass extends Component {
h(
'span',
{
class: 'full-address selectable',
class: 'full-address force-select-all',
},
showDetail.address
)
Expand Down
37 changes: 37 additions & 0 deletions app/frontend/components/pages/sendAda/rawTransactionModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const {h} = require('preact')
const connect = require('unistore/preact').connect
const actions = require('../../../actions')
const Modal = require('../../common/modal')

class RawTransactionModal {
render({rawTransaction, setRawTransactionOpen}) {
return h(
Modal,
{
closeHandler: () => setRawTransactionOpen(false),
bodyClass: 'width-auto',
},
h(
'div',
{class: 'width-auto'},
h('h4', undefined, 'Raw unsigned transaction'),
h(
'div',
undefined,
h(
'div',
{class: 'raw-unsigned-transaction force-select-all'},
h('span', undefined, rawTransaction)
)
)
)
)
}
}

module.exports = connect(
(state) => ({
rawTransaction: state.rawTransaction,
}),
actions
)(RawTransactionModal)
25 changes: 24 additions & 1 deletion app/frontend/components/pages/sendAda/sendAdaPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const printConversionRates = require('../../../helpers/printConversionRates')
const Balance = require('../../common/balance')
const Modal = require('../../common/modal')
const ConfirmTransactionDialog = require('./confirmTransactionDialog')
const RawTransactionModal = require('./rawTransactionModal')

class ThanksForDonationModal extends Component {
componentDidMount() {
Expand Down Expand Up @@ -59,6 +60,7 @@ class SendAdaPage extends Component {
sendAddress,
sendAddressValidationError,
sendAmount,
coinsAmount,
sendAmountValidationError,
updateAddress,
updateAmount,
Expand All @@ -71,6 +73,10 @@ class SendAdaPage extends Component {
showThanksForDonation,
closeThanksForDonationModal,
conversionRates,
getRawTransaction,
rawTransactionOpen,
setRawTransactionOpen,
rawTransaction,
}) {
const enableSubmit =
sendAmount && !sendAmountValidationError && sendAddress && !sendAddressValidationError
Expand Down Expand Up @@ -213,7 +219,20 @@ class SendAdaPage extends Component {
),
h(
'div',
undefined,
{class: 'submit-row'},
h(
'span',
{
onClick: async () => {
await getRawTransaction(sendAddress, coinsAmount)
setRawTransactionOpen(true)
},
class: `link raw-transaction-link${
!enableSubmit || feeRecalculating ? '-disabled' : ''
}`,
},
'Raw unsigned transaction'
),
feeRecalculating
? h(
'button',
Expand All @@ -240,6 +259,7 @@ class SendAdaPage extends Component {
'Submit'
)
),
rawTransactionOpen && h(RawTransactionModal),
showConfirmTransactionDialog && h(ConfirmTransactionDialog),
showThanksForDonation && h(ThanksForDonationModal, {closeThanksForDonationModal})
)
Expand All @@ -255,12 +275,15 @@ module.exports = connect(
sendAddress: state.sendAddress.fieldValue,
sendAmountValidationError: state.sendAmount.validationError,
sendAmount: state.sendAmount.fieldValue,
coinsAmount: state.sendAmount.coins,
transactionFee: state.transactionFee,
showConfirmTransactionDialog: state.showConfirmTransactionDialog,
feeRecalculating: state.calculatingFee,
totalAmount: state.sendAmountForTransactionFee + state.transactionFee,
showThanksForDonation: state.showThanksForDonation,
conversionRates: state.conversionRates && state.conversionRates.data,
rawTransaction: state.rawTransaction,
rawTransactionOpen: state.rawTransactionOpen,
}),
actions
)(SendAdaPage)
2 changes: 2 additions & 0 deletions app/frontend/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const initialState = {
enableTrezor: ADALITE_CONFIG.ADALITE_ENABLE_TREZOR,
showDemoWalletWarningDialog: false,
logoutNotificationOpen: false,
rawTransactionOpen: false,
rawTransaction: '',
}

const createStore = () =>
Expand Down
26 changes: 13 additions & 13 deletions app/frontend/wallet/cardano-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ const CardanoWallet = async (options) => {
}

async function prepareTxAux(address, coins) {
const txInputs = await prepareTxInputs(coins, address)
const txInputs = await prepareTxInputs(address, coins)
const txInputsCoinsSum = txInputs.reduce((acc, elem) => acc + elem.coins, 0)
const fee = computeTxFee(txInputs, coins, address)
const fee = computeTxFee(txInputs, address, coins)
const changeAmount = txInputsCoinsSum - coins - fee

if (changeAmount < 0) {
Expand All @@ -161,15 +161,15 @@ const CardanoWallet = async (options) => {
txInputs.push(TxInputFromUtxo(profitableUtxos[i]))
coins += profitableUtxos[i].coins
}
const txFee = computeTxFee(txInputs, coins, address)
const txFee = computeTxFee(txInputs, address, coins)

return Math.max(coins - txFee, 0)
}

async function getTxFee(coins, address) {
const txInputs = await prepareTxInputs(coins, address)
async function getTxFee(address, coins) {
const txInputs = await prepareTxInputs(address, coins)

return computeTxFee(txInputs, coins, address)
return computeTxFee(txInputs, address, coins)
}

async function getBalance() {
Expand All @@ -195,7 +195,7 @@ const CardanoWallet = async (options) => {
return utxo.coins > addedCost
}

async function prepareTxInputs(coins, address) {
async function prepareTxInputs(address, coins) {
// we do it pseudorandomly to guarantee fee computation stability
const randomGenerator = PseudoRandom(state.randomSeed)
const utxos = shuffleArray(await getUnspentTxOutputs(), randomGenerator)
Expand All @@ -209,13 +209,13 @@ const CardanoWallet = async (options) => {
txInputs.push(TxInputFromUtxo(profitableUtxos[i]))
sumUtxos += profitableUtxos[i].coins

totalCoins = coins + computeTxFee(txInputs, totalCoins, address)
totalCoins = coins + computeTxFee(txInputs, address, totalCoins)
}

return txInputs
}

function computeTxFee(txInputs, coins, address) {
function computeTxFee(txInputs, address, coins) {
if (coins > Number.MAX_SAFE_INTEGER) {
throw new Error(`Unsupported amount of coins: ${coins}`)
}
Expand All @@ -225,7 +225,7 @@ const CardanoWallet = async (options) => {
}, 0)

//first we try one output transaction
const oneOutputFee = txFeeFunction(estimateTxSize(txInputs, coins, address, false))
const oneOutputFee = txFeeFunction(estimateTxSize(txInputs, address, coins, false))

/*
* if (coins+oneOutputFee) is equal to (txInputsCoinsSum) it means there is no change necessary
Expand All @@ -236,7 +236,7 @@ const CardanoWallet = async (options) => {
return oneOutputFee
} else {
//we try to compute fee for 2 output tx
const twoOutputFee = txFeeFunction(estimateTxSize(txInputs, coins, address, true))
const twoOutputFee = txFeeFunction(estimateTxSize(txInputs, address, coins, true))
if (coins + twoOutputFee > txInputsCoinsSum) {
//means one output transaction was possible, while 2 output is not
//so we return fee equal to inputs - coins which is guaranteed to pass
Expand All @@ -247,7 +247,7 @@ const CardanoWallet = async (options) => {
}
}

function estimateTxSize(txInputs, coins, outAddress, hasChange) {
function estimateTxSize(txInputs, outAddress, coins, hasChange) {
const txInputsSize = cbor.encode(new CborIndefiniteLengthArray(txInputs)).length
const outAddressSize = base58.decode(outAddress).length

Expand Down Expand Up @@ -389,7 +389,7 @@ const CardanoWallet = async (options) => {
getHistory,
isOwnAddress,
getVisibleAddressesWithMeta,
_prepareTxAux: prepareTxAux,
prepareTxAux,
verifyAddress,
fetchTxInfo,
_getNewUtxosFromTxAux: getNewUtxosFromTxAux,
Expand Down
30 changes: 28 additions & 2 deletions app/public/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ p {
path {
fill: currentColor;
}
.link {
text-decoration: underline;
color: var(--brand);
cursor: pointer;
}
/* BUTTON */
button,
.button-like {
Expand Down Expand Up @@ -818,13 +823,20 @@ td {
align-items: center;
font-size: 1.1rem;
font-weight: 600;
float: right;
margin: 15px 5px;
margin: 0 5px;
height: 2.6rem;
width: 200px;
text-align: center;
justify-content: center;
}
.raw-transaction-link {
margin: 0 auto auto 0;
}
.raw-transaction-link-disabled {
margin: 0 auto auto 0;
color: var(--disabled);
pointer-events: none;
}
.loading-button-wrapper {
display: flex;
justify-content: center;
Expand Down Expand Up @@ -1414,6 +1426,20 @@ td {
margin-top: 2rem;
}
}
.raw-unsigned-transaction {
display: flex;
padding: 0.3rem;
margin-bottom: 0.5rem;
word-break: break-all;
background-color: #eee;
}
.submit-row {
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: flex-end;
padding: 15px 0;
}

/* tab-row */
.tab-row {
Expand Down
10 changes: 5 additions & 5 deletions app/tests/src/cardano-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,15 @@ describe('successful transaction fee computation', () => {
mockNet.mockAddressSummaryEndpoint()
mockNet.mockUtxoEndpoint()

assert.equal(await wallets.used.getTxFee(47, myAddress), 179288)
assert.equal(await wallets.used.getTxFee(myAddress, 47), 179288)
})

it('should compute the right transaction fee for shorter outgoing address', async () => {
const mockNet = mockNetwork(mockConfig2)
mockNet.mockAddressSummaryEndpoint()
mockNet.mockUtxoEndpoint()

assert.equal(await wallets.used.getTxFee(47, shortAddress), 177838)
assert.equal(await wallets.used.getTxFee(shortAddress, 47), 177838)
})
})

Expand All @@ -182,7 +182,7 @@ describe('transaction serialization', () => {
mockNet.mockAddressSummaryEndpoint()
mockNet.mockUtxoEndpoint()

const txAux = await wallets.used._prepareTxAux(myAddress, 47)
const txAux = await wallets.used.prepareTxAux(myAddress, 47)

// transaction serialization before providing witnesses
const txAuxSerialized = cbor.encode(txAux).toString('hex')
Expand All @@ -198,7 +198,7 @@ describe('transaction serialization', () => {
mockNet.mockAddressSummaryEndpoint()
mockNet.mockUtxoEndpoint()

const txAux = await wallets.smallUtxos._prepareTxAux(myAddress, 1000000)
const txAux = await wallets.smallUtxos.prepareTxAux(myAddress, 1000000)

assert.equal(txAux.inputs.length, 2)
})
Expand All @@ -208,7 +208,7 @@ describe('transaction serialization', () => {
mockNet.mockAddressSummaryEndpoint()
mockNet.mockUtxoEndpoint()

const txAux = await wallets.used._prepareTxAux(myAddress, 47)
const txAux = await wallets.used.prepareTxAux(myAddress, 47)
const expectedTxHash = '73131c773879e7e634022f8e0175399b7e7814c42684377cf6f8c7a1adb23112'

assert.equal(txAux.getId(), expectedTxHash)
Expand Down

0 comments on commit 65a2ea8

Please sign in to comment.