Compare commits
4 Commits
a0d9775015
...
use-flatma
| Author | SHA1 | Date | |
|---|---|---|---|
|
941414b3ee
|
|||
|
e9bc6186b9
|
|||
|
b2ccff5b19
|
|||
| b4d82b8b1f |
40
package-lock.json
generated
40
package-lock.json
generated
@@ -15,7 +15,7 @@
|
||||
"@xo-cash/crypto": "^0.0.1",
|
||||
"@xo-cash/engine": "file:../engine",
|
||||
"@xo-cash/state": "file:../state",
|
||||
"@xo-cash/templates": "^0.0.1",
|
||||
"@xo-cash/templates": "file:../templates",
|
||||
"@xo-cash/types": "^0.0.1",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"clipboardy": "^5.1.0",
|
||||
@@ -51,7 +51,7 @@
|
||||
"@electrum-cash/network": "^4.2.2",
|
||||
"@electrum-cash/protocol": "^2.3.1",
|
||||
"@electrum-cash/servers": "^3.1.0",
|
||||
"@xo-cash/crypto": "0.0.1",
|
||||
"@xo-cash/crypto": "file:../crypto",
|
||||
"@xo-cash/primitives": "0.0.1",
|
||||
"@xo-cash/state": "0.0.2",
|
||||
"@xo-cash/templates": "0.0.1",
|
||||
@@ -113,6 +113,33 @@
|
||||
"vitest": "^4.0.17"
|
||||
}
|
||||
},
|
||||
"../templates": {
|
||||
"name": "@xo-cash/templates",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xo-cash/types": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chalp/eslint-airbnb": "^1.3.0",
|
||||
"@generalprotocols/cspell-dictionary": "^1.0.1",
|
||||
"@stylistic/eslint-plugin": "^5.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.53.1",
|
||||
"@typescript-eslint/parser": "^8.53.1",
|
||||
"@vitest/coverage-v8": "^4.0.17",
|
||||
"@viz-kit/esbuild-analyzer": "^1.0.0",
|
||||
"@xo-cash/eslint-config": "1.0.1",
|
||||
"cspell": "^9.6.0",
|
||||
"eslint": "^9.39.2",
|
||||
"prettier": "^3.6.2",
|
||||
"tsdown": "^0.20.0-beta.4",
|
||||
"typedoc": "^0.28.16",
|
||||
"typedoc-plugin-coverage": "^4.0.2",
|
||||
"typescript": "^5.3.2",
|
||||
"typescript-eslint": "^8.53.1",
|
||||
"vitest": "^4.0.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@alcalzone/ansi-tokenize": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.4.tgz",
|
||||
@@ -929,13 +956,8 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@xo-cash/templates": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@xo-cash/templates/-/templates-0.0.1.tgz",
|
||||
"integrity": "sha512-v5f0YeH9Bw6lNThdE0fI878T4L2jbM8RI1quxdKxnvqHn9hu2jzebqvveEB2TfJWG3sP1GpE1go0Yn87R4sXfw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xo-cash/types": "0.0.1"
|
||||
}
|
||||
"resolved": "../templates",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@xo-cash/types": {
|
||||
"version": "0.0.1",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@xo-cash/crypto": "^0.0.1",
|
||||
"@xo-cash/engine": "file:../engine",
|
||||
"@xo-cash/state": "file:../state",
|
||||
"@xo-cash/templates": "^0.0.1",
|
||||
"@xo-cash/templates": "file:../templates",
|
||||
"@xo-cash/types": "^0.0.1",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"clipboardy": "^5.1.0",
|
||||
|
||||
@@ -83,20 +83,33 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
// Import the default P2PKH template
|
||||
const { templateIdentifier } = await engine.importTemplate(p2pkhTemplate);
|
||||
|
||||
engine
|
||||
.subscribeToLockingBytecodesForTemplate(templateIdentifier)
|
||||
.catch((err) =>
|
||||
console.error(
|
||||
`Error subscribing to locking bytecodes for template ${templateIdentifier}: ${err}`,
|
||||
),
|
||||
);
|
||||
engine
|
||||
.updateUnspentOutputsForTemplate(templateIdentifier)
|
||||
.catch((err) =>
|
||||
console.error(
|
||||
`Error updating unspent outputs for template ${templateIdentifier}: ${err}`,
|
||||
),
|
||||
);
|
||||
// engine
|
||||
// .subscribeToLockingBytecodesForTemplate(templateIdentifier)
|
||||
// .catch((err) =>
|
||||
// console.error(
|
||||
// `Error subscribing to locking bytecodes for template ${templateIdentifier}: ${err}`,
|
||||
// ),
|
||||
// );
|
||||
// engine
|
||||
// .updateUnspentOutputsForTemplate(templateIdentifier)
|
||||
// .catch((err) =>
|
||||
// console.error(
|
||||
// `Error updating unspent outputs for template ${templateIdentifier}: ${err}`,
|
||||
// ),
|
||||
// );
|
||||
|
||||
// Update all the unspents for every template, and subscribe to the locking bytecodes for changes
|
||||
// TODO: Remove the above lines that do the same thing. Minimising changes for BLISS.
|
||||
const updateTemplates = async () => {
|
||||
const templates = await engine.listImportedTemplates();
|
||||
|
||||
templates.forEach(async (template) => {
|
||||
// engine.updateUnspentOutputsForTemplate(generateTemplateIdentifier(template));
|
||||
engine.subscribeToLockingBytecodesForTemplate(generateTemplateIdentifier(template));
|
||||
});
|
||||
};
|
||||
|
||||
updateTemplates();
|
||||
|
||||
// Set default locking parameters for P2PKH
|
||||
// To my knowledge, this doesnt generate any lockscript, so discovery of funds will not work automatically.
|
||||
|
||||
@@ -96,6 +96,12 @@ export class HistoryService {
|
||||
private invitations: Invitation[],
|
||||
) {}
|
||||
|
||||
|
||||
/**
|
||||
* I Might swap this over to invitation based history before the event to make it a bit more evident... Really not happy with the UTXO for demo purposes
|
||||
* But for the actual usage, UTXO is easier to follow - just not good for demo
|
||||
* Long term, this is intended to be in the Engine, so we will just be a consumer of history state.
|
||||
*/
|
||||
async getHistory(): Promise<WalletHistoryItem[]> {
|
||||
const allUtxos = await this.engine.listUnspentOutputsData();
|
||||
const metadataIndex = await this.buildWalletMetadataIndex(allUtxos);
|
||||
|
||||
@@ -510,7 +510,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
const templates = await this.engine.listImportedTemplates();
|
||||
|
||||
// For each template, we need to create a 2d array of all the outputs
|
||||
const outputs = templates.map(template => {
|
||||
const outputs = templates.flatMap(template => {
|
||||
return Object.keys(template.outputs).map(output => {
|
||||
const templateIdentifier = generateTemplateIdentifier(template);
|
||||
|
||||
@@ -522,7 +522,7 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
});
|
||||
|
||||
// then, for each output, we need to get the spendable resources
|
||||
const spendableResources = await Promise.all(outputs.flat().map(output => {
|
||||
const spendableResources = await Promise.all(outputs.map(output => {
|
||||
return this.engine.getSpendableResources(this.data, {
|
||||
templateIdentifier: output.templateIdentifier,
|
||||
outputIdentifier: output.outputIdentifier,
|
||||
|
||||
@@ -19,7 +19,7 @@ import path from 'path';
|
||||
import { createMnemonicFile } from '../../cli/mnemonic.js';
|
||||
import { getMnemonicsDir } from '../../utils/paths.js';
|
||||
import { BCHMnemonicURL } from '../../utils/bch-mnemonic-url.js';
|
||||
import { encodeBip39Mnemonic } from '@bitauth/libauth';
|
||||
import { encodeBip39Mnemonic, generateBip39Mnemonic } from '@bitauth/libauth';
|
||||
|
||||
/**
|
||||
* Status message type.
|
||||
@@ -41,7 +41,7 @@ interface MnemonicFileEntry {
|
||||
* Focus sections the user can tab between.
|
||||
* When saved wallets exist the file list is shown first.
|
||||
*/
|
||||
type FocusSection = 'files' | 'input' | 'saveCheckbox' | 'button';
|
||||
type FocusSection = 'files' | 'input' | 'saveCheckbox' | 'generateRandomSeed' | 'button';
|
||||
|
||||
/**
|
||||
* Reads mnemonic-* files from ~/.config/xo-cli/mnemonics/ (same as xo-cli),
|
||||
@@ -117,8 +117,8 @@ export function SeedInputScreen(): React.ReactElement {
|
||||
* The ordered list of focusable sections (files section only when entries exist).
|
||||
*/
|
||||
const focusSections: FocusSection[] = mnemonicFiles.length > 0
|
||||
? ['files', 'input', 'saveCheckbox', 'button']
|
||||
: ['input', 'saveCheckbox', 'button'];
|
||||
? ['files', 'input', 'generateRandomSeed', 'saveCheckbox', 'button']
|
||||
: ['input', 'generateRandomSeed', 'saveCheckbox', 'button'];
|
||||
|
||||
/**
|
||||
* Shows a status message with the given type.
|
||||
@@ -202,7 +202,7 @@ export function SeedInputScreen(): React.ReactElement {
|
||||
}, [mnemonicFiles, doInitialize]);
|
||||
|
||||
// Keyboard navigation
|
||||
useBlockableInput((_input, key) => {
|
||||
useBlockableInput((input, key) => {
|
||||
if (isSubmitting) return;
|
||||
|
||||
// Tab / Shift-Tab to cycle focus sections
|
||||
@@ -219,7 +219,7 @@ export function SeedInputScreen(): React.ReactElement {
|
||||
|
||||
// Space or Enter toggles "save mnemonic" when that row is focused
|
||||
if (focusedSection === 'saveCheckbox') {
|
||||
if (_input === ' ' || key.return) {
|
||||
if (input === ' ' || key.return) {
|
||||
setSaveMnemonicChecked((v) => !v);
|
||||
return;
|
||||
}
|
||||
@@ -241,6 +241,18 @@ export function SeedInputScreen(): React.ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl-R generates a random seed phrase and fills it in the input
|
||||
if (key.ctrl && input === 'r') {
|
||||
setSeedPhrase(generateBip39Mnemonic());
|
||||
return;
|
||||
}
|
||||
|
||||
// If pressing enter while the generate random seed section is focused, generate a random seed and fill it in the input
|
||||
if (key.return && focusedSection === 'generateRandomSeed') {
|
||||
setSeedPhrase(generateBip39Mnemonic());
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter on button submits manual seed
|
||||
if (key.return && focusedSection === 'button') {
|
||||
handleSubmit();
|
||||
@@ -358,6 +370,19 @@ export function SeedInputScreen(): React.ReactElement {
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Generate random seed phrase and fill in the input */}
|
||||
<Box marginTop={1}>
|
||||
<Box
|
||||
paddingX={1}
|
||||
paddingY={0}
|
||||
backgroundColor={focusedSection === 'generateRandomSeed' ? colors.focus : colors.bgSelected}
|
||||
>
|
||||
<Text color={focusedSection === 'generateRandomSeed' ? colors.bg : colors.text} bold>Generate Random Seed</Text>
|
||||
</Box>
|
||||
|
||||
<Text color={colors.textMuted}> (Ctrl-R)</Text>
|
||||
</Box>
|
||||
|
||||
{/* Save mnemonic checkbox (manual entry only; applies on Continue) */}
|
||||
<Box
|
||||
marginTop={1}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import type { Invitation } from "../services/invitation.js";
|
||||
import type { XOTemplate } from "@xo-cash/types";
|
||||
import type { XOInvitationCommit, XOTemplate } from "@xo-cash/types";
|
||||
|
||||
/**
|
||||
* Color names for invitation states.
|
||||
@@ -249,9 +249,9 @@ export function formatInvitationId(id: string, maxLength: number = 16): string {
|
||||
* @param invitation - The invitation to check
|
||||
* @returns Array of unique entity identifiers
|
||||
*/
|
||||
export function getInvitationParticipants(invitation: Invitation): string[] {
|
||||
export function getInvitationParticipants(commits: Array<XOInvitationCommit>): string[] {
|
||||
const participants = new Set<string>();
|
||||
for (const commit of invitation.data.commits || []) {
|
||||
for (const commit of commits) {
|
||||
if (commit.entityIdentifier) {
|
||||
participants.add(commit.entityIdentifier);
|
||||
}
|
||||
@@ -267,9 +267,14 @@ export function getInvitationParticipants(invitation: Invitation): string[] {
|
||||
* @returns True if the user has made at least one commit
|
||||
*/
|
||||
export function isUserParticipant(
|
||||
invitation: Invitation,
|
||||
invitation: Invitation | Array<XOInvitationCommit>,
|
||||
userEntityId: string | null,
|
||||
): boolean {
|
||||
if (!userEntityId) return false;
|
||||
return getInvitationParticipants(invitation).includes(userEntityId);
|
||||
|
||||
if (Array.isArray(invitation)) {
|
||||
return invitation.some(commit => commit.entityIdentifier === userEntityId);
|
||||
}
|
||||
|
||||
return getInvitationParticipants(invitation.data.commits).includes(userEntityId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user