Skip to content

Commit 889be63

Browse files
author
Mathéo
committed
WIP
1 parent c81cf56 commit 889be63

15 files changed

+283
-92
lines changed

client/HubApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export default class HubApi<
171171
public signTransaction<B extends BehaviorType = DB>(
172172
request: Promise<SignTransactionRequest> | SignTransactionRequest,
173173
requestBehavior: RequestBehavior<B> = this._defaultBehavior as any,
174-
): Promise<B extends BehaviorType.REDIRECT ? void : SignedTransaction> {
174+
): Promise<B extends BehaviorType.REDIRECT ? void : SignedTransaction | SignedTransaction[]> {
175175
return this._request(requestBehavior, RequestType.SIGN_TRANSACTION, [request]);
176176
}
177177

client/PublicRequestTypes.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,19 @@ export interface ChooseAddressResult extends Address {
9292
};
9393
}
9494

95-
export interface SignTransactionRequest extends BasicRequest {
95+
// Transaction data without sender (sender is at request level)
96+
export interface TransactionData {
97+
recipient: string;
98+
recipientType?: Nimiq.AccountType;
99+
value: number;
100+
fee?: number;
101+
extraData?: Bytes;
102+
flags?: number;
103+
validityStartHeight: number; // FIXME To be made optional when hub has its own network
104+
}
105+
106+
// Single transaction request (backward compatible - inline fields)
107+
export interface SignTransactionRequestSingle extends BasicRequest {
96108
sender: string;
97109
recipient: string;
98110
recipientType?: Nimiq.AccountType;
@@ -104,6 +116,15 @@ export interface SignTransactionRequest extends BasicRequest {
104116
validityStartHeight: number; // FIXME To be made optional when hub has its own network
105117
}
106118

119+
// Multi-transaction request (array format)
120+
export interface SignTransactionRequestMulti extends BasicRequest {
121+
sender: string;
122+
recipientLabel?: string; // Label for first recipient (for UI display)
123+
transactions: TransactionData[];
124+
}
125+
126+
export type SignTransactionRequest = SignTransactionRequestSingle | SignTransactionRequestMulti;
127+
107128
export interface SignStakingRequest extends BasicRequest {
108129
senderLabel?: string;
109130
recipientLabel?: string;
@@ -755,7 +776,7 @@ export type RpcRequest = SignTransactionRequest
755776
| ConnectAccountRequest;
756777

757778
export type RpcResult = SignedTransaction
758-
| SignedTransaction[]
779+
| SignedTransaction[] // Can be returned by SIGN_TRANSACTION (multi-tx) or SIGN_STAKING
759780
| PartialSignature
760781
| Account
761782
| Account[]
@@ -779,7 +800,7 @@ export type ResultByRequestType<T> =
779800
T extends RequestType.LIST_CASHLINKS ? Cashlink[] :
780801
T extends RequestType.CHOOSE_ADDRESS ? ChooseAddressResult :
781802
T extends RequestType.ADD_ADDRESS ? Address :
782-
T extends RequestType.SIGN_TRANSACTION ? SignedTransaction :
803+
T extends RequestType.SIGN_TRANSACTION ? SignedTransaction | SignedTransaction[] :
783804
T extends RequestType.SIGN_MULTISIG_TRANSACTION ? PartialSignature :
784805
T extends RequestType.SIGN_STAKING ? SignedTransaction[] :
785806
T extends RequestType.CHECKOUT ? SignedTransaction | SimpleResult :

client/dist/src/HubApi.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default class HubApi<DB extends BehaviorType = BehaviorType.POPUP, IB ext
3232
version: 2;
3333
} ? SimpleResult | SignedTransaction : SignedTransaction>;
3434
chooseAddress<B extends BehaviorType = DB>(request: Promise<ChooseAddressRequest> | ChooseAddressRequest, requestBehavior?: RequestBehavior<B>): Promise<B extends BehaviorType.REDIRECT ? void : ChooseAddressResult>;
35-
signTransaction<B extends BehaviorType = DB>(request: Promise<SignTransactionRequest> | SignTransactionRequest, requestBehavior?: RequestBehavior<B>): Promise<B extends BehaviorType.REDIRECT ? void : SignedTransaction>;
35+
signTransaction<B extends BehaviorType = DB>(request: Promise<SignTransactionRequest> | SignTransactionRequest, requestBehavior?: RequestBehavior<B>): Promise<B extends BehaviorType.REDIRECT ? void : SignedTransaction | SignedTransaction[]>;
3636
signStaking<B extends BehaviorType = DB>(request: Promise<SignStakingRequest> | SignStakingRequest, requestBehavior?: RequestBehavior<B>): Promise<B extends BehaviorType.REDIRECT ? void : SignedTransaction[]>;
3737
signMessage<B extends BehaviorType = DB>(request: Promise<SignMessageRequest> | SignMessageRequest, requestBehavior?: RequestBehavior<B>): Promise<B extends BehaviorType.REDIRECT ? void : SignedMessage>;
3838
signBtcTransaction<B extends BehaviorType = DB>(request: Promise<SignBtcTransactionRequest> | SignBtcTransactionRequest, requestBehavior?: RequestBehavior<B>): Promise<B extends BehaviorType.REDIRECT ? void : SignedBtcTransaction>;

client/dist/src/PublicRequestTypes.d.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,16 @@ export interface ChooseAddressResult extends Address {
7272
};
7373
};
7474
}
75-
export interface SignTransactionRequest extends BasicRequest {
75+
export interface TransactionData {
76+
recipient: string;
77+
recipientType?: Nimiq.AccountType;
78+
value: number;
79+
fee?: number;
80+
extraData?: Bytes;
81+
flags?: number;
82+
validityStartHeight: number;
83+
}
84+
export interface SignTransactionRequestSingle extends BasicRequest {
7685
sender: string;
7786
recipient: string;
7887
recipientType?: Nimiq.AccountType;
@@ -83,6 +92,12 @@ export interface SignTransactionRequest extends BasicRequest {
8392
flags?: number;
8493
validityStartHeight: number;
8594
}
95+
export interface SignTransactionRequestMulti extends BasicRequest {
96+
sender: string;
97+
recipientLabel?: string;
98+
transactions: TransactionData[];
99+
}
100+
export declare type SignTransactionRequest = SignTransactionRequestSingle | SignTransactionRequestMulti;
86101
export interface SignStakingRequest extends BasicRequest {
87102
senderLabel?: string;
88103
recipientLabel?: string;
@@ -615,4 +630,4 @@ export interface SignedPolygonTransaction {
615630
}
616631
export declare type RpcRequest = SignTransactionRequest | SignMultisigTransactionRequest | SignStakingRequest | CreateCashlinkRequest | ManageCashlinkRequest | CheckoutRequest | BasicRequest | SimpleRequest | ChooseAddressRequest | OnboardRequest | RenameRequest | SignMessageRequest | ExportRequest | SignBtcTransactionRequest | AddBtcAddressesRequest | SignPolygonTransactionRequest | SetupSwapRequest | RefundSwapRequest | ConnectAccountRequest;
617632
export declare type RpcResult = SignedTransaction | SignedTransaction[] | PartialSignature | Account | Account[] | SimpleResult | ChooseAddressResult | Address | Cashlink | Cashlink[] | SignedMessage | ExportResult | SignedBtcTransaction | AddBtcAddressesResult | SignedPolygonTransaction | SetupSwapResult | ConnectedAccount;
618-
export declare type ResultByRequestType<T> = T extends RequestType.RENAME ? Account : T extends RequestType.ONBOARD | RequestType.SIGNUP | RequestType.LOGIN | RequestType.MIGRATE | RequestType.LIST ? Account[] : T extends RequestType.LIST_CASHLINKS ? Cashlink[] : T extends RequestType.CHOOSE_ADDRESS ? ChooseAddressResult : T extends RequestType.ADD_ADDRESS ? Address : T extends RequestType.SIGN_TRANSACTION ? SignedTransaction : T extends RequestType.SIGN_MULTISIG_TRANSACTION ? PartialSignature : T extends RequestType.SIGN_STAKING ? SignedTransaction[] : T extends RequestType.CHECKOUT ? SignedTransaction | SimpleResult : T extends RequestType.SIGN_MESSAGE ? SignedMessage : T extends RequestType.LOGOUT | RequestType.CHANGE_PASSWORD ? SimpleResult : T extends RequestType.EXPORT ? ExportResult : T extends RequestType.CREATE_CASHLINK | RequestType.MANAGE_CASHLINK ? Cashlink : T extends RequestType.SIGN_BTC_TRANSACTION ? SignedBtcTransaction : T extends RequestType.SIGN_POLYGON_TRANSACTION ? SignedPolygonTransaction : T extends RequestType.ACTIVATE_BITCOIN ? Account : T extends RequestType.ACTIVATE_POLYGON ? Account : T extends RequestType.ADD_BTC_ADDRESSES ? AddBtcAddressesResult : T extends RequestType.SETUP_SWAP ? SetupSwapResult : T extends RequestType.CONNECT_ACCOUNT ? ConnectedAccount : never;
633+
export declare type ResultByRequestType<T> = T extends RequestType.RENAME ? Account : T extends RequestType.ONBOARD | RequestType.SIGNUP | RequestType.LOGIN | RequestType.MIGRATE | RequestType.LIST ? Account[] : T extends RequestType.LIST_CASHLINKS ? Cashlink[] : T extends RequestType.CHOOSE_ADDRESS ? ChooseAddressResult : T extends RequestType.ADD_ADDRESS ? Address : T extends RequestType.SIGN_TRANSACTION ? SignedTransaction | SignedTransaction[] : T extends RequestType.SIGN_MULTISIG_TRANSACTION ? PartialSignature : T extends RequestType.SIGN_STAKING ? SignedTransaction[] : T extends RequestType.CHECKOUT ? SignedTransaction | SimpleResult : T extends RequestType.SIGN_MESSAGE ? SignedMessage : T extends RequestType.LOGOUT | RequestType.CHANGE_PASSWORD ? SimpleResult : T extends RequestType.EXPORT ? ExportResult : T extends RequestType.CREATE_CASHLINK | RequestType.MANAGE_CASHLINK ? Cashlink : T extends RequestType.SIGN_BTC_TRANSACTION ? SignedBtcTransaction : T extends RequestType.SIGN_POLYGON_TRANSACTION ? SignedPolygonTransaction : T extends RequestType.ACTIVATE_BITCOIN ? Account : T extends RequestType.ACTIVATE_POLYGON ? Account : T extends RequestType.ADD_BTC_ADDRESSES ? AddBtcAddressesResult : T extends RequestType.SETUP_SWAP ? SetupSwapResult : T extends RequestType.CONNECT_ACCOUNT ? ConnectedAccount : never;

demos/Demo.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,23 @@ class Demo {
199199
}
200200
});
201201

202+
document.querySelector('button#sign-multi-transaction')!.addEventListener('click', async () => {
203+
const txRequest = generateSignMultiTransactionRequest();
204+
try {
205+
const result = await demo.client.signTransaction(txRequest, demo._defaultBehavior);
206+
if (demo.isRedirectResult(result)) return;
207+
console.log('Result', result);
208+
if (Array.isArray(result)) {
209+
document.querySelector('#result')!.textContent = `${result.length} transactions signed`;
210+
} else {
211+
document.querySelector('#result')!.textContent = 'TX signed';
212+
}
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);
@@ -259,6 +276,37 @@ class Demo {
259276
};
260277
}
261278

279+
function generateSignMultiTransactionRequest(): SignTransactionRequest {
280+
const $radio = document.querySelector('input[name="address"]:checked');
281+
if (!$radio) {
282+
alert('You have no account to send a tx from, create an account first (signup)');
283+
throw new Error('No account found');
284+
}
285+
const sender = ($radio as HTMLElement).dataset.address!;
286+
const baseFee = parseInt((document.querySelector('#fee') as HTMLInputElement).value, 10) || 100;
287+
const validityStartHeight = parseInt((document.querySelector('#validitystartheight') as HTMLInputElement).value || '1234', 10);
288+
289+
// Sample recipients for multi-transaction demo
290+
const recipients = [
291+
'NQ63 U7XG 1YYE D6FA SXGG 3F5H X403 NBKN JLDU',
292+
'NQ46 2RM7 QE4T 82KR 61Q9 9B7E R38G LBVM N6KY',
293+
'NQ70 APBA 9GCC FL44 D82R UJCD DS4B Y824 3LYJ',
294+
];
295+
296+
return {
297+
appName: 'Hub Demos',
298+
sender,
299+
recipientLabel: 'Alice',
300+
transactions: recipients.map((recipient, index) => ({
301+
recipient,
302+
value: 100000000 + (index * 10000000), // 1 NIM + increments
303+
fee: baseFee + (index * 10),
304+
validityStartHeight: validityStartHeight + index,
305+
extraData: Utf8Tools.stringToUtf8ByteArray(`Multi-tx demo ${index + 1}`),
306+
})),
307+
};
308+
}
309+
262310
async function generateCheckoutRequest(multiCheckout?: boolean): Promise<CheckoutRequest> {
263311
const nimTxValue = parseInt((document.querySelector('#value') as HTMLInputElement).value, 10) || 1337;
264312
const nimTxFee = parseInt((document.querySelector('#fee') as HTMLInputElement).value, 10) || 0;

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-multi-transaction" disabled>Sign Multiple Transactions</button>
9394
</div>
9495

9596
<div class="request">

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,6 @@
7171
"webpack-i18n-tools": "https://github.com/nimiq/webpack-i18n-tools#master",
7272
"webpack-subresource-integrity": "^1.5.2",
7373
"write-file-webpack-plugin": "^4.5.1"
74-
}
74+
},
75+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
7576
}

src/i18n/en.po

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ msgstr ""
250250
msgid "Cashlink created"
251251
msgstr ""
252252

253-
#: src/views/CashlinkManage.vue:229
253+
#: src/views/CashlinkManage.vue:231
254254
msgid "Cashlink created."
255255
msgstr ""
256256

@@ -492,7 +492,7 @@ msgstr ""
492492
msgid "Creating your Account"
493493
msgstr ""
494494

495-
#: src/views/CashlinkManage.vue:231
495+
#: src/views/CashlinkManage.vue:233
496496
#: src/views/SignTransactionLedger.vue:722
497497
msgid "Creating your Cashlink"
498498
msgstr ""
@@ -787,7 +787,7 @@ msgid "OASIS service fee"
787787
msgstr ""
788788

789789
#: src/views/ErrorHandlerUnsupportedLedger.vue:4
790-
#: src/views/RefundSwapLedger.vue:349
790+
#: src/views/RefundSwapLedger.vue:350
791791
msgid "Ok"
792792
msgstr ""
793793

@@ -904,11 +904,11 @@ msgstr ""
904904
msgid "Red Address"
905905
msgstr ""
906906

907-
#: src/views/RefundSwapLedger.vue:319
907+
#: src/views/RefundSwapLedger.vue:320
908908
msgid "Refund Failed"
909909
msgstr ""
910910

911-
#: src/views/RefundSwapLedger.vue:338
911+
#: src/views/RefundSwapLedger.vue:339
912912
msgid "Refunding this Swap is currently not supported on this device or browser. Please use the device and browser you originally created the Swap with. Otherwise please try again in the future."
913913
msgstr ""
914914

@@ -996,7 +996,7 @@ msgstr ""
996996
msgid "Single Accounts"
997997
msgstr ""
998998

999-
#: src/views/CashlinkManage.vue:230
999+
#: src/views/CashlinkManage.vue:232
10001000
#: src/views/CheckoutTransmission.vue:125
10011001
msgid "Something went wrong"
10021002
msgstr ""
@@ -1043,7 +1043,7 @@ msgstr ""
10431043
msgid "Swap Setup Failed"
10441044
msgstr ""
10451045

1046-
#: src/views/RefundSwapLedger.vue:317
1046+
#: src/views/RefundSwapLedger.vue:318
10471047
msgid "Syncing"
10481048
msgstr ""
10491049

@@ -1066,11 +1066,11 @@ msgstr ""
10661066
msgid "Syncing to network..."
10671067
msgstr ""
10681068

1069-
#: src/views/RefundSwapLedger.vue:333
1069+
#: src/views/RefundSwapLedger.vue:334
10701070
msgid "Syncing with {currency} network failed: {error}"
10711071
msgstr ""
10721072

1073-
#: src/views/RefundSwapLedger.vue:327
1073+
#: src/views/RefundSwapLedger.vue:328
10741074
msgid "Syncing with {currency} network..."
10751075
msgstr ""
10761076

@@ -1173,12 +1173,12 @@ msgstr ""
11731173
msgid "Transaction approved"
11741174
msgstr ""
11751175

1176-
#: src/views/CashlinkManage.vue:213
1176+
#: src/views/CashlinkManage.vue:215
11771177
#: src/views/CheckoutTransmission.vue:107
11781178
msgid "Transaction could not be relayed"
11791179
msgstr ""
11801180

1181-
#: src/views/CashlinkManage.vue:211
1181+
#: src/views/CashlinkManage.vue:213
11821182
#: src/views/CheckoutTransmission.vue:105
11831183
msgid "Transaction is expired"
11841184
msgstr ""

0 commit comments

Comments
 (0)