Skip to content

Commit 1d1dff8

Browse files
committed
Allow selecting a sender in the sign-transaction flow
With an address list gated by required balance and auto-updating balances.
1 parent 477d8b7 commit 1d1dff8

File tree

11 files changed

+272
-35
lines changed

11 files changed

+272
-35
lines changed

client/PublicRequestTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export interface ChooseAddressResult extends Address {
9393
}
9494

9595
export interface SignTransactionRequest extends BasicRequest {
96-
sender: string;
96+
sender?: string;
9797
recipient: string;
9898
recipientType?: Nimiq.AccountType;
9999
recipientLabel?: string;

demos/Demo.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class Demo {
182182
});
183183

184184
document.querySelector('button#sign-transaction')!.addEventListener('click', async () => {
185-
const txRequest = generateSignTransactionRequest();
185+
const txRequest = generateSignTransactionRequest(true);
186186
try {
187187
const result = await demo.client.signTransaction(
188188
new Promise<SignTransactionRequest>((resolve) => {
@@ -199,6 +199,23 @@ class Demo {
199199
}
200200
});
201201

202+
document.querySelector('button#sign-transaction-without-sender')!.addEventListener('click', async () => {
203+
const txRequest = generateSignTransactionRequest(false);
204+
try {
205+
const result = await demo.client.signTransaction(
206+
new Promise<SignTransactionRequest>((resolve) => {
207+
window.setTimeout(() => resolve(txRequest), 2000);
208+
}),
209+
demo._defaultBehavior,
210+
);
211+
console.log('Result', result);
212+
document.querySelector('#result')!.textContent = 'TX signed';
213+
} catch (e) {
214+
console.error(e);
215+
document.querySelector('#result')!.textContent = `Error: ${e.message || e}`;
216+
}
217+
});
218+
202219
document.querySelector('button#onboard')!.addEventListener('click', async () => {
203220
try {
204221
const result = await demo.client.onboard({ appName: 'Hub Demos' }, demo._defaultBehavior);
@@ -235,13 +252,16 @@ class Demo {
235252
}
236253
});
237254

238-
function generateSignTransactionRequest(): SignTransactionRequest {
239-
const $radio = document.querySelector('input[name="address"]:checked');
240-
if (!$radio) {
241-
alert('You have no account to send a tx from, create an account first (signup)');
242-
throw new Error('No account found');
255+
function generateSignTransactionRequest(withSender: boolean): SignTransactionRequest {
256+
let sender: string | undefined;
257+
if (withSender) {
258+
const $radio = document.querySelector('input[name="address"]:checked');
259+
if (!$radio) {
260+
alert('You have no account to send a tx from, create an account first (signup)');
261+
throw new Error('No account found');
262+
}
263+
sender = ($radio as HTMLElement).dataset.address!;
243264
}
244-
const sender = ($radio as HTMLElement).dataset.address!;
245265
const value = parseInt((document.querySelector('#value') as HTMLInputElement).value, 10) || 1337;
246266
const fee = parseInt((document.querySelector('#fee') as HTMLInputElement).value, 10) || 0;
247267
const txData = (document.querySelector('#data') as HTMLInputElement).value || '';

demos/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ <h2>Transactions</h2>
9090
<br><button id="checkout" disabled>Checkout</button>
9191
<br><button id="multi-checkout" disabled>Multi Checkout</button>
9292
<br><button id="sign-transaction" disabled>Sign Transaction</button>
93+
<br><button id="sign-transaction-without-sender" disabled>Sign Transaction without Sender</button>
9394
</div>
9495

9596
<div class="request">

src/components/CheckoutCardNimiq.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ class CheckoutCardNimiq
428428
}
429429
430430
private get hasEligibleAddress(): boolean {
431-
432431
const recipientAddress = this.paymentOptions.protocolSpecific.recipient
433432
? this.paymentOptions.protocolSpecific.recipient.toUserFriendlyAddress()
434433
: '';

src/lib/RequestParser.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ export class RequestParser {
7676
return {
7777
kind: requestType,
7878
appName: signTransactionRequest.appName,
79-
sender: Nimiq.Address.fromString(signTransactionRequest.sender),
79+
sender: signTransactionRequest.sender
80+
? Nimiq.Address.fromString(signTransactionRequest.sender)
81+
: undefined,
8082
recipient: Nimiq.Address.fromString(signTransactionRequest.recipient),
8183
recipientType: signTransactionRequest.recipientType || Nimiq.AccountType.Basic,
8284
recipientLabel: signTransactionRequest.recipientLabel,
@@ -875,10 +877,13 @@ export class RequestParser {
875877
const signTransactionRequest = request as ParsedSignTransactionRequest;
876878
return {
877879
appName: signTransactionRequest.appName,
878-
sender: signTransactionRequest.sender instanceof Nimiq.Address
879-
? signTransactionRequest.sender.toUserFriendlyAddress()
880-
// Note: additional sender information is lost and does not survive reloads, see RequestTypes.ts
881-
: signTransactionRequest.sender.address.toUserFriendlyAddress(),
880+
sender: signTransactionRequest.sender
881+
? signTransactionRequest.sender instanceof Nimiq.Address
882+
? signTransactionRequest.sender.toUserFriendlyAddress()
883+
// Note: additional sender information is lost and does not survive reloads,
884+
// see RequestTypes.ts
885+
: signTransactionRequest.sender.address.toUserFriendlyAddress()
886+
: undefined,
882887
recipient: signTransactionRequest.recipient.toUserFriendlyAddress(),
883888
recipientType: signTransactionRequest.recipientType,
884889
recipientLabel: signTransactionRequest.recipientLabel,

src/lib/RequestTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export interface ParsedSignTransactionRequest extends ParsedBasicRequest {
3636
// The sender object is currently only for internal use in RefundSwapLedger and can not be set in public request.
3737
// Note that the object does not get exported to the history state in RpcApi and therefore does not survive reloads.
3838
// However, the RefundSwapLedger handler is built in a way that it starts over on reloads to avoid the problem.
39-
sender: Nimiq.Address | {
39+
sender?: Nimiq.Address | {
4040
address: Nimiq.Address,
4141
label?: string,
4242
walletLabel?: string,

src/lib/RpcApi.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { ERROR_CANCELED, StakingTransactionType, WalletType } from './Constants'
3333
import { includesOrigin } from '@/lib/Helpers';
3434
import Config from 'config';
3535
import { setHistoryStorage, getHistoryStorage } from '@/lib/Helpers';
36-
import { WalletInfo } from './WalletInfo';
36+
import type { WalletInfo } from './WalletInfo';
3737

3838
export default class RpcApi {
3939
public static PERMISSIONED_REQUESTS: RequestType[] = [
@@ -332,12 +332,14 @@ export default class RpcApi {
332332
account = await WalletStore.Instance.get((request as ParsedSimpleRequest).walletId);
333333
errorMsg = 'AccountId not found';
334334
} else if (requestType === RequestType.SIGN_TRANSACTION) {
335-
accountRequired = true;
335+
accountRequired = false;
336336
const parsedSignTransactionRequest = request as ParsedSignTransactionRequest;
337-
const address = parsedSignTransactionRequest.sender instanceof Nimiq.Address
338-
? parsedSignTransactionRequest.sender
339-
: parsedSignTransactionRequest.sender.address;
340-
account = this._store.getters.findWalletByAddress(address.toUserFriendlyAddress(), true);
337+
if (parsedSignTransactionRequest.sender) {
338+
const address = parsedSignTransactionRequest.sender instanceof Nimiq.Address
339+
? parsedSignTransactionRequest.sender
340+
: parsedSignTransactionRequest.sender.address;
341+
account = this._store.getters.findWalletByAddress(address.toUserFriendlyAddress(), true);
342+
}
341343
} else if (requestType === RequestType.SIGN_STAKING) {
342344
accountRequired = true;
343345
// Only support signing staking transactions by the tx's sender or recipient. Note that sending to or

src/views/ErrorHandler.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,13 @@ export default class ErrorHandler extends Vue {
117117
// The wallet can be found by the (optional) sender/signer address in the Hub request
118118
const messageSigner = (this.request as ParsedSignMessageRequest).signer;
119119
const transactionSender = (this.request as ParsedSignTransactionRequest).sender;
120-
const address = messageSigner || (transactionSender instanceof Nimiq.Address
121-
? transactionSender
122-
: transactionSender.address);
123-
return this.findWalletByAddress(address.toUserFriendlyAddress(), true);
120+
const maybeAddress = messageSigner || transactionSender;
121+
if (maybeAddress) {
122+
const address = maybeAddress instanceof Nimiq.Address
123+
? maybeAddress
124+
: maybeAddress.address;
125+
return this.findWalletByAddress(address.toUserFriendlyAddress(), true);
126+
}
124127
} else if (this.request.kind === RequestType.CHECKOUT
125128
|| this.request.kind === RequestType.SIGN_MESSAGE) {
126129
// The keyId of the selected address is in the keyguardRequest

src/views/RefundSwapLedger.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ export default class RefundSwapLedger extends RefundSwap {
104104
105105
const request = this.request as ParsedSignTransactionRequest;
106106
const { sender: senderInfo, recipient, value, fee, data, validityStartHeight } = request;
107+
if (!senderInfo) {
108+
this.$rpc.reject(new Error('Ledger Swap Refunding expects a sender in the request.'));
109+
return;
110+
}
107111
const sender = senderInfo instanceof Nimiq.Address ? senderInfo : senderInfo.address;
108112
// existence guaranteed as already checked previously in RefundSwap
109113
const ledgerAccount = this.findWalletByAddress(recipient.toUserFriendlyAddress(), true)!;

0 commit comments

Comments
 (0)