@1sat/wallet-toolbox 0.0.8 → 0.0.10
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/dist/api/balance/index.d.ts +50 -0
- package/dist/api/balance/index.js +135 -0
- package/dist/api/broadcast/index.d.ts +24 -0
- package/dist/api/broadcast/index.js +73 -0
- package/dist/api/constants.d.ts +21 -0
- package/dist/api/constants.js +29 -0
- package/dist/api/index.d.ts +36 -0
- package/dist/api/index.js +59 -0
- package/dist/api/inscriptions/index.d.ts +25 -0
- package/dist/api/inscriptions/index.js +98 -0
- package/dist/api/locks/index.d.ts +47 -0
- package/dist/api/locks/index.js +291 -0
- package/dist/api/ordinals/index.d.ts +102 -0
- package/dist/api/ordinals/index.js +566 -0
- package/dist/api/payments/index.d.ts +48 -0
- package/dist/api/payments/index.js +185 -0
- package/dist/api/signing/index.d.ts +35 -0
- package/dist/api/signing/index.js +78 -0
- package/dist/api/skills/registry.d.ts +61 -0
- package/dist/api/skills/registry.js +74 -0
- package/dist/api/skills/types.d.ts +71 -0
- package/dist/api/skills/types.js +14 -0
- package/dist/api/tokens/index.d.ts +87 -0
- package/dist/api/tokens/index.js +457 -0
- package/dist/cwi/chrome.d.ts +11 -0
- package/dist/cwi/chrome.js +39 -0
- package/dist/cwi/event.d.ts +11 -0
- package/dist/cwi/event.js +38 -0
- package/dist/cwi/factory.d.ts +14 -0
- package/dist/cwi/factory.js +44 -0
- package/dist/cwi/index.d.ts +11 -0
- package/dist/cwi/index.js +11 -0
- package/dist/cwi/types.d.ts +39 -0
- package/dist/cwi/types.js +39 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +7 -1
- package/dist/indexers/CosignIndexer.js +1 -0
- package/dist/indexers/InscriptionIndexer.js +3 -4
- package/dist/indexers/LockIndexer.js +1 -0
- package/dist/indexers/OrdLockIndexer.js +1 -0
- package/dist/indexers/OriginIndexer.js +1 -1
- package/dist/indexers/index.d.ts +1 -1
- package/dist/indexers/types.d.ts +18 -0
- package/dist/services/OneSatServices.d.ts +19 -10
- package/dist/services/OneSatServices.js +201 -39
- package/dist/services/client/ChaintracksClient.d.ts +55 -13
- package/dist/services/client/ChaintracksClient.js +123 -28
- package/dist/services/client/OrdfsClient.d.ts +2 -2
- package/dist/services/client/OrdfsClient.js +4 -3
- package/dist/services/client/TxoClient.js +9 -0
- package/dist/sync/AddressManager.d.ts +85 -0
- package/dist/sync/AddressManager.js +107 -0
- package/dist/sync/SyncManager.d.ts +207 -0
- package/dist/sync/SyncManager.js +507 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +2 -0
- package/dist/wallet/factory.d.ts +64 -0
- package/dist/wallet/factory.js +129 -0
- package/dist/wallet/index.d.ts +1 -0
- package/dist/wallet/index.js +1 -0
- package/package.json +14 -4
- package/dist/OneSatWallet.d.ts +0 -316
- package/dist/OneSatWallet.js +0 -956
- package/dist/indexers/TransactionParser.d.ts +0 -53
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CWI (Chrome Wallet Interface) - Shared types for BRC-100 WalletInterface implementations
|
|
3
|
+
*/
|
|
4
|
+
export declare enum CWIEventName {
|
|
5
|
+
LIST_OUTPUTS = "cwi_listOutputs",
|
|
6
|
+
LIST_ACTIONS = "cwi_listActions",
|
|
7
|
+
GET_PUBLIC_KEY = "cwi_getPublicKey",
|
|
8
|
+
GET_HEIGHT = "cwi_getHeight",
|
|
9
|
+
GET_HEADER_FOR_HEIGHT = "cwi_getHeaderForHeight",
|
|
10
|
+
GET_NETWORK = "cwi_getNetwork",
|
|
11
|
+
GET_VERSION = "cwi_getVersion",
|
|
12
|
+
IS_AUTHENTICATED = "cwi_isAuthenticated",
|
|
13
|
+
WAIT_FOR_AUTHENTICATION = "cwi_waitForAuthentication",
|
|
14
|
+
CREATE_ACTION = "cwi_createAction",
|
|
15
|
+
SIGN_ACTION = "cwi_signAction",
|
|
16
|
+
ABORT_ACTION = "cwi_abortAction",
|
|
17
|
+
INTERNALIZE_ACTION = "cwi_internalizeAction",
|
|
18
|
+
CREATE_SIGNATURE = "cwi_createSignature",
|
|
19
|
+
VERIFY_SIGNATURE = "cwi_verifySignature",
|
|
20
|
+
ENCRYPT = "cwi_encrypt",
|
|
21
|
+
DECRYPT = "cwi_decrypt",
|
|
22
|
+
CREATE_HMAC = "cwi_createHmac",
|
|
23
|
+
VERIFY_HMAC = "cwi_verifyHmac",
|
|
24
|
+
RELINQUISH_OUTPUT = "cwi_relinquishOutput",
|
|
25
|
+
ACQUIRE_CERTIFICATE = "cwi_acquireCertificate",
|
|
26
|
+
LIST_CERTIFICATES = "cwi_listCertificates",
|
|
27
|
+
PROVE_CERTIFICATE = "cwi_proveCertificate",
|
|
28
|
+
RELINQUISH_CERTIFICATE = "cwi_relinquishCertificate",
|
|
29
|
+
DISCOVER_BY_IDENTITY_KEY = "cwi_discoverByIdentityKey",
|
|
30
|
+
DISCOVER_BY_ATTRIBUTES = "cwi_discoverByAttributes",
|
|
31
|
+
REVEAL_COUNTERPARTY_KEY_LINKAGE = "cwi_revealCounterpartyKeyLinkage",
|
|
32
|
+
REVEAL_SPECIFIC_KEY_LINKAGE = "cwi_revealSpecificKeyLinkage"
|
|
33
|
+
}
|
|
34
|
+
export interface CWIResponseDetail<T = unknown> {
|
|
35
|
+
type: CWIEventName;
|
|
36
|
+
success: boolean;
|
|
37
|
+
data?: T;
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CWI (Chrome Wallet Interface) - Shared types for BRC-100 WalletInterface implementations
|
|
3
|
+
*/
|
|
4
|
+
// BRC-100 Event Names - shared between all CWI implementations
|
|
5
|
+
export var CWIEventName;
|
|
6
|
+
(function (CWIEventName) {
|
|
7
|
+
// Read-only operations
|
|
8
|
+
CWIEventName["LIST_OUTPUTS"] = "cwi_listOutputs";
|
|
9
|
+
CWIEventName["LIST_ACTIONS"] = "cwi_listActions";
|
|
10
|
+
CWIEventName["GET_PUBLIC_KEY"] = "cwi_getPublicKey";
|
|
11
|
+
CWIEventName["GET_HEIGHT"] = "cwi_getHeight";
|
|
12
|
+
CWIEventName["GET_HEADER_FOR_HEIGHT"] = "cwi_getHeaderForHeight";
|
|
13
|
+
CWIEventName["GET_NETWORK"] = "cwi_getNetwork";
|
|
14
|
+
CWIEventName["GET_VERSION"] = "cwi_getVersion";
|
|
15
|
+
CWIEventName["IS_AUTHENTICATED"] = "cwi_isAuthenticated";
|
|
16
|
+
CWIEventName["WAIT_FOR_AUTHENTICATION"] = "cwi_waitForAuthentication";
|
|
17
|
+
// Signing operations (require password)
|
|
18
|
+
CWIEventName["CREATE_ACTION"] = "cwi_createAction";
|
|
19
|
+
CWIEventName["SIGN_ACTION"] = "cwi_signAction";
|
|
20
|
+
CWIEventName["ABORT_ACTION"] = "cwi_abortAction";
|
|
21
|
+
CWIEventName["INTERNALIZE_ACTION"] = "cwi_internalizeAction";
|
|
22
|
+
CWIEventName["CREATE_SIGNATURE"] = "cwi_createSignature";
|
|
23
|
+
CWIEventName["VERIFY_SIGNATURE"] = "cwi_verifySignature";
|
|
24
|
+
CWIEventName["ENCRYPT"] = "cwi_encrypt";
|
|
25
|
+
CWIEventName["DECRYPT"] = "cwi_decrypt";
|
|
26
|
+
CWIEventName["CREATE_HMAC"] = "cwi_createHmac";
|
|
27
|
+
CWIEventName["VERIFY_HMAC"] = "cwi_verifyHmac";
|
|
28
|
+
CWIEventName["RELINQUISH_OUTPUT"] = "cwi_relinquishOutput";
|
|
29
|
+
// Certificate operations
|
|
30
|
+
CWIEventName["ACQUIRE_CERTIFICATE"] = "cwi_acquireCertificate";
|
|
31
|
+
CWIEventName["LIST_CERTIFICATES"] = "cwi_listCertificates";
|
|
32
|
+
CWIEventName["PROVE_CERTIFICATE"] = "cwi_proveCertificate";
|
|
33
|
+
CWIEventName["RELINQUISH_CERTIFICATE"] = "cwi_relinquishCertificate";
|
|
34
|
+
CWIEventName["DISCOVER_BY_IDENTITY_KEY"] = "cwi_discoverByIdentityKey";
|
|
35
|
+
CWIEventName["DISCOVER_BY_ATTRIBUTES"] = "cwi_discoverByAttributes";
|
|
36
|
+
// Key linkage
|
|
37
|
+
CWIEventName["REVEAL_COUNTERPARTY_KEY_LINKAGE"] = "cwi_revealCounterpartyKeyLinkage";
|
|
38
|
+
CWIEventName["REVEAL_SPECIFIC_KEY_LINKAGE"] = "cwi_revealSpecificKeyLinkage";
|
|
39
|
+
})(CWIEventName || (CWIEventName = {}));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from "./api";
|
|
2
2
|
export { OneSatServices, type SyncOutput } from "./services/OneSatServices";
|
|
3
3
|
export type { OrdfsMetadata, OrdfsContentOptions, OrdfsContentResponse, OrdfsResponseHeaders, Capability, } from "./services/types";
|
|
4
4
|
export * from "./services/client";
|
|
5
5
|
export { ReadOnlySigner } from "./signers/ReadOnlySigner";
|
|
6
6
|
export * from "./indexers";
|
|
7
7
|
export * from "./sync";
|
|
8
|
+
export * from "./cwi";
|
|
9
|
+
export * from "./wallet";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,9 @@ import { Buffer } from "buffer";
|
|
|
3
3
|
if (typeof globalThis.Buffer === "undefined") {
|
|
4
4
|
globalThis.Buffer = Buffer;
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
// API Layer - The new unified API for 1sat ecosystem
|
|
7
|
+
export * from "./api";
|
|
8
|
+
// Services
|
|
7
9
|
export { OneSatServices } from "./services/OneSatServices";
|
|
8
10
|
export * from "./services/client";
|
|
9
11
|
export { ReadOnlySigner } from "./signers/ReadOnlySigner";
|
|
@@ -11,3 +13,7 @@ export { ReadOnlySigner } from "./signers/ReadOnlySigner";
|
|
|
11
13
|
export * from "./indexers";
|
|
12
14
|
// Sync Queue
|
|
13
15
|
export * from "./sync";
|
|
16
|
+
// CWI (Chrome Wallet Interface) - BRC-100 implementations
|
|
17
|
+
export * from "./cwi";
|
|
18
|
+
// Wallet factory for creating 1Sat web wallets
|
|
19
|
+
export * from "./wallet";
|
|
@@ -35,11 +35,10 @@ export class InscriptionIndexer extends Indexer {
|
|
|
35
35
|
let owner = parseAddress(script, 0, this.network);
|
|
36
36
|
if (!owner && decoded.scriptSuffix) {
|
|
37
37
|
// Try to find owner in suffix (after OP_ENDIF)
|
|
38
|
-
|
|
39
|
-
owner = parseAddress(suffixScript, 0, this.network);
|
|
38
|
+
owner = parseAddress(decoded.scriptSuffix, 0, this.network);
|
|
40
39
|
// Also check for OP_CODESEPARATOR pattern
|
|
41
|
-
if (!owner &&
|
|
42
|
-
owner = parseAddress(
|
|
40
|
+
if (!owner && decoded.scriptSuffix.chunks[0]?.op === OP.OP_CODESEPARATOR) {
|
|
41
|
+
owner = parseAddress(decoded.scriptSuffix, 1, this.network);
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
// Handle MAP field if present (special case)
|
|
@@ -47,7 +47,7 @@ export class OriginIndexer extends Indexer {
|
|
|
47
47
|
return {
|
|
48
48
|
data: origin,
|
|
49
49
|
tags: [], // Tags will be added in summarize() once origin is determined
|
|
50
|
-
owner: address
|
|
50
|
+
owner: address,
|
|
51
51
|
basket: "1sat",
|
|
52
52
|
};
|
|
53
53
|
}
|
package/dist/indexers/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Indexer, type IndexData, type IndexSummary, type ParseContext, type ParseResult, type Txo, } from "./types";
|
|
1
|
+
export { Indexer, type IndexData, type IndexSummary, type InternalizeProtocol, type ParseContext, type ParseResult, type Txo, } from "./types";
|
|
2
2
|
export type { Bsv21TokenData, Bsv21OutputData, Bsv21TransactionData, } from "./types";
|
|
3
3
|
export { Outpoint } from "./Outpoint";
|
|
4
4
|
export { parseAddress } from "./parseAddress";
|
package/dist/indexers/types.d.ts
CHANGED
|
@@ -54,6 +54,12 @@ export interface IndexSummary {
|
|
|
54
54
|
icon?: string;
|
|
55
55
|
data?: unknown;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Internalization protocol for synced outputs.
|
|
59
|
+
* - "wallet payment": P2PKH-based outputs that can be auto-signed
|
|
60
|
+
* - "basket insertion": Custom script outputs requiring manual unlock scripts
|
|
61
|
+
*/
|
|
62
|
+
export type InternalizeProtocol = "wallet payment" | "basket insertion";
|
|
57
63
|
/**
|
|
58
64
|
* Result from Indexer.parse() method
|
|
59
65
|
*/
|
|
@@ -64,6 +70,12 @@ export interface ParseResult {
|
|
|
64
70
|
basket?: string;
|
|
65
71
|
/** Optional text content (e.g., from text inscriptions). Truncated to 1000 chars when stored. */
|
|
66
72
|
content?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Internalization protocol for this output.
|
|
75
|
+
* - "wallet payment": P2PKH outputs that can be auto-signed (default)
|
|
76
|
+
* - "basket insertion": Custom script outputs requiring manual unlock
|
|
77
|
+
*/
|
|
78
|
+
protocol?: InternalizeProtocol;
|
|
67
79
|
}
|
|
68
80
|
/**
|
|
69
81
|
* Transaction output structure used during parsing
|
|
@@ -73,6 +85,12 @@ export interface Txo {
|
|
|
73
85
|
outpoint: Outpoint;
|
|
74
86
|
owner?: string;
|
|
75
87
|
basket?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Internalization protocol for this output.
|
|
90
|
+
* Set by the first indexer to claim ownership.
|
|
91
|
+
* Defaults to "wallet payment" if not set.
|
|
92
|
+
*/
|
|
93
|
+
protocol?: InternalizeProtocol;
|
|
76
94
|
data: {
|
|
77
95
|
[tag: string]: IndexData;
|
|
78
96
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Beef, Transaction } from "@bsv/sdk";
|
|
2
|
-
import type
|
|
2
|
+
import { type TableOutput, type sdk as toolboxSdk } from "@bsv/wallet-toolbox";
|
|
3
3
|
type Chain = toolboxSdk.Chain;
|
|
4
4
|
type BlockHeader = toolboxSdk.BlockHeader;
|
|
5
5
|
type GetMerklePathResult = toolboxSdk.GetMerklePathResult;
|
|
@@ -32,7 +32,6 @@ export type { SyncOutput };
|
|
|
32
32
|
export declare class OneSatServices implements WalletServices {
|
|
33
33
|
chain: Chain;
|
|
34
34
|
readonly baseUrl: string;
|
|
35
|
-
private storage?;
|
|
36
35
|
readonly chaintracks: ChaintracksClient;
|
|
37
36
|
readonly beef: BeefClient;
|
|
38
37
|
readonly arcade: ArcadeClient;
|
|
@@ -40,7 +39,13 @@ export declare class OneSatServices implements WalletServices {
|
|
|
40
39
|
readonly owner: OwnerClient;
|
|
41
40
|
readonly ordfs: OrdfsClient;
|
|
42
41
|
readonly bsv21: Bsv21Client;
|
|
43
|
-
|
|
42
|
+
private fallbackServices?;
|
|
43
|
+
/**
|
|
44
|
+
* URL for wallet storage sync endpoint (BRC-100 JSON-RPC).
|
|
45
|
+
* Used by StorageClient for remote wallet backup/sync.
|
|
46
|
+
*/
|
|
47
|
+
get storageUrl(): string;
|
|
48
|
+
constructor(chain: Chain, baseUrl?: string, fallbackServices?: WalletServices);
|
|
44
49
|
/**
|
|
45
50
|
* Get list of enabled capabilities from the server
|
|
46
51
|
*/
|
|
@@ -59,11 +64,15 @@ export declare class OneSatServices implements WalletServices {
|
|
|
59
64
|
hashOutputScript(script: string): string;
|
|
60
65
|
getServicesCallHistory(_reset?: boolean): ServicesCallHistory;
|
|
61
66
|
getBsvExchangeRate(): Promise<number>;
|
|
62
|
-
getFiatExchangeRate(
|
|
63
|
-
getStatusForTxids(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
getFiatExchangeRate(currency: "USD" | "GBP" | "EUR", base?: "USD" | "GBP" | "EUR"): Promise<number>;
|
|
68
|
+
getStatusForTxids(txids: string[], _useNext?: boolean): Promise<GetStatusForTxidsResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Helper to get tx status from Beef storage (fallback when Arcade doesn't know the tx)
|
|
71
|
+
*/
|
|
72
|
+
private getStatusFromBeef;
|
|
73
|
+
isUtxo(output: TableOutput): Promise<boolean>;
|
|
74
|
+
getUtxoStatus(_output: string, _outputFormat?: GetUtxoStatusOutputFormat, outpoint?: string, _useNext?: boolean): Promise<GetUtxoStatusResult>;
|
|
75
|
+
getScriptHashHistory(hash: string, useNext?: boolean): Promise<GetScriptHashHistoryResult>;
|
|
76
|
+
hashToHeader(hash: string): Promise<BlockHeader>;
|
|
77
|
+
nLockTimeIsFinal(txOrLockTime: string | number[] | Transaction | number): Promise<boolean>;
|
|
69
78
|
}
|
|
@@ -32,7 +32,6 @@ import { ArcadeClient, BeefClient, Bsv21Client, ChaintracksClient, OrdfsClient,
|
|
|
32
32
|
export class OneSatServices {
|
|
33
33
|
chain;
|
|
34
34
|
baseUrl;
|
|
35
|
-
storage;
|
|
36
35
|
// ===== API Clients =====
|
|
37
36
|
chaintracks;
|
|
38
37
|
beef;
|
|
@@ -41,14 +40,23 @@ export class OneSatServices {
|
|
|
41
40
|
owner;
|
|
42
41
|
ordfs;
|
|
43
42
|
bsv21;
|
|
44
|
-
|
|
43
|
+
// Optional fallback to wallet-toolbox Services for methods we don't implement
|
|
44
|
+
fallbackServices;
|
|
45
|
+
/**
|
|
46
|
+
* URL for wallet storage sync endpoint (BRC-100 JSON-RPC).
|
|
47
|
+
* Used by StorageClient for remote wallet backup/sync.
|
|
48
|
+
*/
|
|
49
|
+
get storageUrl() {
|
|
50
|
+
return `${this.baseUrl}/1sat/wallet`;
|
|
51
|
+
}
|
|
52
|
+
constructor(chain, baseUrl, fallbackServices) {
|
|
53
|
+
this.fallbackServices = fallbackServices;
|
|
45
54
|
this.chain = chain;
|
|
46
55
|
this.baseUrl =
|
|
47
56
|
baseUrl ||
|
|
48
57
|
(chain === "main"
|
|
49
58
|
? "https://1sat.shruggr.cloud"
|
|
50
59
|
: "https://testnet.api.1sat.app");
|
|
51
|
-
this.storage = storage;
|
|
52
60
|
const opts = { timeout: 30000 };
|
|
53
61
|
this.chaintracks = new ChaintracksClient(this.baseUrl, opts);
|
|
54
62
|
this.beef = new BeefClient(this.baseUrl, opts);
|
|
@@ -101,12 +109,25 @@ export class OneSatServices {
|
|
|
101
109
|
return this.chaintracks.currentHeight();
|
|
102
110
|
}
|
|
103
111
|
async getMerklePath(txid, _useNext) {
|
|
112
|
+
console.log("[OneSatServices] getMerklePath called for txid:", txid);
|
|
104
113
|
try {
|
|
105
114
|
const proofBytes = await this.beef.getProof(txid);
|
|
106
|
-
const merklePath = MerklePath.fromBinary(
|
|
107
|
-
|
|
115
|
+
const merklePath = MerklePath.fromBinary([...proofBytes]);
|
|
116
|
+
console.log("[OneSatServices] getMerklePath got proof, blockHeight:", merklePath.blockHeight);
|
|
117
|
+
// Fetch the block header for this merkle path
|
|
118
|
+
const header = await this.chaintracks.findHeaderForHeight(merklePath.blockHeight);
|
|
119
|
+
if (!header) {
|
|
120
|
+
console.log("[OneSatServices] getMerklePath header not found for height:", merklePath.blockHeight);
|
|
121
|
+
return {
|
|
122
|
+
name: "1sat-api",
|
|
123
|
+
error: new ServiceError("HEADER_NOT_FOUND", `Block header not found for height ${merklePath.blockHeight}`),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
console.log("[OneSatServices] getMerklePath success, returning merklePath and header");
|
|
127
|
+
return { name: "1sat-api", merklePath, header };
|
|
108
128
|
}
|
|
109
129
|
catch (error) {
|
|
130
|
+
console.error("[OneSatServices] getMerklePath error:", error);
|
|
110
131
|
return {
|
|
111
132
|
name: "1sat-api",
|
|
112
133
|
error: new ServiceError("NETWORK_ERROR", error instanceof Error ? error.message : "Unknown error"),
|
|
@@ -114,27 +135,19 @@ export class OneSatServices {
|
|
|
114
135
|
}
|
|
115
136
|
}
|
|
116
137
|
async postBeef(beef, txids) {
|
|
138
|
+
console.log("[OneSatServices] postBeef called with txids:", txids);
|
|
117
139
|
const results = [];
|
|
118
140
|
for (const txid of txids) {
|
|
119
141
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
status: "error",
|
|
130
|
-
data: { detail: "Transaction not found in BEEF" },
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
|
-
});
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
// Use Extended Format (EF) which includes source transaction data
|
|
137
|
-
const status = await this.arcade.submitTransaction(beefTx.tx.toEF());
|
|
142
|
+
// Submit as AtomicBEEF which includes all source transactions
|
|
143
|
+
console.log("[OneSatServices] Submitting tx to arcade:", txid);
|
|
144
|
+
const atomicBeef = beef.toBinaryAtomic(txid);
|
|
145
|
+
// TODO: Remove hardcoded callback headers after server testing
|
|
146
|
+
const status = await this.arcade.submitTransaction(atomicBeef, {
|
|
147
|
+
callbackUrl: `${this.baseUrl}/1sat/arc/callback`,
|
|
148
|
+
callbackToken: "test-callback-token",
|
|
149
|
+
});
|
|
150
|
+
console.log("[OneSatServices] Arcade response:", status);
|
|
138
151
|
if (status.txStatus === "MINED" ||
|
|
139
152
|
status.txStatus === "SEEN_ON_NETWORK" ||
|
|
140
153
|
status.txStatus === "ACCEPTED_BY_NETWORK") {
|
|
@@ -163,6 +176,7 @@ export class OneSatServices {
|
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
178
|
catch (error) {
|
|
179
|
+
console.error("[OneSatServices] postBeef error:", error);
|
|
166
180
|
results.push({
|
|
167
181
|
name: "1sat-api",
|
|
168
182
|
status: "error",
|
|
@@ -203,29 +217,177 @@ export class OneSatServices {
|
|
|
203
217
|
},
|
|
204
218
|
};
|
|
205
219
|
}
|
|
206
|
-
// ===== WalletServices Interface (
|
|
220
|
+
// ===== WalletServices Interface (Delegated to fallback) =====
|
|
207
221
|
async getBsvExchangeRate() {
|
|
208
|
-
|
|
222
|
+
if (!this.fallbackServices) {
|
|
223
|
+
throw new Error("getBsvExchangeRate not implemented");
|
|
224
|
+
}
|
|
225
|
+
return this.fallbackServices.getBsvExchangeRate();
|
|
209
226
|
}
|
|
210
|
-
async getFiatExchangeRate(
|
|
211
|
-
|
|
227
|
+
async getFiatExchangeRate(currency, base) {
|
|
228
|
+
if (!this.fallbackServices) {
|
|
229
|
+
throw new Error("getFiatExchangeRate not implemented");
|
|
230
|
+
}
|
|
231
|
+
return this.fallbackServices.getFiatExchangeRate(currency, base);
|
|
212
232
|
}
|
|
213
|
-
async getStatusForTxids(
|
|
214
|
-
|
|
233
|
+
async getStatusForTxids(txids, _useNext) {
|
|
234
|
+
const results = [];
|
|
235
|
+
let currentHeight;
|
|
236
|
+
for (const txid of txids) {
|
|
237
|
+
try {
|
|
238
|
+
// Try Arcade first (only knows about txs broadcast through it)
|
|
239
|
+
const status = await this.arcade.getStatus(txid);
|
|
240
|
+
if (status.txStatus === "MINED" || status.txStatus === "IMMUTABLE") {
|
|
241
|
+
// Get current height for depth calculation if we haven't already
|
|
242
|
+
if (currentHeight === undefined) {
|
|
243
|
+
currentHeight = await this.getHeight();
|
|
244
|
+
}
|
|
245
|
+
const depth = status.blockHeight
|
|
246
|
+
? currentHeight - status.blockHeight + 1
|
|
247
|
+
: 1;
|
|
248
|
+
results.push({ txid, status: "mined", depth });
|
|
249
|
+
}
|
|
250
|
+
else if (status.txStatus === "SEEN_ON_NETWORK" ||
|
|
251
|
+
status.txStatus === "ACCEPTED_BY_NETWORK" ||
|
|
252
|
+
status.txStatus === "SENT_TO_NETWORK" ||
|
|
253
|
+
status.txStatus === "RECEIVED") {
|
|
254
|
+
results.push({ txid, status: "known", depth: 0 });
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// REJECTED, DOUBLE_SPEND_ATTEMPTED, UNKNOWN - fall back to Beef
|
|
258
|
+
results.push(await this.getStatusFromBeef(txid));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// Arcade 404 or error - fall back to Beef storage
|
|
263
|
+
// NOTE: If Arcade's scope is too limited (only knows txs it broadcast),
|
|
264
|
+
// consider using Beef storage as the primary source instead.
|
|
265
|
+
results.push(await this.getStatusFromBeef(txid));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
name: "1sat-api",
|
|
270
|
+
status: "success",
|
|
271
|
+
results,
|
|
272
|
+
};
|
|
215
273
|
}
|
|
216
|
-
|
|
217
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Helper to get tx status from Beef storage (fallback when Arcade doesn't know the tx)
|
|
276
|
+
*/
|
|
277
|
+
async getStatusFromBeef(txid) {
|
|
278
|
+
try {
|
|
279
|
+
const beefBytes = await this.beef.getBeef(txid);
|
|
280
|
+
const tx = Transaction.fromBEEF(Array.from(beefBytes));
|
|
281
|
+
if (tx.merklePath) {
|
|
282
|
+
// Has a valid merkle path = mined
|
|
283
|
+
const currentHeight = await this.getHeight();
|
|
284
|
+
const depth = currentHeight - tx.merklePath.blockHeight + 1;
|
|
285
|
+
return { txid, status: "mined", depth };
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// No merkle path = known but not yet mined
|
|
289
|
+
return { txid, status: "known", depth: 0 };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// 404 or error from Beef = unknown
|
|
294
|
+
return { txid, status: "unknown", depth: undefined };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async isUtxo(output) {
|
|
298
|
+
const outpoint = `${output.txid}_${output.vout}`;
|
|
299
|
+
const spendTxid = await this.txo.getSpend(outpoint);
|
|
300
|
+
return spendTxid === null;
|
|
218
301
|
}
|
|
219
|
-
async getUtxoStatus(_output, _outputFormat,
|
|
220
|
-
|
|
302
|
+
async getUtxoStatus(_output, _outputFormat, outpoint, _useNext) {
|
|
303
|
+
// We ignore _output (script hash) since we look up directly by outpoint
|
|
304
|
+
if (!outpoint) {
|
|
305
|
+
return {
|
|
306
|
+
name: "1sat-api",
|
|
307
|
+
status: "error",
|
|
308
|
+
error: new ServiceError("INVALID_PARAMETER", "outpoint is required for getUtxoStatus"),
|
|
309
|
+
details: [],
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
const txo = await this.txo.get(outpoint, {
|
|
314
|
+
sats: true,
|
|
315
|
+
spend: true,
|
|
316
|
+
block: true,
|
|
317
|
+
});
|
|
318
|
+
const isUtxo = !txo.spend;
|
|
319
|
+
const [txid, voutStr] = txo.outpoint.split("_");
|
|
320
|
+
const vout = parseInt(voutStr, 10);
|
|
321
|
+
return {
|
|
322
|
+
name: "1sat-api",
|
|
323
|
+
status: "success",
|
|
324
|
+
isUtxo,
|
|
325
|
+
details: isUtxo
|
|
326
|
+
? [
|
|
327
|
+
{
|
|
328
|
+
txid,
|
|
329
|
+
index: vout,
|
|
330
|
+
satoshis: txo.satoshis,
|
|
331
|
+
height: txo.blockHeight,
|
|
332
|
+
},
|
|
333
|
+
]
|
|
334
|
+
: [],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
// TXO not found - treat as not a UTXO
|
|
339
|
+
return {
|
|
340
|
+
name: "1sat-api",
|
|
341
|
+
status: "success",
|
|
342
|
+
isUtxo: false,
|
|
343
|
+
details: [],
|
|
344
|
+
};
|
|
345
|
+
}
|
|
221
346
|
}
|
|
222
|
-
async getScriptHashHistory(
|
|
223
|
-
|
|
347
|
+
async getScriptHashHistory(hash, useNext) {
|
|
348
|
+
if (!this.fallbackServices) {
|
|
349
|
+
throw new Error("getScriptHashHistory not implemented");
|
|
350
|
+
}
|
|
351
|
+
return this.fallbackServices.getScriptHashHistory(hash, useNext);
|
|
224
352
|
}
|
|
225
|
-
async hashToHeader(
|
|
226
|
-
|
|
353
|
+
async hashToHeader(hash) {
|
|
354
|
+
const header = await this.chaintracks.findHeaderForBlockHash(hash);
|
|
355
|
+
if (!header) {
|
|
356
|
+
throw new Error(`Block header not found for hash: ${hash}`);
|
|
357
|
+
}
|
|
358
|
+
return header;
|
|
227
359
|
}
|
|
228
|
-
async nLockTimeIsFinal(
|
|
229
|
-
|
|
360
|
+
async nLockTimeIsFinal(txOrLockTime) {
|
|
361
|
+
const MAXINT = 0xffffffff;
|
|
362
|
+
const BLOCK_LIMIT = 500000000;
|
|
363
|
+
let nLockTime;
|
|
364
|
+
if (typeof txOrLockTime === "number") {
|
|
365
|
+
nLockTime = txOrLockTime;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
let tx;
|
|
369
|
+
if (typeof txOrLockTime === "string") {
|
|
370
|
+
tx = Transaction.fromHex(txOrLockTime);
|
|
371
|
+
}
|
|
372
|
+
else if (Array.isArray(txOrLockTime)) {
|
|
373
|
+
tx = Transaction.fromBinary(txOrLockTime);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
tx = txOrLockTime;
|
|
377
|
+
}
|
|
378
|
+
// If all inputs have max sequence, the transaction is final regardless of lockTime
|
|
379
|
+
if (tx.inputs.every((i) => i.sequence === MAXINT)) {
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
nLockTime = tx.lockTime;
|
|
383
|
+
}
|
|
384
|
+
// If lockTime >= BLOCK_LIMIT, it's a timestamp (seconds since epoch)
|
|
385
|
+
if (nLockTime >= BLOCK_LIMIT) {
|
|
386
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
387
|
+
return nLockTime < currentTime;
|
|
388
|
+
}
|
|
389
|
+
// Otherwise, it's a block height
|
|
390
|
+
const height = await this.getHeight();
|
|
391
|
+
return nLockTime < height;
|
|
230
392
|
}
|
|
231
393
|
}
|