hodl-wallet 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/app/account.d.ts +7 -0
- package/dist/app/account.js +248 -0
- package/dist/app/account.js.map +1 -0
- package/dist/app/format.d.ts +3 -0
- package/dist/app/format.js +27 -0
- package/dist/app/format.js.map +1 -0
- package/dist/app/index.d.ts +8 -0
- package/dist/app/index.js +7 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/menu.d.ts +12 -0
- package/dist/app/menu.js +78 -0
- package/dist/app/menu.js.map +1 -0
- package/dist/app/output.d.ts +15 -0
- package/dist/app/output.js +81 -0
- package/dist/app/output.js.map +1 -0
- package/dist/app/session.d.ts +105 -0
- package/dist/app/session.js +2 -0
- package/dist/app/session.js.map +1 -0
- package/dist/app/transfer.d.ts +3 -0
- package/dist/app/transfer.js +144 -0
- package/dist/app/transfer.js.map +1 -0
- package/dist/app/ui.d.ts +60 -0
- package/dist/app/ui.js +15 -0
- package/dist/app/ui.js.map +1 -0
- package/dist/browser-core.d.ts +2 -0
- package/dist/browser-core.js +2 -0
- package/dist/browser-core.js.map +1 -1
- package/dist/index.js +24 -1096
- package/dist/index.js.map +1 -1
- package/dist/node-session.d.ts +51 -0
- package/dist/node-session.js +410 -0
- package/dist/node-session.js.map +1 -0
- package/dist/persist.d.ts +6 -0
- package/dist/persist.js +45 -25
- package/dist/persist.js.map +1 -1
- package/dist/ui-inquirer.d.ts +30 -0
- package/dist/ui-inquirer.js +62 -0
- package/dist/ui-inquirer.js.map +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ HODL Wallet now supports exporting and importing encrypted .HODL files, which se
|
|
|
72
72
|
- **Export HODL File**: Save your wallet data (including private keys and addresses) to an encrypted .HODL file.
|
|
73
73
|
- **Import HODL File**: Restore your wallet from a previously exported .HODL file.
|
|
74
74
|
|
|
75
|
-
These files are encrypted using your wallet password, providing an additional layer of security for storing and transferring your wallet information.
|
|
75
|
+
These files are encrypted using your wallet password, providing an additional layer of security for storing and transferring your wallet information. Files saved in the legacy format by older releases can no longer be imported.
|
|
76
76
|
|
|
77
77
|
The main advantage of exporting a HODL file is that to access the private key, you need BOTH the file AND the password. This two-factor approach significantly enhances security. However, keep in mind that this solution is only compatible with HODL Wallet.
|
|
78
78
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WalletSession } from './session.js';
|
|
2
|
+
import type { Ui } from './ui.js';
|
|
3
|
+
export declare function showAccountDetails(ui: Ui, session: WalletSession): Promise<void>;
|
|
4
|
+
export declare function switchNetwork(ui: Ui, session: WalletSession, loggedIn?: boolean): Promise<void>;
|
|
5
|
+
/** Offers to name a recipient the address book does not know yet. */
|
|
6
|
+
export declare function saveContact(ui: Ui, session: WalletSession, address: string): Promise<void>;
|
|
7
|
+
export declare function loadAccount(ui: Ui, session: WalletSession, loggedIn?: boolean): Promise<void>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { clearSensitiveData } from '../sensitive-data.js';
|
|
2
|
+
import { notice, showAddress, showError } from './output.js';
|
|
3
|
+
async function confirmOverwrite(ui, session) {
|
|
4
|
+
if (!await session.hasAccount())
|
|
5
|
+
return true;
|
|
6
|
+
return ui.confirm({
|
|
7
|
+
message: 'This action will overwrite the existing account; without a backup it cannot be recovered. Are you sure you want to continue?',
|
|
8
|
+
default: false
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async function importFromMnemonic(ui, session) {
|
|
12
|
+
const mnemonic = await ui.password({
|
|
13
|
+
message: 'Enter your mnemonic phrase (12 or 24 words):',
|
|
14
|
+
validate: input => {
|
|
15
|
+
if (input.trim() === '')
|
|
16
|
+
return true;
|
|
17
|
+
return session.network.validateMnemonic(input) || 'Please enter a valid mnemonic phrase or leave empty to cancel.';
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (!mnemonic.trim())
|
|
21
|
+
return;
|
|
22
|
+
await session.importMnemonic(mnemonic);
|
|
23
|
+
await showAddress(ui, session);
|
|
24
|
+
}
|
|
25
|
+
async function importPrivateKey(ui, session) {
|
|
26
|
+
const privateKey = await ui.password({
|
|
27
|
+
message: 'Private-key (leave empty to cancel):',
|
|
28
|
+
validate: input => {
|
|
29
|
+
if (input.trim() === '')
|
|
30
|
+
return true;
|
|
31
|
+
return session.network.validatePrivateKey(input) ||
|
|
32
|
+
'Please enter a valid private-key for the selected network or leave empty to cancel.';
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (!privateKey.trim())
|
|
36
|
+
return;
|
|
37
|
+
try {
|
|
38
|
+
await session.importPrivateKey(privateKey);
|
|
39
|
+
await showAddress(ui, session);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
showError(ui, 'Invalid private-key.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function showAccountDetails(ui, session) {
|
|
46
|
+
const account = await session.accountDetails();
|
|
47
|
+
if (!account) {
|
|
48
|
+
showError(ui, 'Account not initialized.');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const rows = [['Address', account.address], ['Private-key', account.privateKey]];
|
|
52
|
+
if (account.mnemonic) {
|
|
53
|
+
rows.push(['Mnemonic Phrase', account.mnemonic]);
|
|
54
|
+
rows.push(['WARNING', 'Please keep your private-key and mnemonic phrase secure. Never share it.']);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
rows.push(['WARNING', 'Please keep your private-key secure. Never share it.']);
|
|
58
|
+
}
|
|
59
|
+
ui.table({ head: [{ colSpan: 2, content: 'Account Details' }], tone: 'green', wordWrap: true, secret: true, rows });
|
|
60
|
+
clearSensitiveData(account);
|
|
61
|
+
}
|
|
62
|
+
async function createNewAccount(ui, session) {
|
|
63
|
+
let created = false;
|
|
64
|
+
if (await session.hasMnemonic()) {
|
|
65
|
+
const useExisting = await ui.confirm({ message: 'Use existing mnemonic to create account?', default: true });
|
|
66
|
+
if (useExisting) {
|
|
67
|
+
await session.createAccount('existing-mnemonic');
|
|
68
|
+
created = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!created) {
|
|
72
|
+
const withMnemonic = await ui.confirm({ message: 'Create account with mnemonic?', default: true });
|
|
73
|
+
if (withMnemonic) {
|
|
74
|
+
const words = await ui.select({
|
|
75
|
+
message: 'Choose mnemonic phrase length:',
|
|
76
|
+
choices: [
|
|
77
|
+
{ name: '12 words (standard)', value: 12 },
|
|
78
|
+
{ name: '24 words (more secure)', value: 24 }
|
|
79
|
+
],
|
|
80
|
+
default: 12
|
|
81
|
+
});
|
|
82
|
+
await session.createAccount(words);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
await session.createAccount('random');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const contacts = await session.contacts?.list() ?? [];
|
|
89
|
+
if (session.contacts && contacts.length > 0) {
|
|
90
|
+
const clear = await ui.confirm({
|
|
91
|
+
message: `Do you want to delete all ${contacts.length} addresses from the previous account?`,
|
|
92
|
+
default: false
|
|
93
|
+
});
|
|
94
|
+
if (clear) {
|
|
95
|
+
await session.contacts.clear();
|
|
96
|
+
notice(ui, 'green', 'Address Book', 'All addresses deleted successfully.');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let message = 'Do you want to display sensitive information (private key';
|
|
100
|
+
if (await session.hasMnemonic())
|
|
101
|
+
message += ' and mnemonic';
|
|
102
|
+
message += ')?';
|
|
103
|
+
if (await ui.confirm({ message, default: false })) {
|
|
104
|
+
await showAccountDetails(ui, session);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
await showAddress(ui, session);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function switchNetwork(ui, session, loggedIn = true) {
|
|
111
|
+
const plugin = await ui.select({
|
|
112
|
+
message: 'Select the network:',
|
|
113
|
+
choices: session.networks().map(network => ({ name: network.name, value: network }))
|
|
114
|
+
});
|
|
115
|
+
await session.selectNetwork(plugin);
|
|
116
|
+
if (await session.hasAccount()) {
|
|
117
|
+
await showAddress(ui, session);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
ui.print(`\nNo account found for ${session.plugin.name}. Please create or import an account.`);
|
|
121
|
+
await loadAccount(ui, session, loggedIn);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function deleteFromAddressBook(ui, session) {
|
|
125
|
+
const contacts = await session.contacts.list();
|
|
126
|
+
if (contacts.length === 0) {
|
|
127
|
+
notice(ui, 'yellow', 'Address Book', 'No addresses in the address book.');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const address = await ui.select({
|
|
131
|
+
message: 'Select an address to delete:',
|
|
132
|
+
choices: [
|
|
133
|
+
...contacts.map(contact => ({ name: `${contact.address} (${contact.name})`, value: contact.address })),
|
|
134
|
+
{ name: 'Go Back', value: '' }
|
|
135
|
+
]
|
|
136
|
+
});
|
|
137
|
+
if (!address)
|
|
138
|
+
return;
|
|
139
|
+
if (await ui.confirm({ message: 'Are you sure you want to delete this address?', default: false })) {
|
|
140
|
+
await session.contacts.delete(address);
|
|
141
|
+
notice(ui, 'green', 'Address Book', 'Address deleted successfully.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Offers to name a recipient the address book does not know yet. */
|
|
145
|
+
export async function saveContact(ui, session, address) {
|
|
146
|
+
if (!session.contacts || await session.contacts.get(address))
|
|
147
|
+
return;
|
|
148
|
+
const name = await ui.input({ message: 'Name for the address book (leave empty to skip):' });
|
|
149
|
+
if (name.trim() === '')
|
|
150
|
+
return;
|
|
151
|
+
await session.contacts.set(address, name);
|
|
152
|
+
ui.table({
|
|
153
|
+
head: [{ colSpan: 2, content: 'Recipient saved to the address book.' }],
|
|
154
|
+
tone: 'green',
|
|
155
|
+
rows: [[name, address]]
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async function runImport(ui, session, action) {
|
|
159
|
+
if (action === 'importMnemonic') {
|
|
160
|
+
await importFromMnemonic(ui, session);
|
|
161
|
+
}
|
|
162
|
+
else if (action === 'importPrivateKey') {
|
|
163
|
+
await importPrivateKey(ui, session);
|
|
164
|
+
}
|
|
165
|
+
else if (typeof action === 'object' && await action.run(ui)) {
|
|
166
|
+
await showAddress(ui, session);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function importChoices(session) {
|
|
170
|
+
return [
|
|
171
|
+
...session.importActions().map(action => ({ name: action.name, value: action })),
|
|
172
|
+
{ name: 'Import Mnemonic (12 or 24 words)', value: 'importMnemonic' },
|
|
173
|
+
{ name: 'Import Private-key', value: 'importPrivateKey' }
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
export async function loadAccount(ui, session, loggedIn = false) {
|
|
177
|
+
if (!loggedIn && await session.hasAccount())
|
|
178
|
+
return;
|
|
179
|
+
const choices = [];
|
|
180
|
+
if (!loggedIn) {
|
|
181
|
+
choices.push({ name: 'Create New Account', value: 'create' }, ...importChoices(session));
|
|
182
|
+
if (session.capabilities.switchNetworkFirst)
|
|
183
|
+
choices.push({ name: 'Switch Network', value: 'switch' });
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
if (session.capabilities.replaceAccount)
|
|
187
|
+
choices.push({ name: 'Create New Account', value: 'create' });
|
|
188
|
+
choices.push({ name: 'Import Options', value: 'import' });
|
|
189
|
+
choices.push({ name: 'Export Options', value: 'export' }, { name: 'Switch Network', value: 'switch' });
|
|
190
|
+
if (session.contacts)
|
|
191
|
+
choices.push({ name: 'Manage Address Book', value: 'addressBook' });
|
|
192
|
+
choices.push({ name: 'Go Back', value: 'back', back: true });
|
|
193
|
+
}
|
|
194
|
+
const action = await ui.select({ message: 'Select an account option:', choices });
|
|
195
|
+
if (action === 'back')
|
|
196
|
+
return;
|
|
197
|
+
if (action === 'addressBook') {
|
|
198
|
+
const next = await ui.select({
|
|
199
|
+
message: 'Select an address book option:',
|
|
200
|
+
choices: [{ name: 'Delete Address', value: 'delete' }, { name: 'Go Back', value: 'back', back: true }]
|
|
201
|
+
});
|
|
202
|
+
if (next === 'back')
|
|
203
|
+
return loadAccount(ui, session, loggedIn);
|
|
204
|
+
await deleteFromAddressBook(ui, session);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (action === 'import') {
|
|
208
|
+
const next = await ui.select({
|
|
209
|
+
message: 'Select an import option:',
|
|
210
|
+
choices: [...importChoices(session), { name: 'Go Back', value: 'back', back: true }]
|
|
211
|
+
});
|
|
212
|
+
if (next === 'back')
|
|
213
|
+
return loadAccount(ui, session, loggedIn);
|
|
214
|
+
if (!await confirmOverwrite(ui, session))
|
|
215
|
+
return;
|
|
216
|
+
await runImport(ui, session, next);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (action === 'export') {
|
|
220
|
+
const next = await ui.select({
|
|
221
|
+
message: 'Select an export option:',
|
|
222
|
+
choices: [
|
|
223
|
+
...session.exportActions().map(item => ({ name: item.name, value: item })),
|
|
224
|
+
{ name: 'Export Private-key', value: 'privateKey' },
|
|
225
|
+
{ name: 'Go Back', value: 'back', back: true }
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
if (next === 'back')
|
|
229
|
+
return loadAccount(ui, session, loggedIn);
|
|
230
|
+
if (next === 'privateKey')
|
|
231
|
+
await showAccountDetails(ui, session);
|
|
232
|
+
else if (typeof next === 'object')
|
|
233
|
+
await next.run(ui);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (action === 'create') {
|
|
237
|
+
if (!await confirmOverwrite(ui, session))
|
|
238
|
+
return;
|
|
239
|
+
await createNewAccount(ui, session);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (action === 'switch') {
|
|
243
|
+
await switchNetwork(ui, session, loggedIn);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
await runImport(ui, session, action);
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../../app/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQ7D,KAAK,UAAU,gBAAgB,CAAC,EAAM,EAAE,OAAsB;IAC1D,IAAI,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,EAAE,CAAC,OAAO,CAAC;QACd,OAAO,EAAE,8HAA8H;QACvI,OAAO,EAAE,KAAK;KACjB,CAAC,CAAC;AACP,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,EAAM,EAAE,OAAsB;IAC5D,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC;QAC/B,OAAO,EAAE,8CAA8C;QACvD,QAAQ,EAAE,KAAK,CAAC,EAAE;YACd,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAC;YACrC,OAAO,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,gEAAgE,CAAC;QACvH,CAAC;KACJ,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO;IAC7B,MAAM,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,EAAM,EAAE,OAAsB;IAC1D,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC;QACjC,OAAO,EAAE,sCAAsC;QAC/C,QAAQ,EAAE,KAAK,CAAC,EAAE;YACd,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAC;YACrC,OAAO,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC;gBAC5C,qFAAqF,CAAC;QAC9F,CAAC;KACJ,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;QAAE,OAAO;IAC/B,IAAI,CAAC;QACD,MAAM,OAAO,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC3C,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACL,SAAS,CAAC,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAC1C,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAM,EAAE,OAAsB;IACnE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,SAAS,CAAC,EAAE,EAAE,0BAA0B,CAAC,CAAC;QAC1C,OAAO;IACX,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IACjF,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,CAAC,iBAAiB,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,0EAA0E,CAAC,CAAC,CAAC;IACvG,CAAC;SAAM,CAAC;QACJ,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,sDAAsD,CAAC,CAAC,CAAC;IACnF,CAAC;IACD,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACpH,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,EAAM,EAAE,OAAsB;IAC1D,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,MAAM,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,0CAA0C,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7G,IAAI,WAAW,EAAE,CAAC;YACd,MAAM,OAAO,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC;YACjD,OAAO,GAAG,IAAI,CAAC;QACnB,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,+BAA+B,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACnG,IAAI,YAAY,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,CAAU;gBACnC,OAAO,EAAE,gCAAgC;gBACzC,OAAO,EAAE;oBACL,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,EAAE,EAAE;oBAC1C,EAAE,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,EAAE,EAAE;iBAChD;gBACD,OAAO,EAAE,EAAE;aACd,CAAC,CAAC;YACH,MAAM,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACJ,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC;YAC3B,OAAO,EAAE,6BAA6B,QAAQ,CAAC,MAAM,uCAAuC;YAC5F,OAAO,EAAE,KAAK;SACjB,CAAC,CAAC;QACH,IAAI,KAAK,EAAE,CAAC;YACR,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,qCAAqC,CAAC,CAAC;QAC/E,CAAC;IACL,CAAC;IAED,IAAI,OAAO,GAAG,2DAA2D,CAAC;IAC1E,IAAI,MAAM,OAAO,CAAC,WAAW,EAAE;QAAE,OAAO,IAAI,eAAe,CAAC;IAC5D,OAAO,IAAI,IAAI,CAAC;IAEhB,IAAI,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAChD,MAAM,kBAAkB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACJ,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAM,EAAE,OAAsB,EAAE,QAAQ,GAAG,IAAI;IAC/E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC;QAC3B,OAAO,EAAE,qBAAqB;QAC9B,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;KACvF,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAEpC,IAAI,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QAC7B,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;SAAM,CAAC;QACJ,EAAE,CAAC,KAAK,CAAC,0BAA0B,OAAO,CAAC,MAAM,CAAC,IAAI,uCAAuC,CAAC,CAAC;QAC/F,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;AACL,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,EAAM,EAAE,OAAsB;IAC/D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAS,CAAC,IAAI,EAAE,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,mCAAmC,CAAC,CAAC;QAC1E,OAAO;IACX,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,CAAS;QACpC,OAAO,EAAE,8BAA8B;QACvC,OAAO,EAAE;YACL,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YACtG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;SACjC;KACJ,CAAC,CAAC;IACH,IAAI,CAAC,OAAO;QAAE,OAAO;IAErB,IAAI,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,+CAA+C,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACjG,MAAM,OAAO,CAAC,QAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,+BAA+B,CAAC,CAAC;IACzE,CAAC;AACL,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAM,EAAE,OAAsB,EAAE,OAAe;IAC7E,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IACrE,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,kDAAkD,EAAE,CAAC,CAAC;IAC7F,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO;IAC/B,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1C,EAAE,CAAC,KAAK,CAAC;QACL,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC;QACvE,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;KAC1B,CAAC,CAAC;AACP,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,EAAM,EAAE,OAAsB,EAAE,MAAqB;IAC1E,IAAI,MAAM,KAAK,gBAAgB,EAAE,CAAC;QAC9B,MAAM,kBAAkB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;SAAM,IAAI,MAAM,KAAK,kBAAkB,EAAE,CAAC;QACvC,MAAM,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;SAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QAC5D,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,OAAsB;IACzC,OAAO;QACH,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAuB,EAAE,CAAC,CAAC;QACjG,EAAE,IAAI,EAAE,kCAAkC,EAAE,KAAK,EAAE,gBAAgB,EAAE;QACrE,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,kBAAkB,EAAE;KAC5D,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAM,EAAE,OAAsB,EAAE,QAAQ,GAAG,KAAK;IAC9E,IAAI,CAAC,QAAQ,IAAI,MAAM,OAAO,CAAC,UAAU,EAAE;QAAE,OAAO;IAEpD,MAAM,OAAO,GAAiC,EAAE,CAAC;IACjD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACzF,IAAI,OAAO,CAAC,YAAY,CAAC,kBAAkB;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3G,CAAC;SAAM,CAAC;QACJ,IAAI,OAAO,CAAC,YAAY,CAAC,cAAc;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvG,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvG,IAAI,OAAO,CAAC,QAAQ;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,2BAA2B,EAAE,OAAO,EAAE,CAAC,CAAC;IAElF,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO;IAE9B,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAAoB;YAC5C,OAAO,EAAE,gCAAgC;YACzC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SACzG,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,qBAAqB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACzC,OAAO;IACX,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAAgB;YACxC,OAAO,EAAE,0BAA0B;YACnC,OAAO,EAAE,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SACvF,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC;YAAE,OAAO;QACjD,MAAM,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACnC,OAAO;IACX,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAA+B;YACvD,OAAO,EAAE,0BAA0B;YACnC,OAAO,EAAE;gBACL,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAqB,EAAE,CAAC,CAAC;gBAC3F,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,EAAE;gBACnD,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;aACjD;SACJ,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC/D,IAAI,IAAI,KAAK,YAAY;YAAE,MAAM,kBAAkB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;aAC5D,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtD,OAAO;IACX,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC;YAAE,OAAO;QACjD,MAAM,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpC,OAAO;IACX,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3C,OAAO;IACX,CAAC;IAED,MAAM,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function errorMessage(error) {
|
|
2
|
+
return error instanceof Error ? error.message : String(error);
|
|
3
|
+
}
|
|
4
|
+
export function formatAmount(num) {
|
|
5
|
+
num = parseFloat(num.toString());
|
|
6
|
+
// Integers get .00
|
|
7
|
+
if (num === Math.floor(num)) {
|
|
8
|
+
return num.toString() + '.00';
|
|
9
|
+
}
|
|
10
|
+
// Decimals: at most 3 places, trailing zeros removed, at least 2 kept
|
|
11
|
+
let formatted = num.toFixed(3);
|
|
12
|
+
while (formatted.endsWith('0') && formatted.split('.')[1].length > 2) {
|
|
13
|
+
formatted = formatted.slice(0, -1);
|
|
14
|
+
}
|
|
15
|
+
return formatted;
|
|
16
|
+
}
|
|
17
|
+
export function formatDate(date) {
|
|
18
|
+
return new Date(date).toLocaleString('en-GB', {
|
|
19
|
+
year: 'numeric',
|
|
20
|
+
month: '2-digit',
|
|
21
|
+
day: '2-digit',
|
|
22
|
+
hour: '2-digit',
|
|
23
|
+
minute: '2-digit',
|
|
24
|
+
hour12: false
|
|
25
|
+
}).replace(/(\d{2})\/(\d{2})\/(\d{4})/, '$3-$2-$1').replace(",", "");
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../app/format.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAoB;IAC7C,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEjC,mBAAmB;IACnB,IAAI,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC;IAClC,CAAC;IAED,sEAAsE;IACtE,IAAI,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAE/B,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnE,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAA4B;IACnD,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE;QAC1C,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,KAAK;KAChB,CAAC,CAAC,OAAO,CAAC,2BAA2B,EAAE,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { PromptAborted, isPromptAborted } from './ui.js';
|
|
2
|
+
export type { Cell, Choice, Spinner, TableSpec, Tone, Ui, Validation } from './ui.js';
|
|
3
|
+
export type { AccountDetails, Contact, ContactsPort, HostAction, NewAccount, PendingTransfer, SentTransfer, SessionCapabilities, TransferDraft, TransferPort, WalletSession } from './session.js';
|
|
4
|
+
export { WELCOME_ART, farewell, showAddress, showError, welcome } from './output.js';
|
|
5
|
+
export { errorMessage, formatAmount, formatDate } from './format.js';
|
|
6
|
+
export { AccountNotInitialized, initialize, mainMenu, runWallet, showBalance, showTransactions } from './menu.js';
|
|
7
|
+
export { loadAccount, showAccountDetails, switchNetwork } from './account.js';
|
|
8
|
+
export { transferFunds } from './transfer.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { PromptAborted, isPromptAborted } from './ui.js';
|
|
2
|
+
export { WELCOME_ART, farewell, showAddress, showError, welcome } from './output.js';
|
|
3
|
+
export { errorMessage, formatAmount, formatDate } from './format.js';
|
|
4
|
+
export { AccountNotInitialized, initialize, mainMenu, runWallet, showBalance, showTransactions } from './menu.js';
|
|
5
|
+
export { loadAccount, showAccountDetails, switchNetwork } from './account.js';
|
|
6
|
+
export { transferFunds } from './transfer.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../app/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAMzD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WalletSession } from './session.js';
|
|
2
|
+
import type { Ui } from './ui.js';
|
|
3
|
+
/** No account exists after the account menu closed. Hosts that can show the menu again may do so quietly. */
|
|
4
|
+
export declare class AccountNotInitialized extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare function showBalance(ui: Ui, session: WalletSession): Promise<void>;
|
|
8
|
+
export declare function showTransactions(ui: Ui, session: WalletSession): Promise<void>;
|
|
9
|
+
export declare function mainMenu(ui: Ui, session: WalletSession): Promise<void>;
|
|
10
|
+
export declare function initialize(ui: Ui, session: WalletSession): Promise<void>;
|
|
11
|
+
/** The signed-in experience: pick or create an account, show its address, then the main menu. */
|
|
12
|
+
export declare function runWallet(ui: Ui, session: WalletSession): Promise<void>;
|
package/dist/app/menu.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { loadAccount } from './account.js';
|
|
2
|
+
import { errorMessage, formatAmount } from './format.js';
|
|
3
|
+
import { sentTransfersTable, showAddress, showError } from './output.js';
|
|
4
|
+
import { transferFunds } from './transfer.js';
|
|
5
|
+
/** No account exists after the account menu closed. Hosts that can show the menu again may do so quietly. */
|
|
6
|
+
export class AccountNotInitialized extends Error {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('Failed to initialize account.');
|
|
9
|
+
this.name = 'AccountNotInitialized';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export async function showBalance(ui, session) {
|
|
13
|
+
if (!await session.hasAccount()) {
|
|
14
|
+
showError(ui, 'Account not initialized.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const balances = await session.tokenBalances();
|
|
18
|
+
ui.table({
|
|
19
|
+
head: ['Token', 'Balance'],
|
|
20
|
+
tone: 'blue',
|
|
21
|
+
colWidths: [21, 22],
|
|
22
|
+
rows: balances.map(([token, balance]) => [token,
|
|
23
|
+
token === 'USDT' ? Number(balance).toFixed(2) : formatAmount(balance)])
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export async function showTransactions(ui, session) {
|
|
27
|
+
const transfers = await session.sentTransfers();
|
|
28
|
+
ui.table(sentTransfersTable(session, transfers, { status: true, tone: 'blue', empty: 'No transaction history available.' }));
|
|
29
|
+
}
|
|
30
|
+
export async function mainMenu(ui, session) {
|
|
31
|
+
for (;;) {
|
|
32
|
+
const action = await ui.select({
|
|
33
|
+
message: 'What would you like to do?',
|
|
34
|
+
choices: [
|
|
35
|
+
{ name: 'Transfer Funds', value: 'transferFunds' },
|
|
36
|
+
{ name: 'Show Balance', value: 'balance' },
|
|
37
|
+
{ name: 'Show Sent Transfers', value: 'showTransactions' },
|
|
38
|
+
{ name: 'Account Settings', value: 'account' },
|
|
39
|
+
...session.menuActions().map(item => ({ name: item.name, value: item })),
|
|
40
|
+
{ name: 'Exit', value: 'exit' }
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
if (action === 'exit')
|
|
44
|
+
return;
|
|
45
|
+
if (action === 'transferFunds')
|
|
46
|
+
await transferFunds(ui, session);
|
|
47
|
+
else if (action === 'balance')
|
|
48
|
+
await showBalance(ui, session);
|
|
49
|
+
else if (action === 'showTransactions')
|
|
50
|
+
await showTransactions(ui, session);
|
|
51
|
+
else if (action === 'account')
|
|
52
|
+
await loadAccount(ui, session, true);
|
|
53
|
+
else
|
|
54
|
+
await action.run(ui);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function initialize(ui, session) {
|
|
58
|
+
try {
|
|
59
|
+
await session.start();
|
|
60
|
+
if (await session.hasAccount()) {
|
|
61
|
+
await showAddress(ui, session);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await loadAccount(ui, session);
|
|
65
|
+
if (!await session.hasAccount()) {
|
|
66
|
+
throw new AccountNotInitialized();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw new Error(`Initialization failed: ${errorMessage(error)}`, { cause: error });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** The signed-in experience: pick or create an account, show its address, then the main menu. */
|
|
74
|
+
export async function runWallet(ui, session) {
|
|
75
|
+
await initialize(ui, session);
|
|
76
|
+
await mainMenu(ui, session);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../app/menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,6GAA6G;AAC7G,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC5C;QACI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACxC,CAAC;CACJ;AAID,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAM,EAAE,OAAsB;IAC5D,IAAI,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QAC9B,SAAS,CAAC,EAAE,EAAE,0BAA0B,CAAC,CAAC;QAC1C,OAAO;IACX,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;IAC/C,EAAE,CAAC,KAAK,CAAC;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;QAC1B,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QACnB,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK;YAC3C,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;KAC9E,CAAC,CAAC;AACP,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAM,EAAE,OAAsB;IACjE,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;IAChD,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC,CAAC,CAAC;AACjI,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAM,EAAE,OAAsB;IACzD,SAAS,CAAC;QACN,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAa;YACvC,OAAO,EAAE,4BAA4B;YACrC,OAAO,EAAE;gBACL,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE;gBAClD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE;gBAC1C,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,kBAAkB,EAAE;gBAC1D,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,SAAS,EAAE;gBAC9C,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAkB,EAAE,CAAC,CAAC;gBACtF,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;aAClC;SACJ,CAAC,CAAC;QAEH,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO;QAC9B,IAAI,MAAM,KAAK,eAAe;YAAE,MAAM,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;aAC5D,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;aACzD,IAAI,MAAM,KAAK,kBAAkB;YAAE,MAAM,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;aACvE,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;;YAC/D,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAM,EAAE,OAAsB;IAC3D,IAAI,CAAC;QACD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YAC7B,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YAC/B,OAAO;QACX,CAAC;QACD,MAAM,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YAC9B,MAAM,IAAI,qBAAqB,EAAE,CAAC;QACtC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0BAA0B,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACvF,CAAC;AACL,CAAC;AAED,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAM,EAAE,OAAsB;IAC1D,MAAM,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SentTransfer, WalletSession } from './session.js';
|
|
2
|
+
import type { TableSpec, Tone, Ui } from './ui.js';
|
|
3
|
+
export declare const WELCOME_ART = " \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2588 \u2588\u2003\u2588\u2580\u2588\u2003\u2588\u2580\u2584\u2003\u2588 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2588\u2580\u2588\u2003\u2588\u2584\u2588\u2003\u2588\u2584\u2580\u2003\u2588\u2584\u2584 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 WALLET \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591";
|
|
4
|
+
export declare function welcome(ui: Ui): void;
|
|
5
|
+
export declare function farewell(ui: Ui): void;
|
|
6
|
+
export declare function notice(ui: Ui, tone: Tone, head: string, text: string): void;
|
|
7
|
+
export declare function showError(ui: Ui, message: string, data?: unknown): void;
|
|
8
|
+
export declare function showTransactionError(ui: Ui, error: unknown): void;
|
|
9
|
+
export declare function showAddress(ui: Ui, session: WalletSession): Promise<void>;
|
|
10
|
+
/** Sent transfers, one row each followed by its explorer link. Columns follow what the host records. */
|
|
11
|
+
export declare function sentTransfersTable(session: Pick<WalletSession, 'capabilities' | 'contacts'>, transfers: SentTransfer[], options: {
|
|
12
|
+
status: boolean;
|
|
13
|
+
tone: Tone;
|
|
14
|
+
empty?: string;
|
|
15
|
+
}): TableSpec;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { errorMessage, formatAmount, formatDate } from './format.js';
|
|
2
|
+
export const WELCOME_ART = ` ░░░░░░░░░░░░░░ █ █\u2003█▀█\u2003█▀▄\u2003█ ░░░░░░░░░░░░░░
|
|
3
|
+
░░░░░░░░░░░░░░ █▀█\u2003█▄█\u2003█▄▀\u2003█▄▄ ░░░░░░░░░░░░░░
|
|
4
|
+
░░░░░░░░░░░░░░ ──────── WALLET ░░░░░░░░░░░░░░`;
|
|
5
|
+
const EXIT_PHRASES = [
|
|
6
|
+
"Buy the rumor, sell the news",
|
|
7
|
+
"The trend is your friend",
|
|
8
|
+
"Don't fight the tape",
|
|
9
|
+
"Cut your losses and let your profits run",
|
|
10
|
+
"Be fearful when others are greedy, and greedy when others are fearful",
|
|
11
|
+
"The market can remain irrational longer than you can remain solvent",
|
|
12
|
+
"Bulls make money, bears make money, pigs get slaughtered",
|
|
13
|
+
"No one is bigger than the market",
|
|
14
|
+
"Don't catch a falling knife",
|
|
15
|
+
"Past performance is not indicative of future results",
|
|
16
|
+
"The stock market is a device for transferring money from the impatient to the patient",
|
|
17
|
+
"Time in the market beats timing the market",
|
|
18
|
+
"Buy low, sell high",
|
|
19
|
+
"Diversification is the only free lunch in investing",
|
|
20
|
+
"The four most dangerous words in investing are: 'This time it's different'",
|
|
21
|
+
"Markets can remain irrational a lot longer than you and I can remain solvent",
|
|
22
|
+
"Risk comes from not knowing what you're doing",
|
|
23
|
+
"In the short run, the market is a voting machine. In the long run, it's a weighing machine",
|
|
24
|
+
"Invest in yourself. Your career is the engine of your wealth",
|
|
25
|
+
"Who has the gold makes the rules",
|
|
26
|
+
"The best time to invest was yesterday. The second best time is now",
|
|
27
|
+
"Don't put all your eggs in one basket",
|
|
28
|
+
"Knowledge is power in the world of investing",
|
|
29
|
+
"Patience is a virtue in the stock market",
|
|
30
|
+
"The market is never wrong, but opinions often are"
|
|
31
|
+
];
|
|
32
|
+
export function welcome(ui) {
|
|
33
|
+
ui.banner(WELCOME_ART);
|
|
34
|
+
}
|
|
35
|
+
export function farewell(ui) {
|
|
36
|
+
ui.table({
|
|
37
|
+
head: ['✨ Good bye!'],
|
|
38
|
+
tone: 'yellow',
|
|
39
|
+
wordWrap: true,
|
|
40
|
+
rows: [[EXIT_PHRASES[Math.floor(Math.random() * EXIT_PHRASES.length)]]]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export function notice(ui, tone, head, text) {
|
|
44
|
+
ui.table({ head: [head], tone, rows: [[text]] });
|
|
45
|
+
}
|
|
46
|
+
export function showError(ui, message, data) {
|
|
47
|
+
ui.table({ head: [message], tone: 'red', wordWrap: true, ...(data ? { rows: [[String(data)]] } : {}) });
|
|
48
|
+
}
|
|
49
|
+
export function showTransactionError(ui, error) {
|
|
50
|
+
const reason = typeof error === 'object' && error !== null && 'reason' in error
|
|
51
|
+
? String(error.reason)
|
|
52
|
+
: null;
|
|
53
|
+
const message = error instanceof Error ? error.message : errorMessage(error);
|
|
54
|
+
const data = reason ? reason.replace(/(\w+):/g, "\n$1:").trim() : null;
|
|
55
|
+
showError(ui, message, data);
|
|
56
|
+
}
|
|
57
|
+
export async function showAddress(ui, session) {
|
|
58
|
+
ui.table({ head: [`${session.plugin.name} Address`], tone: 'green', rows: [[await session.address()]] });
|
|
59
|
+
}
|
|
60
|
+
/** Sent transfers, one row each followed by its explorer link. Columns follow what the host records. */
|
|
61
|
+
export function sentTransfersTable(session, transfers, options) {
|
|
62
|
+
const withContact = Boolean(session.contacts);
|
|
63
|
+
const withBalance = session.capabilities.balanceHistory;
|
|
64
|
+
const head = ['Date', 'Recipient', ...(withContact ? ['Contact'] : []), 'Token', 'Amount',
|
|
65
|
+
...(withBalance ? ['Balance'] : []), ...(options.status ? ['Status'] : [])];
|
|
66
|
+
const rows = [];
|
|
67
|
+
if (options.empty && transfers.length === 0)
|
|
68
|
+
rows.push([{ colSpan: head.length, content: options.empty }]);
|
|
69
|
+
for (const tx of transfers) {
|
|
70
|
+
rows.push([
|
|
71
|
+
formatDate(tx.timestamp), tx.recipient,
|
|
72
|
+
...(withContact ? [tx.contact ?? '-'] : []),
|
|
73
|
+
tx.token, formatAmount(tx.amount),
|
|
74
|
+
...(withBalance ? [tx.balance !== undefined ? formatAmount(tx.balance) : '-'] : []),
|
|
75
|
+
...(options.status ? [tx.status || '-'] : [])
|
|
76
|
+
]);
|
|
77
|
+
rows.push([{ colSpan: head.length, content: tx.url }]);
|
|
78
|
+
}
|
|
79
|
+
return { head, rows, tone: options.tone };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../app/output.ts"],"names":[],"mappings":"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"}
|