Skip to content

Commit 59a3f2c

Browse files
committed
Update Staking UI (hacky)
Request parsing should happen in SignStakingApi, not ad-hoc in the UI logic.
1 parent 261f4fe commit 59a3f2c

File tree

16 files changed

+240
-147
lines changed

16 files changed

+240
-147
lines changed

client/src/PublicRequest.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ export type SignStakingRequest = SimpleRequest & {
194194
transaction: Uint8Array | Uint8Array[], // An array is only allowed for retire_stake + remove_stake transactions
195195
senderLabel?: string,
196196
recipientLabel?: string,
197+
validatorAddress?: string,
198+
validatorImageUrl?: string,
199+
fromValidatorAddress?: string,
200+
fromValidatorImageUrl?: string,
201+
amount?: number,
197202
};
198203

199204
export type SignBtcTransactionRequestStandard = SimpleRequest & BitcoinTransactionInfo & {

src/lib/RequestParser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ class RequestParser { // eslint-disable-line no-unused-vars
389389
*/
390390
_parseUrl(url, parameterName) {
391391
const parsedUrl = new URL(url);
392-
const whitelistedProtocols = ['https:', 'http:', 'chrome-extension:', 'moz-extension:'];
392+
const whitelistedProtocols = ['https:', 'http:', 'chrome-extension:', 'moz-extension:', 'data:'];
393393
if (!whitelistedProtocols.includes(parsedUrl.protocol)) {
394394
const protocolString = whitelistedProtocols.join(', ');
395395
throw new Errors.InvalidRequestError(`${parameterName} protocol must be one of: ${protocolString}`);

src/request/sign-staking/SignStaking.css

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
flex-direction: column;
2828
justify-content: center;
2929
align-items: center;
30-
padding-top: 2rem;
3130
flex-grow: 1;
3231
}
3332

@@ -118,8 +117,6 @@
118117
#confirm-staking .accounts {
119118
flex-direction: row;
120119
align-items: flex-start;
121-
padding-bottom: 2rem;
122-
border-bottom: 1px solid rgba(31, 35, 72, 0.1);
123120
flex-shrink: 0;
124121
}
125122

@@ -147,10 +144,3 @@
147144
opacity: 0.5;
148145
margin-bottom: 0.25rem;
149146
}
150-
151-
#confirm-staking .data-section {
152-
margin: 1rem 3rem;
153-
text-align: center;
154-
max-width: 100%;
155-
overflow-wrap: break-word;
156-
}

src/request/sign-staking/SignStaking.js

Lines changed: 163 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/* global AddressInfo */
99
/* global NumberFormatting */
1010
/* global lunasToCoins */
11+
/* global I18n */
1112

1213
/**
1314
* @callback SignStaking.resolve
@@ -24,29 +25,175 @@ class SignStaking {
2425
this._request = request;
2526
this.$el = /** @type {HTMLElement} */ (document.getElementById(SignStaking.Pages.CONFIRM_STAKING));
2627

27-
const transaction = request.plain[request.plain.length - 1];
28-
28+
this.$headline = /** @type {HTMLElement} */ (this.$el.querySelector('#headline'));
2929
this.$accountDetails = /** @type {HTMLElement} */ (this.$el.querySelector('#account-details'));
3030

3131
const $sender = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .sender'));
32-
this._senderAddressInfo = new AddressInfo({
33-
userFriendlyAddress: transaction.sender,
34-
label: request.senderLabel || null,
35-
imageUrl: null,
36-
accountLabel: request.keyLabel || null,
37-
});
32+
const $recipient = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .recipient'));
33+
34+
const transaction = request.plain[request.plain.length - 1];
35+
36+
/** @type {Nimiq.Address | undefined} */
37+
let validatorAddress;
38+
39+
let displayValue = transaction.value;
40+
41+
if (transaction.recipientType === 'staking') {
42+
switch (transaction.data.type) {
43+
case 'create-staker':
44+
if (transaction.data.delegation) {
45+
validatorAddress = Nimiq.Address.fromUserFriendlyAddress(transaction.data.delegation);
46+
}
47+
case 'add-stake': // eslint-disable-line no-fallthrough
48+
validatorAddress = validatorAddress || request.validatorAddress;
49+
50+
if (!validatorAddress) {
51+
throw new Errors.InvalidRequestError('No delegation or validatorAddress provided');
52+
}
53+
54+
this.$headline.textContent = I18n.translatePhrase('sign-staking-heading-stake');
55+
this._senderAddressInfo = new AddressInfo({ // From user
56+
userFriendlyAddress: transaction.sender,
57+
label: request.senderLabel || null,
58+
imageUrl: null,
59+
accountLabel: request.keyLabel || null,
60+
});
61+
this._recipientAddressInfo = new AddressInfo({ // To validator
62+
userFriendlyAddress: validatorAddress.toUserFriendlyAddress(),
63+
label: request.recipientLabel || null,
64+
imageUrl: request.validatorImageUrl || null,
65+
accountLabel: null,
66+
});
67+
break;
68+
case 'update-staker': { // Change validator
69+
if (transaction.data.newDelegation) {
70+
validatorAddress = Nimiq.Address.fromUserFriendlyAddress(transaction.data.newDelegation);
71+
}
72+
73+
if (!validatorAddress) {
74+
throw new Errors.InvalidRequestError('No newDelegation provided');
75+
}
76+
77+
if (!request.amount) {
78+
throw new Errors.InvalidRequestError('No amount provided');
79+
}
80+
displayValue = request.amount;
81+
82+
const fromValidatorAddress = request.validatorAddress;
83+
if (!fromValidatorAddress) {
84+
throw new Errors.InvalidRequestError('No fromValidatorAddress provided');
85+
}
86+
87+
this.$headline.textContent = I18n.translatePhrase('sign-staking-heading-change');
88+
this._senderAddressInfo = new AddressInfo({ // From previous validator
89+
userFriendlyAddress: fromValidatorAddress.toUserFriendlyAddress(),
90+
label: request.senderLabel || null,
91+
imageUrl: request.fromValidatorImageUrl || null,
92+
accountLabel: request.keyLabel || null,
93+
});
94+
this._recipientAddressInfo = new AddressInfo({ // To new validator
95+
userFriendlyAddress: validatorAddress.toUserFriendlyAddress(),
96+
label: request.recipientLabel || null,
97+
imageUrl: request.validatorImageUrl || null,
98+
accountLabel: null,
99+
});
100+
break;
101+
}
102+
case 'set-active-stake':
103+
case 'retire-stake':
104+
validatorAddress = request.validatorAddress;
105+
106+
if (!validatorAddress) {
107+
throw new Errors.InvalidRequestError('No validatorAddress provided');
108+
}
109+
110+
if (!request.amount) {
111+
throw new Errors.InvalidRequestError('No amount provided');
112+
}
113+
displayValue = request.amount;
114+
115+
this.$headline.textContent = I18n.translatePhrase('sign-staking-heading-unstake');
116+
this._senderAddressInfo = new AddressInfo({ // From validator
117+
userFriendlyAddress: validatorAddress.toUserFriendlyAddress(),
118+
label: request.recipientLabel || null,
119+
imageUrl: request.validatorImageUrl || null,
120+
accountLabel: null,
121+
});
122+
this._recipientAddressInfo = new AddressInfo({ // To User
123+
userFriendlyAddress: transaction.sender,
124+
label: request.senderLabel || null,
125+
imageUrl: null,
126+
accountLabel: request.keyLabel || null,
127+
});
128+
break;
129+
case 'create-validator':
130+
case 'update-validator':
131+
case 'deactivate-validator':
132+
case 'reactivate-validator':
133+
case 'retire-validator':
134+
default:
135+
this.$headline.textContent = I18n.translatePhrase('sign-tx-heading-tx');
136+
this._senderAddressInfo = new AddressInfo({
137+
userFriendlyAddress: transaction.sender,
138+
label: request.senderLabel || null,
139+
imageUrl: null,
140+
accountLabel: request.keyLabel || null,
141+
});
142+
this._recipientAddressInfo = new AddressInfo({
143+
userFriendlyAddress: transaction.recipient,
144+
label: request.recipientLabel || null,
145+
imageUrl: null,
146+
accountLabel: null,
147+
});
148+
break;
149+
}
150+
} else {
151+
switch (transaction.senderData.type) {
152+
case 'remove-stake':
153+
validatorAddress = request.validatorAddress;
154+
155+
if (!validatorAddress) {
156+
throw new Errors.InvalidRequestError('No validatorAddress provided');
157+
}
158+
159+
this.$headline.textContent = I18n.translatePhrase('sign-staking-heading-unstake');
160+
this._senderAddressInfo = new AddressInfo({ // From validator
161+
userFriendlyAddress: validatorAddress.toUserFriendlyAddress(),
162+
label: request.senderLabel || null,
163+
imageUrl: request.validatorImageUrl || null,
164+
accountLabel: null,
165+
});
166+
this._recipientAddressInfo = new AddressInfo({ // To User
167+
userFriendlyAddress: transaction.recipient,
168+
label: request.recipientLabel || null,
169+
imageUrl: null,
170+
accountLabel: request.keyLabel || null,
171+
});
172+
break;
173+
case 'delete-validator':
174+
default:
175+
this.$headline.textContent = I18n.translatePhrase('sign-tx-heading-tx');
176+
this._senderAddressInfo = new AddressInfo({
177+
userFriendlyAddress: transaction.sender,
178+
label: request.senderLabel || null,
179+
imageUrl: null,
180+
accountLabel: null,
181+
});
182+
this._recipientAddressInfo = new AddressInfo({
183+
userFriendlyAddress: transaction.recipient,
184+
label: request.recipientLabel || null,
185+
imageUrl: null,
186+
accountLabel: request.keyLabel || null,
187+
});
188+
break;
189+
}
190+
}
191+
38192
this._senderAddressInfo.renderTo($sender);
39193
$sender.addEventListener('click', () => {
40194
this._openDetails(this._senderAddressInfo);
41195
});
42196

43-
const $recipient = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .recipient'));
44-
this._recipientAddressInfo = new AddressInfo({
45-
userFriendlyAddress: transaction.recipient,
46-
label: request.recipientLabel || null,
47-
imageUrl: null,
48-
accountLabel: null,
49-
});
50197
this._recipientAddressInfo.renderTo($recipient);
51198
$recipient.addEventListener('click', () => {
52199
this._openDetails(this._recipientAddressInfo);
@@ -57,23 +204,15 @@ class SignStaking {
57204

58205
const $value = /** @type {HTMLDivElement} */ (this.$el.querySelector('#value'));
59206
const $fee = /** @type {HTMLDivElement} */ (this.$el.querySelector('#fee'));
60-
const $data = /** @type {HTMLDivElement} */ (this.$el.querySelector('#data'));
61207

62208
// Set value and fee.
63-
$value.textContent = NumberFormatting.formatNumber(lunasToCoins(transaction.value));
209+
$value.textContent = NumberFormatting.formatNumber(lunasToCoins(displayValue));
64210
if ($fee && transaction.fee > 0) {
65211
$fee.textContent = NumberFormatting.formatNumber(lunasToCoins(transaction.fee));
66212
const $feeSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.fee-section'));
67213
$feeSection.classList.remove('display-none');
68214
}
69215

70-
if ($data && transaction.data.raw.length) {
71-
// Set transaction extra data.
72-
$data.textContent = this._formatData(transaction);
73-
const $dataSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.data-section'));
74-
$dataSection.classList.remove('display-none');
75-
}
76-
77216
// Set up password box.
78217
const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('#password-box'));
79218
this._passwordBox = new PasswordBox($passwordBox, {
@@ -161,114 +300,6 @@ class SignStaking {
161300
// Go to start page
162301
window.location.hash = SignStaking.Pages.CONFIRM_STAKING;
163302
}
164-
165-
/**
166-
* @param {Nimiq.PlainTransaction} plain
167-
* @returns {string}
168-
*/
169-
_formatData(plain) {
170-
console.log(plain);
171-
// That either the recipient or the sender is a staking account type is validated in SignStakingApi
172-
if (plain.recipientType === 'basic') {
173-
switch (plain.data.type) {
174-
case 'create-staker': {
175-
let text = 'Start staking';
176-
const { delegation } = plain.data;
177-
if (delegation) {
178-
text += ` with validator ${delegation}`;
179-
} else {
180-
text += ' with no validator';
181-
}
182-
return text;
183-
}
184-
case 'update-staker': {
185-
let text = 'Change validator';
186-
const { newDelegation, reactivateAllStake } = plain.data;
187-
if (newDelegation) {
188-
text += ` to validator ${newDelegation}`;
189-
} else {
190-
text += ' to no validator';
191-
}
192-
if (reactivateAllStake) {
193-
text += ' and reactivate all stake';
194-
}
195-
return text;
196-
}
197-
case 'add-stake': {
198-
const { staker } = plain.data;
199-
return `Add stake to ${staker}`;
200-
}
201-
case 'set-active-stake': {
202-
const { newActiveBalance } = plain.data;
203-
return `Set active stake to ${newActiveBalance / 1e5} NIM`;
204-
}
205-
case 'retire-stake': {
206-
const { retireStake } = plain.data;
207-
return `Retire ${retireStake / 1e5} NIM stake`;
208-
}
209-
case 'create-validator': {
210-
let text = `Create validator ${plain.sender}`;
211-
const { rewardAddress } = plain.data;
212-
if (rewardAddress !== plain.sender) {
213-
text += ` with reward address ${rewardAddress}`;
214-
}
215-
// TODO: Somehow let users see validator key, signing key, and signal data that they are signing
216-
return text;
217-
}
218-
case 'update-validator': {
219-
let text = `Update validator ${plain.sender}`;
220-
const {
221-
newRewardAddress,
222-
newVotingKey,
223-
newSigningKey,
224-
newSignalData,
225-
} = plain.data;
226-
text += ` ${plain.sender}`;
227-
if (newRewardAddress) {
228-
text += `, updating reward address to ${newRewardAddress}`;
229-
}
230-
if (newVotingKey) {
231-
text += ', updating voting key';
232-
}
233-
if (newSigningKey) {
234-
text += ', updating signing key';
235-
}
236-
if (newSignalData) {
237-
text += ', updating signal data';
238-
}
239-
return text;
240-
}
241-
case 'deactivate-validator': {
242-
const { validator } = plain.data;
243-
return `Deactivate validator ${validator}`;
244-
}
245-
case 'reactivate-validator': {
246-
const { validator } = plain.data;
247-
return `Reactivate validator ${validator}`;
248-
}
249-
case 'retire-validator': {
250-
return `Retire validator ${plain.sender}`;
251-
}
252-
default: {
253-
return `Unrecognized in-staking data: ${plain.data.type} - ${plain.data.raw}`;
254-
}
255-
}
256-
} else {
257-
switch (plain.senderData.type) {
258-
case 'remove-stake': {
259-
return 'Unstake';
260-
}
261-
case 'delete-validator': {
262-
// Cannot show the validator address here, as the recipient can be any address and the validator
263-
// address is the signer, which the Keyguard only knows after password entry.
264-
return 'Delete validator';
265-
}
266-
default: {
267-
return `Unrecognized out-staking data: ${plain.senderData.type} - ${plain.senderData.raw}`;
268-
}
269-
}
270-
}
271-
}
272303
}
273304

274305
SignStaking.Pages = {

0 commit comments

Comments
 (0)