@zebec-network/exchange-card-sdk 1.9.0-dev.7 → 1.9.0-dev.9
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/services/aleoService.d.ts +4 -2
- package/dist/services/aleoService.js +67 -23
- package/dist/services/cantonService.d.ts +0 -31
- package/dist/services/cantonService.js +103 -57
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +1 -1
- package/dist/utils.d.ts +1 -0
- package/package.json +4 -5
- package/dist/services/bitcoinService.d.ts +0 -48
- package/dist/services/bitcoinService.js +0 -146
|
@@ -83,6 +83,8 @@ export declare class AleoService {
|
|
|
83
83
|
transferStableCoin(params: AleoTransferStableCoinParams): Promise<{
|
|
84
84
|
transactionId: string;
|
|
85
85
|
}>;
|
|
86
|
-
getPublicBalance(
|
|
87
|
-
getPublicTokenBalance(
|
|
86
|
+
getPublicBalance(): Promise<string>;
|
|
87
|
+
getPublicTokenBalance(tokenProgramId: string, tokenSymbol: string): Promise<string>;
|
|
88
|
+
getPrivateBalance(): Promise<string>;
|
|
89
|
+
getPrivateTokenBalance(tokenProgramId: string, tokenSymbol: string): Promise<string>;
|
|
88
90
|
}
|
|
@@ -181,28 +181,12 @@ export class AleoService {
|
|
|
181
181
|
});
|
|
182
182
|
return result;
|
|
183
183
|
}
|
|
184
|
-
async getPublicBalance(
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
if (balance) {
|
|
189
|
-
// regex to extract the number part and convert it to a string with 6 decimal places
|
|
190
|
-
const regex = /(\d+)u64/;
|
|
191
|
-
const match = balance.match(regex);
|
|
192
|
-
if (match) {
|
|
193
|
-
const amount = match[1];
|
|
194
|
-
const formattedAmount = fromMicroUnits(amount);
|
|
195
|
-
return formattedAmount;
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
throw new Error(`Invalid balance format: ${balance}`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
return "0";
|
|
203
|
-
}
|
|
184
|
+
async getPublicBalance() {
|
|
185
|
+
const balance = await this.networkClient.getPublicBalance(this.wallet.address);
|
|
186
|
+
const formattedAmount = fromMicroUnits(balance);
|
|
187
|
+
return formattedAmount;
|
|
204
188
|
}
|
|
205
|
-
async getPublicTokenBalance(
|
|
189
|
+
async getPublicTokenBalance(tokenProgramId, tokenSymbol) {
|
|
206
190
|
const tokenMetadata = await getTokenBySymbol(tokenSymbol, this.sandbox ? "testnet" : "mainnet");
|
|
207
191
|
if (!("decimals" in tokenMetadata)) {
|
|
208
192
|
throw new Error(`Token metadata for ${tokenSymbol} does not include decimals.`);
|
|
@@ -216,9 +200,9 @@ export class AleoService {
|
|
|
216
200
|
if (!balanceMappingName) {
|
|
217
201
|
throw new Error("No public balance mapping found (no 'balances' or 'account').");
|
|
218
202
|
}
|
|
219
|
-
const balance = await this.networkClient.getProgramMappingValue(tokenProgramId, balanceMappingName,
|
|
203
|
+
const balance = await this.networkClient.getProgramMappingValue(tokenProgramId, balanceMappingName, this.wallet.address);
|
|
220
204
|
if (balance) {
|
|
221
|
-
const regex = /(\d+)
|
|
205
|
+
const regex = /(\d+)u\d+/;
|
|
222
206
|
const match = balance.match(regex);
|
|
223
207
|
if (match) {
|
|
224
208
|
const amount = match[1];
|
|
@@ -233,4 +217,64 @@ export class AleoService {
|
|
|
233
217
|
return "0";
|
|
234
218
|
}
|
|
235
219
|
}
|
|
220
|
+
async getPrivateBalance() {
|
|
221
|
+
const programId = "credits.aleo";
|
|
222
|
+
const records = await this.wallet.requestRecords(programId, false);
|
|
223
|
+
if (!records) {
|
|
224
|
+
throw new Error(`No records found for program ${programId}`);
|
|
225
|
+
}
|
|
226
|
+
// console.log("Fetched Records:", records);
|
|
227
|
+
const unspent = records.filter((r) => r && typeof r === "object" && "spent" in r && !r.spent);
|
|
228
|
+
if (!unspent || !unspent.length) {
|
|
229
|
+
throw new Error(`No unspent ${programId} records found`);
|
|
230
|
+
}
|
|
231
|
+
const decrypted = await Promise.all(unspent.map(async (rec) => {
|
|
232
|
+
if (!rec ||
|
|
233
|
+
typeof rec !== "object" ||
|
|
234
|
+
!("recordCiphertext" in rec) ||
|
|
235
|
+
typeof rec.recordCiphertext !== "string") {
|
|
236
|
+
throw new Error(`Invalid record format: ${JSON.stringify(rec)}`);
|
|
237
|
+
}
|
|
238
|
+
const plaintext = await this.wallet.decrypt(rec.recordCiphertext);
|
|
239
|
+
return plaintext.replace(/\s+/g, " ").trim();
|
|
240
|
+
}));
|
|
241
|
+
const balance = decrypted
|
|
242
|
+
.map((line) => {
|
|
243
|
+
const match = line.match(/microcredits:\s*(\d+)u64/);
|
|
244
|
+
return match ? BigInt(match[1]) : 0n;
|
|
245
|
+
})
|
|
246
|
+
.reduce((acc, val) => acc + val, 0n);
|
|
247
|
+
return fromMicroUnits(balance, 6);
|
|
248
|
+
}
|
|
249
|
+
async getPrivateTokenBalance(tokenProgramId, tokenSymbol) {
|
|
250
|
+
const records = await this.wallet.requestRecords(tokenProgramId, false);
|
|
251
|
+
if (!records) {
|
|
252
|
+
throw new Error(`No records found for program ${tokenProgramId}`);
|
|
253
|
+
}
|
|
254
|
+
const unspent = records.filter((r) => r && typeof r === "object" && "spent" in r && !r.spent);
|
|
255
|
+
if (!unspent || !unspent.length) {
|
|
256
|
+
throw new Error(`No unspent ${tokenProgramId} records found`);
|
|
257
|
+
}
|
|
258
|
+
const decrypted = await Promise.all(unspent.map(async (rec) => {
|
|
259
|
+
if (!rec ||
|
|
260
|
+
typeof rec !== "object" ||
|
|
261
|
+
!("recordCiphertext" in rec) ||
|
|
262
|
+
typeof rec.recordCiphertext !== "string") {
|
|
263
|
+
throw new Error(`Invalid record format: ${JSON.stringify(rec)}`);
|
|
264
|
+
}
|
|
265
|
+
const plaintext = await this.wallet.decrypt(rec.recordCiphertext);
|
|
266
|
+
return plaintext.replace(/\s+/g, " ").trim();
|
|
267
|
+
}));
|
|
268
|
+
const balance = decrypted
|
|
269
|
+
.map((line) => {
|
|
270
|
+
const match = line.match(/amount:\s*(\d+)u\d+/);
|
|
271
|
+
return match ? BigInt(match[1]) : 0n;
|
|
272
|
+
})
|
|
273
|
+
.reduce((acc, val) => acc + val, 0n);
|
|
274
|
+
const tokenMetadata = await getTokenBySymbol(tokenSymbol, this.sandbox ? "testnet" : "mainnet");
|
|
275
|
+
if (!("decimals" in tokenMetadata)) {
|
|
276
|
+
throw new Error(`Token metadata for ${tokenSymbol} does not include decimals.`);
|
|
277
|
+
}
|
|
278
|
+
return fromMicroUnits(balance, tokenMetadata.decimals);
|
|
279
|
+
}
|
|
236
280
|
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type AuthTokenProvider, LedgerController, TokenStandardController, ValidatorController, type WalletSDK } from "@canton-network/wallet-sdk";
|
|
2
|
-
export interface CantonWallet {
|
|
3
|
-
partyId: string;
|
|
4
|
-
executeTransaction: (command: unknown, disclosedContractId: unknown[]) => Promise<string>;
|
|
5
|
-
}
|
|
6
|
-
export interface CantonConfig {
|
|
7
|
-
ledgerApiUrl: string;
|
|
8
|
-
validatorAppApiUrl: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function createLedgerFactory(ledgerApiUrl: string): (userId: string, authTokenProvider: AuthTokenProvider, isAdmin: boolean) => LedgerController;
|
|
11
|
-
export declare function createValidatorFactory(validatorAppApiUrl: string): (userId: string, authTokenProvider: AuthTokenProvider) => ValidatorController;
|
|
12
|
-
export declare function createTokenStandardFactory(ledgerApiUrl: string, validatorAppApiUrl: string): (userId: string, authTokenProvider: AuthTokenProvider, isAdmin: boolean) => TokenStandardController;
|
|
13
|
-
export declare class CantonService {
|
|
14
|
-
readonly wallet: CantonWallet;
|
|
15
|
-
readonly cantonConfig: CantonConfig;
|
|
16
|
-
readonly cantonWalletSdk: WalletSDK;
|
|
17
|
-
private apiService;
|
|
18
|
-
constructor(wallet: CantonWallet, cantonConfig: CantonConfig, sdkOptions?: {
|
|
19
|
-
sandbox?: boolean;
|
|
20
|
-
});
|
|
21
|
-
connect(): Promise<void>;
|
|
22
|
-
fetchVault(symbol?: string): Promise<{
|
|
23
|
-
address: string;
|
|
24
|
-
tag?: string;
|
|
25
|
-
}>;
|
|
26
|
-
transferNative(params: {
|
|
27
|
-
amount: number | string;
|
|
28
|
-
instrumentId: string;
|
|
29
|
-
instrumentAdmin: string;
|
|
30
|
-
}): Promise<string>;
|
|
31
|
-
}
|
|
@@ -1,57 +1,103 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
// import {
|
|
3
|
+
// type AuthTokenProvider,
|
|
4
|
+
// LedgerController,
|
|
5
|
+
// localNetStaticConfig,
|
|
6
|
+
// TokenStandardController,
|
|
7
|
+
// ValidatorController,
|
|
8
|
+
// type WalletSDK,
|
|
9
|
+
// WalletSDKImpl,
|
|
10
|
+
// } from "@canton-network/wallet-sdk";
|
|
11
|
+
// import { ZebecCardAPIService } from "../helpers/apiHelpers";
|
|
12
|
+
// export interface CantonWallet {
|
|
13
|
+
// partyId: string;
|
|
14
|
+
// executeTransaction: (command: unknown, disclosedContractId: unknown[]) => Promise<string>;
|
|
15
|
+
// }
|
|
16
|
+
// export interface CantonConfig {
|
|
17
|
+
// ledgerApiUrl: string;
|
|
18
|
+
// validatorAppApiUrl: string;
|
|
19
|
+
// }
|
|
20
|
+
// export function createLedgerFactory(ledgerApiUrl: string) {
|
|
21
|
+
// return (userId: string, authTokenProvider: AuthTokenProvider, isAdmin: boolean) => {
|
|
22
|
+
// return new LedgerController(
|
|
23
|
+
// userId,
|
|
24
|
+
// new URL(ledgerApiUrl),
|
|
25
|
+
// undefined,
|
|
26
|
+
// isAdmin,
|
|
27
|
+
// authTokenProvider,
|
|
28
|
+
// );
|
|
29
|
+
// };
|
|
30
|
+
// }
|
|
31
|
+
// export function createValidatorFactory(validatorAppApiUrl: string) {
|
|
32
|
+
// return (userId: string, authTokenProvider: AuthTokenProvider) => {
|
|
33
|
+
// return new ValidatorController(userId, new URL(validatorAppApiUrl), authTokenProvider);
|
|
34
|
+
// };
|
|
35
|
+
// }
|
|
36
|
+
// export function createTokenStandardFactory(ledgerApiUrl: string, validatorAppApiUrl: string) {
|
|
37
|
+
// return (userId: string, authTokenProvider: AuthTokenProvider, isAdmin: boolean) => {
|
|
38
|
+
// return new TokenStandardController(
|
|
39
|
+
// userId,
|
|
40
|
+
// new URL(ledgerApiUrl),
|
|
41
|
+
// new URL(validatorAppApiUrl),
|
|
42
|
+
// undefined,
|
|
43
|
+
// authTokenProvider,
|
|
44
|
+
// isAdmin,
|
|
45
|
+
// );
|
|
46
|
+
// };
|
|
47
|
+
// }
|
|
48
|
+
// export class CantonService {
|
|
49
|
+
// readonly cantonWalletSdk: WalletSDK;
|
|
50
|
+
// private apiService: ZebecCardAPIService;
|
|
51
|
+
// constructor(
|
|
52
|
+
// readonly wallet: CantonWallet,
|
|
53
|
+
// readonly cantonConfig: CantonConfig,
|
|
54
|
+
// sdkOptions?: { sandbox?: boolean },
|
|
55
|
+
// ) {
|
|
56
|
+
// this.apiService = new ZebecCardAPIService(sdkOptions?.sandbox || false);
|
|
57
|
+
// this.cantonWalletSdk = new WalletSDKImpl().configure({
|
|
58
|
+
// logger: console,
|
|
59
|
+
// ledgerFactory: createLedgerFactory(cantonConfig.ledgerApiUrl),
|
|
60
|
+
// validatorFactory: createValidatorFactory(cantonConfig.validatorAppApiUrl),
|
|
61
|
+
// tokenStandardFactory: createTokenStandardFactory(
|
|
62
|
+
// cantonConfig.ledgerApiUrl,
|
|
63
|
+
// cantonConfig.validatorAppApiUrl,
|
|
64
|
+
// ),
|
|
65
|
+
// });
|
|
66
|
+
// }
|
|
67
|
+
// async connect() {
|
|
68
|
+
// await this.cantonWalletSdk.connect();
|
|
69
|
+
// }
|
|
70
|
+
// async fetchVault(symbol = "CANTON"): Promise<{ address: string; tag?: string }> {
|
|
71
|
+
// const data = await this.apiService.fetchVault(symbol);
|
|
72
|
+
// return data;
|
|
73
|
+
// }
|
|
74
|
+
// // methods for transfering natve assets
|
|
75
|
+
// async transferNative(params: {
|
|
76
|
+
// amount: number | string;
|
|
77
|
+
// instrumentId: string;
|
|
78
|
+
// instrumentAdmin: string;
|
|
79
|
+
// }) {
|
|
80
|
+
// const vault = await this.fetchVault("CANTON");
|
|
81
|
+
// const sender = this.wallet.partyId;
|
|
82
|
+
// const receiver = vault.address;
|
|
83
|
+
// const memo = vault.tag;
|
|
84
|
+
// // implement transfer logic here
|
|
85
|
+
// this.cantonWalletSdk.tokenStandard?.setTransferFactoryRegistryUrl(
|
|
86
|
+
// localNetStaticConfig.LOCALNET_REGISTRY_API_URL,
|
|
87
|
+
// );
|
|
88
|
+
// const [transferCommand, disclosedContracts] =
|
|
89
|
+
// // biome-ignore lint/style/noNonNullAssertion: we can be sure that tokenStandard is defined here since we set its factory in the SDK constructor
|
|
90
|
+
// await this.cantonWalletSdk.tokenStandard!.createTransfer(
|
|
91
|
+
// sender,
|
|
92
|
+
// receiver,
|
|
93
|
+
// params.amount.toString(),
|
|
94
|
+
// {
|
|
95
|
+
// instrumentId: params.instrumentId,
|
|
96
|
+
// instrumentAdmin: params.instrumentAdmin,
|
|
97
|
+
// },
|
|
98
|
+
// [],
|
|
99
|
+
// memo,
|
|
100
|
+
// );
|
|
101
|
+
// return this.wallet.executeTransaction(transferCommand, disclosedContracts);
|
|
102
|
+
// }
|
|
103
|
+
// }
|
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./aleoService";
|
|
2
2
|
export * from "./algorandService";
|
|
3
3
|
export * from "./bobaService";
|
|
4
|
-
export * from "./cantonService";
|
|
4
|
+
// export * from "./cantonService";
|
|
5
5
|
export * from "./nearService";
|
|
6
6
|
export * from "./octaService";
|
|
7
7
|
export * from "./quaiService";
|
package/dist/utils.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Zebec Network | Ashish Sapkota",
|
|
3
3
|
"dependencies": {
|
|
4
|
-
"@canton-network/wallet-sdk": "^0.21.1",
|
|
5
4
|
"@near-js/crypto": "^2.5.1",
|
|
6
5
|
"@near-js/providers": "^2.5.1",
|
|
7
6
|
"@near-js/transactions": "^2.5.1",
|
|
@@ -12,8 +11,7 @@
|
|
|
12
11
|
"@stellar/stellar-sdk": "^14.4.3",
|
|
13
12
|
"algosdk": "^3.4.0",
|
|
14
13
|
"axios": "^1.11.0",
|
|
15
|
-
"bignumber.js": "^
|
|
16
|
-
"bitcoinjs-lib": "^6.1.7",
|
|
14
|
+
"bignumber.js": "^10.0.2",
|
|
17
15
|
"ethers": "^6.15.0",
|
|
18
16
|
"quais": "^1.0.0-alpha.52",
|
|
19
17
|
"xrpl": "^4.4.1"
|
|
@@ -25,6 +23,7 @@
|
|
|
25
23
|
"@near-js/accounts": "^2.5.1",
|
|
26
24
|
"@near-js/keystores": "^2.5.1",
|
|
27
25
|
"@near-js/signers": "^2.5.1",
|
|
26
|
+
"@near-js/tokens": "^2.5.1",
|
|
28
27
|
"@typechain/ethers-v6": "^0.5.1",
|
|
29
28
|
"@types/mocha": "^10.0.10",
|
|
30
29
|
"@types/node": "^24.3.1",
|
|
@@ -54,7 +53,7 @@
|
|
|
54
53
|
},
|
|
55
54
|
"repository": {
|
|
56
55
|
"type": "git",
|
|
57
|
-
"url": "git+
|
|
56
|
+
"url": "git+git@github.com:Zebec-Fintech-Labs/card-sdk-mono.git"
|
|
58
57
|
},
|
|
59
58
|
"scripts": {
|
|
60
59
|
"build": "npm run clean && tsc",
|
|
@@ -65,5 +64,5 @@
|
|
|
65
64
|
},
|
|
66
65
|
"type": "module",
|
|
67
66
|
"types": "dist/index.d.ts",
|
|
68
|
-
"version": "1.9.0-dev.
|
|
67
|
+
"version": "1.9.0-dev.9"
|
|
69
68
|
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import * as bitcoin from "bitcoinjs-lib";
|
|
2
|
-
interface BitcoinWallet {
|
|
3
|
-
address: string;
|
|
4
|
-
signTransaction: (psbt: bitcoin.Psbt) => Promise<bitcoin.Psbt>;
|
|
5
|
-
broadcastTransaction: (tx: string) => Promise<string>;
|
|
6
|
-
}
|
|
7
|
-
export declare class BitcoinService {
|
|
8
|
-
readonly wallet: BitcoinWallet;
|
|
9
|
-
private apiService;
|
|
10
|
-
private network;
|
|
11
|
-
private apiEndpoint;
|
|
12
|
-
constructor(wallet: BitcoinWallet, sdkOptions?: {
|
|
13
|
-
sandbox?: boolean;
|
|
14
|
-
apiKey?: string;
|
|
15
|
-
});
|
|
16
|
-
/**
|
|
17
|
-
* Fetches the Bitcoin vault address.
|
|
18
|
-
*
|
|
19
|
-
* @returns {Promise<{ address: string }>} A promise that resolves to the vault address.
|
|
20
|
-
*/
|
|
21
|
-
fetchVault(): Promise<{
|
|
22
|
-
address: string;
|
|
23
|
-
tag?: string;
|
|
24
|
-
}>;
|
|
25
|
-
getUTXOs(): Promise<Array<{
|
|
26
|
-
txid: string;
|
|
27
|
-
vout: number;
|
|
28
|
-
value: number;
|
|
29
|
-
rawTx: Buffer;
|
|
30
|
-
}>>;
|
|
31
|
-
private getBalance;
|
|
32
|
-
/**
|
|
33
|
-
* Transfers Bitcoin to the vault address.
|
|
34
|
-
*
|
|
35
|
-
* @param {string} amount - The amount of BTC to transfer in BTC units
|
|
36
|
-
* @param {number} feeRate - Fee rate in satoshis per byte
|
|
37
|
-
* @returns {Promise<string>} - A promise that resolves to the transaction hash
|
|
38
|
-
* @throws {Error} If there is not enough balance or if the transaction fails.
|
|
39
|
-
*/
|
|
40
|
-
transferBTC(amount: string, feeRate?: number): Promise<string>;
|
|
41
|
-
/**
|
|
42
|
-
* Gets the balance of the Bitcoin wallet.
|
|
43
|
-
*
|
|
44
|
-
* @returns {Promise<string>} - A promise that resolves to the wallet balance in BTC
|
|
45
|
-
*/
|
|
46
|
-
getWalletBalance(): Promise<string>;
|
|
47
|
-
}
|
|
48
|
-
export {};
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
import * as bitcoin from "bitcoinjs-lib";
|
|
3
|
-
import { BITCOIN_ENDPOINTS } from "../constants";
|
|
4
|
-
import { ZebecCardAPIService } from "../helpers/apiHelpers";
|
|
5
|
-
export class BitcoinService {
|
|
6
|
-
wallet;
|
|
7
|
-
apiService;
|
|
8
|
-
network;
|
|
9
|
-
apiEndpoint;
|
|
10
|
-
constructor(wallet, sdkOptions) {
|
|
11
|
-
this.wallet = wallet;
|
|
12
|
-
const sandbox = sdkOptions?.sandbox ?? false;
|
|
13
|
-
this.apiService = new ZebecCardAPIService(sandbox);
|
|
14
|
-
this.network = sandbox ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
|
|
15
|
-
this.apiEndpoint = sandbox ? BITCOIN_ENDPOINTS.Sandbox : BITCOIN_ENDPOINTS.Production;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Fetches the Bitcoin vault address.
|
|
19
|
-
*
|
|
20
|
-
* @returns {Promise<{ address: string }>} A promise that resolves to the vault address.
|
|
21
|
-
*/
|
|
22
|
-
async fetchVault() {
|
|
23
|
-
const data = await this.apiService.fetchVault("BTC");
|
|
24
|
-
return data;
|
|
25
|
-
}
|
|
26
|
-
async getUTXOs() {
|
|
27
|
-
const response = await axios.get(`${this.apiEndpoint}/address/${this.wallet.address}/utxo`);
|
|
28
|
-
console.log("utxos:", response.data);
|
|
29
|
-
return Promise.all(response.data.map(async (utxo) => {
|
|
30
|
-
const rawTx = await axios.get(`${this.apiEndpoint}/tx/${utxo.txid}/hex`);
|
|
31
|
-
console.log("txHex:", rawTx.data);
|
|
32
|
-
if (!rawTx.data) {
|
|
33
|
-
throw new Error("Transaction not found");
|
|
34
|
-
}
|
|
35
|
-
// const scriptPubKey = txResponse.data.vout[utxo.vout].scriptpubkey;
|
|
36
|
-
return {
|
|
37
|
-
txid: utxo.txid,
|
|
38
|
-
vout: utxo.vout,
|
|
39
|
-
value: utxo.value,
|
|
40
|
-
rawTx: Buffer.from(rawTx.data, "hex"),
|
|
41
|
-
};
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
async getBalance() {
|
|
45
|
-
const response = await axios.get(`${this.apiEndpoint}/address/${this.wallet.address}/utxo`);
|
|
46
|
-
const utxos = response.data;
|
|
47
|
-
return utxos.reduce((sum, utxo) => sum + utxo.value, 0);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Transfers Bitcoin to the vault address.
|
|
51
|
-
*
|
|
52
|
-
* @param {string} amount - The amount of BTC to transfer in BTC units
|
|
53
|
-
* @param {number} feeRate - Fee rate in satoshis per byte
|
|
54
|
-
* @returns {Promise<string>} - A promise that resolves to the transaction hash
|
|
55
|
-
* @throws {Error} If there is not enough balance or if the transaction fails.
|
|
56
|
-
*/
|
|
57
|
-
async transferBTC(amount, feeRate = 10) {
|
|
58
|
-
// Convert BTC to satoshis
|
|
59
|
-
const satoshisToSend = Math.floor(Number(amount) * 100_000_000);
|
|
60
|
-
// Fetch deposit address
|
|
61
|
-
const vault = await this.fetchVault();
|
|
62
|
-
console.log({ vault });
|
|
63
|
-
let retries = 0;
|
|
64
|
-
const maxRetries = 5;
|
|
65
|
-
let delay = 1000;
|
|
66
|
-
const psbt = new bitcoin.Psbt({ network: this.network });
|
|
67
|
-
const utxos = await this.getUTXOs();
|
|
68
|
-
let inputAmount = 0;
|
|
69
|
-
for (const utxo of utxos) {
|
|
70
|
-
const transaction = bitcoin.Transaction.fromBuffer(utxo.rawTx);
|
|
71
|
-
const script = transaction.outs[utxo.vout].script;
|
|
72
|
-
const value = transaction.outs[utxo.vout].value;
|
|
73
|
-
inputAmount += value;
|
|
74
|
-
psbt.addInput({
|
|
75
|
-
hash: utxo.txid,
|
|
76
|
-
index: utxo.vout,
|
|
77
|
-
// nonWitnessUtxo: utxo.rawTx,
|
|
78
|
-
witnessUtxo: {
|
|
79
|
-
script: script,
|
|
80
|
-
value: value,
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
if (inputAmount >= satoshisToSend)
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
if (inputAmount < satoshisToSend) {
|
|
87
|
-
throw new Error("Insufficient UTXO amount");
|
|
88
|
-
}
|
|
89
|
-
// Add output for payment
|
|
90
|
-
psbt.addOutput({
|
|
91
|
-
// address: vault.address,
|
|
92
|
-
address: "tb1q6h8w53xzj74n28kg8qq3d78xxgrch8zd2km97d",
|
|
93
|
-
value: satoshisToSend,
|
|
94
|
-
});
|
|
95
|
-
// Add change output if necessary
|
|
96
|
-
const estimatedFee = Math.ceil(psbt.toBuffer().length * feeRate);
|
|
97
|
-
// Check wallet balance
|
|
98
|
-
const balance = utxos.reduce((sum, utxo) => sum + utxo.value, 0);
|
|
99
|
-
if (balance < satoshisToSend + estimatedFee) {
|
|
100
|
-
throw new Error("Insufficient balance");
|
|
101
|
-
}
|
|
102
|
-
const changeAmount = inputAmount - satoshisToSend - estimatedFee;
|
|
103
|
-
if (changeAmount > 0) {
|
|
104
|
-
psbt.addOutput({
|
|
105
|
-
address: this.wallet.address,
|
|
106
|
-
value: changeAmount,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
console.log("psbt:", JSON.stringify(psbt));
|
|
110
|
-
// Sign transaction
|
|
111
|
-
const signedPsbt = await this.wallet.signTransaction(psbt);
|
|
112
|
-
const tx = signedPsbt.finalizeAllInputs().extractTransaction();
|
|
113
|
-
while (retries < maxRetries) {
|
|
114
|
-
try {
|
|
115
|
-
// Broadcast transaction
|
|
116
|
-
return this.wallet.broadcastTransaction(tx.toHex());
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
console.debug("error: ", error);
|
|
120
|
-
if (retries >= maxRetries) {
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
123
|
-
retries += 1;
|
|
124
|
-
console.debug(`Retrying in ${delay / 1000} seconds...`);
|
|
125
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
126
|
-
delay *= 2; // Exponential backoff
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
throw new Error("Max retries reached");
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Gets the balance of the Bitcoin wallet.
|
|
133
|
-
*
|
|
134
|
-
* @returns {Promise<string>} - A promise that resolves to the wallet balance in BTC
|
|
135
|
-
*/
|
|
136
|
-
async getWalletBalance() {
|
|
137
|
-
try {
|
|
138
|
-
const balanceSats = await this.getBalance();
|
|
139
|
-
return (balanceSats / 100_000_000).toString();
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
console.debug("Error fetching BTC balance:", error);
|
|
143
|
-
return "0";
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|