Skip to content

Commit

Permalink
updated BTCTransactionInfo layout
Browse files Browse the repository at this point in the history
- Added fiat value
- Added time estimation as well as fee costs (in BTC and fiat currency)
  • Loading branch information
onmax committed Feb 23, 2022
1 parent 9b15cd9 commit e9a19ed
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 17 deletions.
17 changes: 15 additions & 2 deletions client/src/PublicRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ export type SignTransactionRequest

export type SignBtcTransactionRequestStandard = SimpleRequest & BitcoinTransactionInfo & {
layout?: 'standard',

// data needed for display
fiatCurrency?: string,
fiatRate?: number,
delay?: number,
feePerByte?: number,
};

export type SignBtcTransactionRequestCheckout = SimpleRequest & BitcoinTransactionInfo & {
Expand All @@ -183,9 +189,16 @@ export type SignBtcTransactionRequestCheckout = SimpleRequest & BitcoinTransacti
shopLogoUrl?: string,
time?: number,
expires?: number,
fiatCurrency?: string,
fiatAmount?: number,
vendorMarkup?: number,

// data needed for display
fiatCurrency: string,
fiatRate: number,

/**
* @deprecated use fiatRate and sum of inputs instead
*/
fiatAmount?: number,
};

export type SignBtcTransactionRequest
Expand Down
63 changes: 61 additions & 2 deletions src/request/sign-btc-transaction/SignBtcTransaction.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,66 @@
font-weight: bold;
}

#confirm-transaction .fee-section {
#confirm-transaction .fee-section,
#confirm-transaction .fiat-section,
#confirm-transaction .estimation-section .speed-gauge,
#confirm-transaction .estimation-section .values,
#confirm-transaction .estimation-section .info-icon {
opacity: 0.5;
margin-bottom: 0.25rem;
margin-top: 0;
}

#confirm-transaction .estimation-section {
display: flex;
justify-content: center;
align-items: center;
gap: 1.5rem;
}

#confirm-transaction .needle,
#confirm-transaction .needle-cover {
transform-origin: 50% 54.1666667%;
transition: transform 0.3s var(--nimiq-ease);
}

#confirm-transaction .needle-cover {
fill: white;
}

#confirm-transaction .estimation-section .info-icon {
margin-top: 6px;
}

#confirm-transaction .tooltip {
margin-top: -.25rem;
align-self: flex-start;
}

#confirm-transaction .tooltip-box {
--tooltip-width: 250px;
width: var(--tooltip-width);
right: calc(var(--tooltip-width) * -1 / 4);
}

#confirm-transaction .tx-info:hover .tooltip::after,
#confirm-transaction .tx-info:hover .tooltip .tooltip-box {
visibility: visible;
opacity: 1;
}

#confirm-transaction .tooltip-box > p,
#confirm-transaction .network-fee .network-fee__btc {
opacity: 0.6;
}

#confirm-transaction .tooltip-box p:first-child {
margin-top: 0.75rem;
}


#confirm-transaction .network-fee {
display: flex;
flex-direction: column;
}


107 changes: 98 additions & 9 deletions src/request/sign-btc-transaction/SignBtcTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/* global SignBtcTransactionApi */
/* global PasswordBox */
/* global Errors */
/* global TemplateTags */
/* global Utf8Tools */
/* global TopLevelApi */
/* global PaymentInfoLine */
Expand All @@ -23,7 +24,7 @@

class SignBtcTransaction {
/**
* @param {Parsed<KeyguardRequest.SignBtcTransactionRequest>} request
* @param {Required<Parsed<KeyguardRequest.SignBtcTransactionRequest>>} request
* @param {SignBtcTransaction.resolve} resolve
* @param {reject} reject
*/
Expand Down Expand Up @@ -94,16 +95,104 @@ class SignBtcTransaction {

/** @type {HTMLDivElement} */
const $value = (this.$el.querySelector('#value'));
/** @type {HTMLDivElement} */
const $fee = (this.$el.querySelector('#fee'));
const amountBTC = BitcoinUtils.satoshisToCoins(recipientOutput.value);
$value.textContent = NumberFormatting.formatNumber(amountBTC, 8);

// Set value and fee.
$value.textContent = NumberFormatting.formatNumber(BitcoinUtils.satoshisToCoins(recipientOutput.value), 8);
if ($fee && fee > 0) {
$fee.textContent = NumberFormatting.formatNumber(BitcoinUtils.satoshisToCoins(fee), 8);
const feeBTC = NumberFormatting.formatNumber(BitcoinUtils.satoshisToCoins(fee), 8);

// Right now, we have two types of layouts. See #445
if (request.layout === SignBtcTransactionApi.Layouts.CHECKOUT) {
/** @type {HTMLDivElement} */
const $feeSection = (this.$el.querySelector('.fee-section'));
$feeSection.classList.remove('display-none');
const $fee = (this.$el.querySelector('#fee'));

if ($fee && fee > 0) {
$fee.textContent = feeBTC;
/** @type {HTMLDivElement} */
const $feeSection = (this.$el.querySelector('.fee-section'));
$feeSection.classList.remove('display-none');
}
} else if (request.layout === SignBtcTransactionApi.Layouts.STANDARD) {
/** @type {HTMLSpanElement|null} */
const $fiat = (this.$el.querySelector('#fiat'));
if (!$fiat) return;

const {
fiatCurrency: currency, fiatRate, delay, feePerByte,
} = request;

const amountFiat = fiatRate * amountBTC;
$fiat.textContent = `~${NumberFormatting.formatCurrency(amountFiat, currency)}`;


/** @type {HTMLDivElement|null} */
const $fiatSection = (this.$el.querySelector('.fiat-section'));
if (!$fiatSection) return;
$fiatSection.classList.remove('display-none');

/** @type {HTMLDivElement|null} */
const $estimationSection = (this.$el.querySelector('.estimation-section'));
if (!$estimationSection) return;
$estimationSection.classList.remove('display-none');

let estimatedDuration = '';
let speed = 50;
if (delay === 1) { estimatedDuration = I18n.translatePhrase('sign-btc-tx-15m'); speed = 100; }
if (delay === 12) { estimatedDuration = I18n.translatePhrase('sign-btc-tx-2-4h'); speed = 50; }
if (delay === 36) { estimatedDuration = I18n.translatePhrase('sign-btc-tx-6h-plus'); speed = 0; }

const rotation = (Math.min(100, Math.max(0, speed)) / 100 - 1) * 180;
this.$el.querySelectorAll('#speed-gauge-icon g > path').forEach($path => {
// @ts-ignore
$path.style.transform = `rotate(${rotation}deg)`;
});

if (!request.changeOutput || !request.changeOutput.value) return;
const fiatAmount = NumberFormatting.formatCurrency(BitcoinUtils.satoshisToCoins(fee) * fiatRate, currency);

/** @type {HTMLDivElement|null} */
const $values = ($estimationSection.querySelector('#values'));
if (!$values) return;
$values.innerText = `${estimatedDuration} / ${fiatAmount}`;

/** @type {HTMLDivElement|null} */
const $tooltip = document.querySelector('#txInfo');
if (!$tooltip) return;
$tooltip.classList.add('tooltip', 'top');
$tooltip.tabIndex = 0; // make the tooltip focusable

/* eslint-disable indent */
$tooltip.innerHTML = TemplateTags.hasVars(2)`
<svg class="info-icon nq-icon">
<use xlink:href="../../../node_modules/@nimiq/style/nimiq-style.icons.svg#nq-info-circle-small"/>
</svg>
<div class="tooltip-box">
<div class="network-fee">
<span>
<label data-i18n="sign-btc-tx-network-fee">
Network fee
</label>
: ${feePerByte}
<label data-i18n="sign-btc-tx-sat-byte">
sat/byte
</label>
</span>
<span class="network-fee__btc">
${feeBTC}
<span class="btc-symbol"></span>
</span>
</div>
<p data-i18n="sign-btc-tx-fee-description">
Increase the speed of your transaction by paying a higher network fee.
The fees go directly to the miners.
<p>
<p data-i18n="sign-btc-tx-fee-values-estimations">
Duration and fees are estimates.
</p>
</div>
`;
/* eslint-enable indent */

I18n.translateDom($tooltip);
}

// Set up password box.
Expand Down
26 changes: 23 additions & 3 deletions src/request/sign-btc-transaction/SignBtcTransactionApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ class SignBtcTransactionApi extends BitcoinRequestParserMixin(TopLevelApi) {
+ 'sequence number < 0xffffffff');
}
parsedRequest.layout = this.parseLayout(request.layout);

parsedRequest.fiatRate = this.parseNonNegativeFiniteNumber(request.fiatRate) || 0;
if (!parsedRequest.fiatRate) {
throw new Errors.InvalidRequestError('fiatRate must be defined and different to 0');
}
parsedRequest.fiatCurrency = this.parseFiatCurrency(request.fiatCurrency) || '';
if (!parsedRequest.fiatCurrency) {
throw new Errors.InvalidRequestError('fiatCurrency must be defined and different to empty string');
}

if (request.layout === SignBtcTransactionApi.Layouts.CHECKOUT
&& parsedRequest.layout === SignBtcTransactionApi.Layouts.CHECKOUT) {
parsedRequest.shopOrigin = this.parseShopOrigin(request.shopOrigin);
Expand All @@ -44,9 +54,8 @@ class SignBtcTransactionApi extends BitcoinRequestParserMixin(TopLevelApi) {
}

parsedRequest.fiatAmount = this.parseNonNegativeFiniteNumber(request.fiatAmount);
parsedRequest.fiatCurrency = this.parseFiatCurrency(request.fiatCurrency);
if ((parsedRequest.fiatAmount === undefined) !== (parsedRequest.fiatCurrency === undefined)) {
throw new Errors.InvalidRequestError('fiatAmount and fiatCurrency must be both defined or undefined.');
if (parsedRequest.fiatCurrency !== undefined) {
throw new Errors.InvalidRequestError('fiatAmount is deprecated. Use fiatRate instead.');
}

parsedRequest.vendorMarkup = this.parseVendorMarkup(request.vendorMarkup);
Expand All @@ -60,6 +69,17 @@ class SignBtcTransactionApi extends BitcoinRequestParserMixin(TopLevelApi) {
throw new Errors.InvalidRequestError('`expires` must be greater than `time`');
}
}
} else if (request.layout === SignBtcTransactionApi.Layouts.STANDARD
&& parsedRequest.layout === SignBtcTransactionApi.Layouts.STANDARD) {
parsedRequest.delay = this.parseNonNegativeFiniteNumber(request.delay) || 0;
if (!parsedRequest.delay) {
throw new Errors.InvalidRequestError('delay must be defined.');
}

parsedRequest.feePerByte = this.parseNonNegativeFiniteNumber(request.feePerByte) || 0;
if (!parsedRequest.feePerByte) {
throw new Errors.InvalidRequestError('feePerByte must be defined.');
}
}

return parsedRequest;
Expand Down
27 changes: 27 additions & 0 deletions src/request/sign-btc-transaction/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,33 @@ <h1 data-i18n="sign-tx-heading-tx" class="nq-h1 hide-checkout hide-cashlink">Con
<div class="fee-section nq-text-s display-none">
+ <span id="fee"></span> <span class="btc-symbol"></span> <span data-i18n="sign-tx-fee">fee</span>
</div>

<div class="fiat-section nq-text-s display-none">
<span id="fiat"></span>
</div>
</div>

<div class="estimation-section nq-text-s display-none">
<svg id="speed-gauge-icon" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"
fill="currentColor" class="speed-gauge">
<path d="M12 0a12 12 0 110 24 12 12 0 010-24zM4.67 18.8a15.84 15.84 0 0114.66 0 10 10 0 10-14.66 0z" />
<!-- housing -->
<path d="M5.5 11.25H4a.75.75 0 100 1.5h1.5a.75.75 0 100-1.5z" /> <!-- 1/5 -->
<path d="M6.97 5.91a.75.75 0 10-1.06 1.06l1.06 1.06a.75.75 0 101.06-1.06L6.97 5.91z" /> <!-- 2/5 -->
<path d="M12 6.25a.75.75 0 01-.75-.75V4a.75.75 0 111.5 0v1.5a.75.75 0 01-.75.75z" /> <!-- 3/5 -->
<path d="M17.03 5.91a.75.75 0 111.06 1.06l-1.06 1.06a.75.75 0 11-1.06-1.06l1.06-1.06z" /> <!-- 4/5 -->
<path d="M18.5 11.25H20a.75.75 0 110 1.5h-1.5a.75.75 0 110-1.5z" /> <!-- 5/5 -->
<g transform="translate(0 -1)">
<path
d="M21.02 13.07a.5.5 0 00-.38-.49c-.78-.2-7.64-1.93-8.7-1.94a2.39 2.39 0 00-2.4 2.38 2.42 2.42 0 002.37 2.4c1.07 0 7.95-1.67 8.73-1.86a.5.5 0 00.38-.49z"
class="needle-cover" />
<path
d="M21.02 13.07a.5.5 0 00-.38-.49c-.78-.2-7.64-1.93-8.7-1.94a2.39 2.39 0 00-2.4 2.38 2.42 2.42 0 002.37 2.4c1.07 0 7.95-1.67 8.73-1.86a.5.5 0 00.38-.49z"
class="needle" />
</g>
</svg>
<div id="values" class="values"></div>
<div id="txInfo" class="tx-info"></div>
</div>
</div>

Expand Down
11 changes: 10 additions & 1 deletion src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,14 @@
"sign-swap-exchange-fee": "Swap fee",
"sign-swap-of-exchange-value": "of swap value.",
"sign-swap-total-fees": "Total fees",
"sign-swap-your-bank": "Your bank"
"sign-swap-your-bank": "Your bank",

"sign-btc-tx-15m": "15m",
"sign-btc-tx-2-4h": "2-4h",
"sign-btc-tx-6h-plus": "6h+",
"sign-btc-tx-network-fee": "Network fee",
"sign-btc-tx-sat-byte": "sat/byte",
"sign-btc-tx-fee-description": "Increase the speed of your transaction by paying a higher network fee. The fees go directly to the miners.",
"sign-btc-tx-fee-values-estimations": "Duration and fees are estimates.",
"sign-btc-tx-fee": "fee"
}

0 comments on commit e9a19ed

Please sign in to comment.