@ic-pay/icpay-sdk 1.3.61
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 +32 -0
- package/dist/declarations/icp-ledger/icp-ledger.did.d.ts +82 -0
- package/dist/declarations/icp-ledger/icp-ledger.did.js +76 -0
- package/dist/declarations/icpay_canister_backend/icpay_canister_backend.did +193 -0
- package/dist/declarations/icpay_canister_backend/icpay_canister_backend.did.d.ts +219 -0
- package/dist/declarations/icpay_canister_backend/icpay_canister_backend.did.js +231 -0
- package/dist/declarations/icrc-ledger/ledger.did +560 -0
- package/dist/declarations/icrc-ledger/ledger.did.d.ts +364 -0
- package/dist/declarations/icrc-ledger/ledger.did.js +530 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +162 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +24 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +131 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +163 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1211 -0
- package/dist/index.js.map +1 -0
- package/dist/protected.d.ts +28 -0
- package/dist/protected.d.ts.map +1 -0
- package/dist/protected.js +336 -0
- package/dist/protected.js.map +1 -0
- package/dist/types/index.d.ts +482 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +42 -0
- package/dist/utils.js.map +1 -0
- package/dist/wallet.d.ts +83 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +261 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.IcpayWallet = exports.IcpayError = exports.Icpay = void 0;
|
|
21
|
+
const errors_1 = require("./errors");
|
|
22
|
+
const events_1 = require("./events");
|
|
23
|
+
const wallet_1 = require("./wallet");
|
|
24
|
+
const axios_1 = __importDefault(require("axios"));
|
|
25
|
+
const agent_1 = require("@dfinity/agent");
|
|
26
|
+
const icpay_canister_backend_did_js_1 = require("./declarations/icpay_canister_backend/icpay_canister_backend.did.js");
|
|
27
|
+
const ledger_did_js_1 = require("./declarations/icrc-ledger/ledger.did.js");
|
|
28
|
+
const principal_1 = require("@dfinity/principal");
|
|
29
|
+
const utils_1 = require("./utils");
|
|
30
|
+
const protected_1 = require("./protected");
|
|
31
|
+
class Icpay {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.privateApiClient = null;
|
|
34
|
+
this.connectedWallet = null;
|
|
35
|
+
this.icpayCanisterId = null;
|
|
36
|
+
this.accountInfoCache = null;
|
|
37
|
+
this.verifiedLedgersCache = { data: null, timestamp: 0 };
|
|
38
|
+
this.config = {
|
|
39
|
+
environment: 'production',
|
|
40
|
+
apiUrl: 'https://api.icpay.org',
|
|
41
|
+
debug: false,
|
|
42
|
+
enableEvents: false,
|
|
43
|
+
awaitServerNotification: false,
|
|
44
|
+
...config
|
|
45
|
+
};
|
|
46
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'constructor', { config: this.config });
|
|
47
|
+
// Validate authentication configuration
|
|
48
|
+
if (!this.config.publishableKey && !this.config.secretKey) {
|
|
49
|
+
throw new Error('Either publishableKey or secretKey must be provided');
|
|
50
|
+
}
|
|
51
|
+
this.icHost = config.icHost || 'https://ic0.app';
|
|
52
|
+
this.connectedWallet = config.connectedWallet || null;
|
|
53
|
+
this.actorProvider = config.actorProvider;
|
|
54
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'constructor', { connectedWallet: this.connectedWallet, actorProvider: this.actorProvider });
|
|
55
|
+
// Initialize wallet with connected wallet if provided
|
|
56
|
+
this.wallet = new wallet_1.IcpayWallet({ connectedWallet: this.connectedWallet });
|
|
57
|
+
// Initialize event center
|
|
58
|
+
this.events = new events_1.IcpayEventCenter();
|
|
59
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'constructor', { connectedWallet: this.connectedWallet });
|
|
60
|
+
// Create public API client (always available)
|
|
61
|
+
this.publicApiClient = axios_1.default.create({
|
|
62
|
+
baseURL: this.config.apiUrl,
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
'Authorization': `Bearer ${this.config.publishableKey || this.config.secretKey}`
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'publicApiClient created', this.publicApiClient);
|
|
69
|
+
// Create private API client (only if secret key is provided)
|
|
70
|
+
if (this.config.secretKey) {
|
|
71
|
+
const privateHeaders = {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
'Authorization': `Bearer ${this.config.secretKey}`
|
|
74
|
+
};
|
|
75
|
+
this.privateApiClient = axios_1.default.create({
|
|
76
|
+
baseURL: this.config.apiUrl,
|
|
77
|
+
headers: privateHeaders
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'privateApiClient created', this.privateApiClient);
|
|
81
|
+
// Initialize protected API
|
|
82
|
+
this.protected = (0, protected_1.createProtectedApi)({
|
|
83
|
+
privateApiClient: this.privateApiClient,
|
|
84
|
+
emitStart: (name, args) => this.emitMethodStart(name, args),
|
|
85
|
+
emitSuccess: (name, result) => this.emitMethodSuccess(name, result),
|
|
86
|
+
emitError: (name, error) => this.emitMethodError(name, error),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// ===== Event API (no Lit required) =====
|
|
90
|
+
on(type, listener) {
|
|
91
|
+
return this.events.on(type, listener);
|
|
92
|
+
}
|
|
93
|
+
off(type, listener) {
|
|
94
|
+
this.events.off(type, listener);
|
|
95
|
+
}
|
|
96
|
+
emit(type, detail) {
|
|
97
|
+
if (this.config.enableEvents) {
|
|
98
|
+
this.events.emit(type, detail);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
addEventListener(type, listener) {
|
|
102
|
+
this.events.addEventListener(type, listener);
|
|
103
|
+
}
|
|
104
|
+
removeEventListener(type, listener) {
|
|
105
|
+
this.events.removeEventListener(type, listener);
|
|
106
|
+
}
|
|
107
|
+
dispatchEvent(event) {
|
|
108
|
+
return this.events.dispatchEvent(event);
|
|
109
|
+
}
|
|
110
|
+
emitError(error) {
|
|
111
|
+
const err = error instanceof errors_1.IcpayError
|
|
112
|
+
? error
|
|
113
|
+
: new errors_1.IcpayError({
|
|
114
|
+
code: errors_1.ICPAY_ERROR_CODES.UNKNOWN_ERROR,
|
|
115
|
+
message: (error && (error.message || error.toString())) || 'Unknown error',
|
|
116
|
+
details: error
|
|
117
|
+
});
|
|
118
|
+
if (this.config.enableEvents) {
|
|
119
|
+
this.events.emit('icpay-sdk-error', err);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
emitMethodStart(name, args) {
|
|
123
|
+
if (this.config.enableEvents) {
|
|
124
|
+
this.events.emit('icpay-sdk-method-start', { name, args });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
emitMethodSuccess(name, result) {
|
|
128
|
+
if (this.config.enableEvents) {
|
|
129
|
+
this.events.emit('icpay-sdk-method-success', { name, result });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
emitMethodError(name, error) {
|
|
133
|
+
if (this.config.enableEvents) {
|
|
134
|
+
this.events.emit('icpay-sdk-method-error', { name, error });
|
|
135
|
+
}
|
|
136
|
+
this.emitError(error);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Check if SDK has secret key for private operations
|
|
140
|
+
*/
|
|
141
|
+
hasSecretKey() {
|
|
142
|
+
return !!this.config.secretKey && !!this.privateApiClient;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Require secret key for private operations
|
|
146
|
+
*/
|
|
147
|
+
requireSecretKey(methodName) {
|
|
148
|
+
if (!this.hasSecretKey()) {
|
|
149
|
+
throw new errors_1.IcpayError({
|
|
150
|
+
code: 'SECRET_KEY_REQUIRED',
|
|
151
|
+
message: `${methodName} requires secret key authentication. Please provide secretKey and accountId in configuration.`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get account information (public method - limited data)
|
|
157
|
+
*/
|
|
158
|
+
async getAccountInfo() {
|
|
159
|
+
this.emitMethodStart('getAccountInfo');
|
|
160
|
+
try {
|
|
161
|
+
const response = await this.publicApiClient.get('/sdk/public/account');
|
|
162
|
+
const account = response.data;
|
|
163
|
+
const result = {
|
|
164
|
+
id: account.id,
|
|
165
|
+
name: account.name,
|
|
166
|
+
isActive: account.isActive,
|
|
167
|
+
isLive: account.isLive,
|
|
168
|
+
accountCanisterId: account.accountCanisterId,
|
|
169
|
+
icpayCanisterId: account.icpayCanisterId,
|
|
170
|
+
};
|
|
171
|
+
this.emitMethodSuccess('getAccountInfo', result);
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const err = new errors_1.IcpayError({
|
|
176
|
+
code: 'ACCOUNT_INFO_FETCH_FAILED',
|
|
177
|
+
message: 'Failed to fetch account information',
|
|
178
|
+
details: error
|
|
179
|
+
});
|
|
180
|
+
this.emitMethodError('getAccountInfo', err);
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get verified ledgers (public method)
|
|
186
|
+
*/
|
|
187
|
+
async getVerifiedLedgers() {
|
|
188
|
+
this.emitMethodStart('getVerifiedLedgers');
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
const cacheAge = 60 * 60 * 1000; // 60 minutes cache
|
|
191
|
+
// Return cached data if it's still fresh
|
|
192
|
+
if (this.verifiedLedgersCache.data && (now - this.verifiedLedgersCache.timestamp) < cacheAge) {
|
|
193
|
+
return this.verifiedLedgersCache.data;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const response = await this.publicApiClient.get('/sdk/public/ledgers/verified');
|
|
197
|
+
const ledgers = response.data.map((ledger) => ({
|
|
198
|
+
id: ledger.id,
|
|
199
|
+
name: ledger.name,
|
|
200
|
+
symbol: ledger.symbol,
|
|
201
|
+
canisterId: ledger.canisterId,
|
|
202
|
+
decimals: ledger.decimals,
|
|
203
|
+
logoUrl: ledger.logoUrl,
|
|
204
|
+
verified: ledger.verified,
|
|
205
|
+
fee: ledger.fee,
|
|
206
|
+
currentPrice: ledger.currentPrice ?? null,
|
|
207
|
+
lastPriceUpdate: ledger.lastPriceUpdate ?? null,
|
|
208
|
+
}));
|
|
209
|
+
// Update cache
|
|
210
|
+
this.verifiedLedgersCache = {
|
|
211
|
+
data: ledgers,
|
|
212
|
+
timestamp: now
|
|
213
|
+
};
|
|
214
|
+
this.emitMethodSuccess('getVerifiedLedgers', { count: ledgers.length });
|
|
215
|
+
return ledgers;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
const err = new errors_1.IcpayError({
|
|
219
|
+
code: 'VERIFIED_LEDGERS_FETCH_FAILED',
|
|
220
|
+
message: 'Failed to fetch verified ledgers',
|
|
221
|
+
details: error
|
|
222
|
+
});
|
|
223
|
+
this.emitMethodError('getVerifiedLedgers', err);
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get a verified ledger's canister ID by its symbol (public helper)
|
|
229
|
+
*/
|
|
230
|
+
async getLedgerCanisterIdBySymbol(symbol) {
|
|
231
|
+
this.emitMethodStart('getLedgerCanisterIdBySymbol', { symbol });
|
|
232
|
+
if (!symbol || typeof symbol !== 'string') {
|
|
233
|
+
throw new errors_1.IcpayError({
|
|
234
|
+
code: 'INVALID_LEDGER_SYMBOL',
|
|
235
|
+
message: 'Symbol must be a non-empty string'
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const ledgers = await this.getVerifiedLedgers();
|
|
239
|
+
const match = ledgers.find(l => l.symbol.toLowerCase() === symbol.toLowerCase());
|
|
240
|
+
if (!match) {
|
|
241
|
+
throw new errors_1.IcpayError({
|
|
242
|
+
code: 'LEDGER_SYMBOL_NOT_FOUND',
|
|
243
|
+
message: `Verified ledger with symbol ${symbol} not found`
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const result = match.canisterId;
|
|
247
|
+
this.emitMethodSuccess('getLedgerCanisterIdBySymbol', { symbol, canisterId: result });
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Trigger transaction sync from canister (public method)
|
|
252
|
+
*
|
|
253
|
+
* This method attempts to sync a transaction directly from the canister to the API database
|
|
254
|
+
* and returns the result immediately. This is useful when you know a transaction exists
|
|
255
|
+
* in the canister but it's not showing up in the API database yet.
|
|
256
|
+
*/
|
|
257
|
+
async triggerTransactionSync(canisterTransactionId) {
|
|
258
|
+
this.emitMethodStart('triggerTransactionSync', { canisterTransactionId });
|
|
259
|
+
try {
|
|
260
|
+
const response = await this.publicApiClient.get(`/sdk/public/transactions/${canisterTransactionId}/sync`);
|
|
261
|
+
this.emitMethodSuccess('triggerTransactionSync', response.data);
|
|
262
|
+
return response.data;
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
const err = new errors_1.IcpayError({
|
|
266
|
+
code: 'TRANSACTION_SYNC_TRIGGER_FAILED',
|
|
267
|
+
message: 'Failed to trigger transaction sync from canister',
|
|
268
|
+
details: error
|
|
269
|
+
});
|
|
270
|
+
this.emitMethodError('triggerTransactionSync', err);
|
|
271
|
+
throw err;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Fetch and cache account info, including icpayCanisterId (public method)
|
|
276
|
+
*/
|
|
277
|
+
async fetchAccountInfo() {
|
|
278
|
+
if (this.accountInfoCache) {
|
|
279
|
+
this.icpayCanisterId = this.accountInfoCache.icpayCanisterId.toString();
|
|
280
|
+
return this.accountInfoCache;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
// Use public endpoint to get account info
|
|
284
|
+
const response = await this.publicApiClient.get('/sdk/public/account');
|
|
285
|
+
this.accountInfoCache = response.data;
|
|
286
|
+
if (response.data && response.data.icpayCanisterId) {
|
|
287
|
+
this.icpayCanisterId = response.data.icpayCanisterId.toString();
|
|
288
|
+
}
|
|
289
|
+
return this.accountInfoCache;
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
throw new errors_1.IcpayError({
|
|
293
|
+
code: 'ACCOUNT_INFO_FETCH_FAILED',
|
|
294
|
+
message: 'Failed to fetch account information',
|
|
295
|
+
details: error
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Show wallet connection modal
|
|
301
|
+
*/
|
|
302
|
+
async showWalletModal() {
|
|
303
|
+
this.emitMethodStart('showWalletModal');
|
|
304
|
+
try {
|
|
305
|
+
const res = await this.wallet.showConnectionModal();
|
|
306
|
+
this.emitMethodSuccess('showWalletModal', res);
|
|
307
|
+
return res;
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
this.emitMethodError('showWalletModal', error);
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Connect to a specific wallet provider
|
|
316
|
+
*/
|
|
317
|
+
async connectWallet(providerId) {
|
|
318
|
+
this.emitMethodStart('connectWallet', { providerId });
|
|
319
|
+
try {
|
|
320
|
+
const res = await this.wallet.connectToProvider(providerId);
|
|
321
|
+
this.emitMethodSuccess('connectWallet', res);
|
|
322
|
+
return res;
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
this.emitMethodError('connectWallet', error);
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get available wallet providers
|
|
331
|
+
*/
|
|
332
|
+
getWalletProviders() {
|
|
333
|
+
this.emitMethodStart('getWalletProviders');
|
|
334
|
+
const res = this.wallet.getProviders();
|
|
335
|
+
this.emitMethodSuccess('getWalletProviders', { count: Array.isArray(res) ? res.length : undefined });
|
|
336
|
+
return res;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Check if a wallet provider is available
|
|
340
|
+
*/
|
|
341
|
+
isWalletProviderAvailable(providerId) {
|
|
342
|
+
this.emitMethodStart('isWalletProviderAvailable', { providerId });
|
|
343
|
+
const res = this.wallet.isProviderAvailable(providerId);
|
|
344
|
+
this.emitMethodSuccess('isWalletProviderAvailable', { providerId, available: res });
|
|
345
|
+
return res;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Get the connected wallet's account address
|
|
349
|
+
*/
|
|
350
|
+
getAccountAddress() {
|
|
351
|
+
this.emitMethodStart('getAccountAddress');
|
|
352
|
+
const res = this.wallet.getAccountAddress();
|
|
353
|
+
this.emitMethodSuccess('getAccountAddress', { accountAddress: res });
|
|
354
|
+
return res;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get balance for a specific ledger canister
|
|
358
|
+
*/
|
|
359
|
+
async getLedgerBalance(ledgerCanisterId) {
|
|
360
|
+
this.emitMethodStart('getLedgerBalance', { ledgerCanisterId });
|
|
361
|
+
try {
|
|
362
|
+
// Extract principal from connected wallet
|
|
363
|
+
let principal = null;
|
|
364
|
+
if (this.connectedWallet) {
|
|
365
|
+
if (this.connectedWallet.owner) {
|
|
366
|
+
principal = this.connectedWallet.owner;
|
|
367
|
+
}
|
|
368
|
+
else if (this.connectedWallet.principal) {
|
|
369
|
+
principal = this.connectedWallet.principal;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (!principal) {
|
|
373
|
+
throw new Error('No principal available for balance check');
|
|
374
|
+
}
|
|
375
|
+
// Convert string principal to Principal object
|
|
376
|
+
const principalObj = principal_1.Principal.fromText(principal);
|
|
377
|
+
// Create anonymous actor for balance queries (no signing required)
|
|
378
|
+
const agent = new agent_1.HttpAgent({ host: this.icHost });
|
|
379
|
+
const actor = agent_1.Actor.createActor(ledger_did_js_1.idlFactory, { agent, canisterId: ledgerCanisterId });
|
|
380
|
+
// Get the balance of the user's account
|
|
381
|
+
const result = await actor.icrc1_balance_of({
|
|
382
|
+
owner: principalObj,
|
|
383
|
+
subaccount: []
|
|
384
|
+
});
|
|
385
|
+
const out = BigInt(result);
|
|
386
|
+
this.emitMethodSuccess('getLedgerBalance', { ledgerCanisterId, balance: out.toString() });
|
|
387
|
+
return out;
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
this.emitMethodError('getLedgerBalance', error);
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Create a simple memo with account canister ID as bytes
|
|
396
|
+
* Example: 1 => Uint8Array([1]), 2 => Uint8Array([2])
|
|
397
|
+
*/
|
|
398
|
+
createMemoWithAccountCanisterId(accountCanisterId) {
|
|
399
|
+
// Convert number to bytes (simple approach)
|
|
400
|
+
const bytes = [];
|
|
401
|
+
let num = accountCanisterId;
|
|
402
|
+
// Handle 0 case
|
|
403
|
+
if (num === 0) {
|
|
404
|
+
return new Uint8Array([0]);
|
|
405
|
+
}
|
|
406
|
+
// Convert to bytes (little-endian)
|
|
407
|
+
while (num > 0) {
|
|
408
|
+
bytes.push(num & 0xff);
|
|
409
|
+
num = Math.floor(num / 256);
|
|
410
|
+
}
|
|
411
|
+
return new Uint8Array(bytes);
|
|
412
|
+
}
|
|
413
|
+
createPackedMemo(accountCanisterId, intentCode) {
|
|
414
|
+
let memo = (BigInt(accountCanisterId >>> 0) << BigInt(32)) | BigInt(intentCode >>> 0);
|
|
415
|
+
if (memo === BigInt(0))
|
|
416
|
+
return new Uint8Array([0]);
|
|
417
|
+
const out = [];
|
|
418
|
+
while (memo > BigInt(0)) {
|
|
419
|
+
out.push(Number(memo & BigInt(0xff)));
|
|
420
|
+
memo >>= BigInt(8);
|
|
421
|
+
}
|
|
422
|
+
return new Uint8Array(out);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Send funds to a specific canister/ledger (public method)
|
|
426
|
+
* This is now a real transaction
|
|
427
|
+
*/
|
|
428
|
+
async sendFunds(request) {
|
|
429
|
+
this.emitMethodStart('sendFunds', { request: { ...request, amount: typeof request.amount === 'string' ? request.amount : String(request.amount) } });
|
|
430
|
+
try {
|
|
431
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'sendFunds start', { request });
|
|
432
|
+
// Fetch account info to get accountCanisterId if not provided
|
|
433
|
+
let accountCanisterId = request.accountCanisterId;
|
|
434
|
+
if (!accountCanisterId) {
|
|
435
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'fetching account info for accountCanisterId');
|
|
436
|
+
const accountInfo = await this.getAccountInfo();
|
|
437
|
+
accountCanisterId = accountInfo.accountCanisterId.toString();
|
|
438
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'accountCanisterId resolved', { accountCanisterId });
|
|
439
|
+
}
|
|
440
|
+
// Always use icpayCanisterId as toPrincipal
|
|
441
|
+
if (!this.icpayCanisterId) {
|
|
442
|
+
await this.fetchAccountInfo();
|
|
443
|
+
}
|
|
444
|
+
const ledgerCanisterId = request.ledgerCanisterId;
|
|
445
|
+
let toPrincipal = this.icpayCanisterId;
|
|
446
|
+
const amount = typeof request.amount === 'string' ? BigInt(request.amount) : BigInt(request.amount);
|
|
447
|
+
const host = this.icHost;
|
|
448
|
+
let memo = undefined;
|
|
449
|
+
// Check balance before sending
|
|
450
|
+
const requiredAmount = amount;
|
|
451
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'checking balance', { ledgerCanisterId, requiredAmount: requiredAmount.toString() });
|
|
452
|
+
// Helper function to make amounts human-readable
|
|
453
|
+
const formatAmount = (amount, decimals = 8, symbol = '') => {
|
|
454
|
+
const divisor = BigInt(10 ** decimals);
|
|
455
|
+
const whole = amount / divisor;
|
|
456
|
+
const fraction = amount % divisor;
|
|
457
|
+
const fractionStr = fraction.toString().padStart(decimals, '0').replace(/0+$/, '');
|
|
458
|
+
return `${whole}${fractionStr ? '.' + fractionStr : ''} ${symbol}`.trim();
|
|
459
|
+
};
|
|
460
|
+
// Check if user has sufficient balance based on ledger type
|
|
461
|
+
try {
|
|
462
|
+
// Get the actual balance from the specific ledger (works for all ICRC ledgers including ICP)
|
|
463
|
+
const actualBalance = await this.getLedgerBalance(ledgerCanisterId);
|
|
464
|
+
if (actualBalance < requiredAmount) {
|
|
465
|
+
const requiredFormatted = formatAmount(requiredAmount, 8, 'tokens');
|
|
466
|
+
const availableFormatted = formatAmount(actualBalance, 8, 'tokens');
|
|
467
|
+
throw (0, errors_1.createBalanceError)(requiredFormatted, availableFormatted, {
|
|
468
|
+
required: requiredAmount,
|
|
469
|
+
available: actualBalance,
|
|
470
|
+
ledgerCanisterId
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'balance ok', { actualBalance: actualBalance.toString() });
|
|
474
|
+
}
|
|
475
|
+
catch (balanceError) {
|
|
476
|
+
// If we can't fetch the specific ledger balance, fall back to the old logic
|
|
477
|
+
throw new errors_1.IcpayError({
|
|
478
|
+
code: 'INSUFFICIENT_BALANCE',
|
|
479
|
+
message: 'Insufficient balance',
|
|
480
|
+
details: { required: requiredAmount, available: 0 }
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
// 1) Create payment intent via API
|
|
484
|
+
let paymentIntentId = null;
|
|
485
|
+
let paymentIntentCode = null;
|
|
486
|
+
try {
|
|
487
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'creating payment intent');
|
|
488
|
+
// Get the expected sender principal from connected wallet
|
|
489
|
+
const expectedSenderPrincipal = this.connectedWallet?.owner || this.connectedWallet?.principal?.toString();
|
|
490
|
+
if (!expectedSenderPrincipal) {
|
|
491
|
+
throw new errors_1.IcpayError({
|
|
492
|
+
code: errors_1.ICPAY_ERROR_CODES.WALLET_NOT_CONNECTED,
|
|
493
|
+
message: 'Wallet must be connected to create payment intent',
|
|
494
|
+
details: { connectedWallet: this.connectedWallet },
|
|
495
|
+
retryable: false,
|
|
496
|
+
userAction: 'Connect your wallet first'
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
const intentResp = await this.publicApiClient.post('/sdk/public/payments/intents', {
|
|
500
|
+
amount: request.amount,
|
|
501
|
+
ledgerCanisterId,
|
|
502
|
+
expectedSenderPrincipal,
|
|
503
|
+
metadata: request.metadata || {},
|
|
504
|
+
});
|
|
505
|
+
paymentIntentId = intentResp.data?.paymentIntent?.id || null;
|
|
506
|
+
paymentIntentCode = intentResp.data?.paymentIntent?.intentCode ?? null;
|
|
507
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'payment intent created', { paymentIntentId, paymentIntentCode, expectedSenderPrincipal });
|
|
508
|
+
// Emit transaction created event
|
|
509
|
+
if (paymentIntentId) {
|
|
510
|
+
this.emit('icpay-sdk-transaction-created', {
|
|
511
|
+
paymentIntentId,
|
|
512
|
+
amount: request.amount,
|
|
513
|
+
ledgerCanisterId,
|
|
514
|
+
expectedSenderPrincipal
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (e) {
|
|
519
|
+
// Do not proceed without a payment intent
|
|
520
|
+
// Throw a standardized error so integrators can handle it consistently
|
|
521
|
+
const err = new errors_1.IcpayError({
|
|
522
|
+
code: errors_1.ICPAY_ERROR_CODES.API_ERROR,
|
|
523
|
+
message: 'Failed to create payment intent. Please try again.',
|
|
524
|
+
details: e,
|
|
525
|
+
retryable: true,
|
|
526
|
+
userAction: 'Try again'
|
|
527
|
+
});
|
|
528
|
+
this.emitError(err);
|
|
529
|
+
throw err;
|
|
530
|
+
}
|
|
531
|
+
// Build packed memo if possible
|
|
532
|
+
try {
|
|
533
|
+
const acctIdNum = parseInt(accountCanisterId);
|
|
534
|
+
if (!isNaN(acctIdNum) && paymentIntentCode != null) {
|
|
535
|
+
memo = this.createPackedMemo(acctIdNum, Number(paymentIntentCode));
|
|
536
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'built packed memo', { accountCanisterId: acctIdNum, paymentIntentCode });
|
|
537
|
+
}
|
|
538
|
+
else if (!isNaN(acctIdNum)) {
|
|
539
|
+
memo = this.createMemoWithAccountCanisterId(acctIdNum);
|
|
540
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'built legacy memo', { accountCanisterId: acctIdNum });
|
|
541
|
+
}
|
|
542
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'memo', { memo });
|
|
543
|
+
}
|
|
544
|
+
catch { }
|
|
545
|
+
let transferResult;
|
|
546
|
+
if (ledgerCanisterId === 'ryjl3-tyaaa-aaaaa-aaaba-cai') {
|
|
547
|
+
// ICP Ledger: use ICRC-1 transfer (ICP ledger supports ICRC-1)
|
|
548
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'sending ICRC-1 transfer (ICP)');
|
|
549
|
+
transferResult = await this.sendFundsToLedger(ledgerCanisterId, toPrincipal, amount, memo, host);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
// ICRC-1 ledgers: use principal directly
|
|
553
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'sending ICRC-1 transfer');
|
|
554
|
+
transferResult = await this.sendFundsToLedger(ledgerCanisterId, toPrincipal, amount, memo, host);
|
|
555
|
+
}
|
|
556
|
+
// Assume transferResult returns a block index or transaction id
|
|
557
|
+
const blockIndex = transferResult?.Ok?.toString() || transferResult?.blockIndex?.toString() || `temp-${Date.now()}`;
|
|
558
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'transfer result', { blockIndex });
|
|
559
|
+
// First, notify the canister about the ledger transaction
|
|
560
|
+
let canisterTransactionId;
|
|
561
|
+
let notifyStatus = null;
|
|
562
|
+
try {
|
|
563
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'notifying canister about ledger tx');
|
|
564
|
+
const notifyRes = await this.notifyLedgerTransaction(this.icpayCanisterId, ledgerCanisterId, BigInt(blockIndex));
|
|
565
|
+
// notify returns { id, status, amount }
|
|
566
|
+
if (typeof notifyRes === 'string') {
|
|
567
|
+
canisterTransactionId = parseInt(notifyRes, 10);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
canisterTransactionId = parseInt(notifyRes.id, 10);
|
|
571
|
+
notifyStatus = notifyRes;
|
|
572
|
+
}
|
|
573
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'canister notified', { canisterTransactionId });
|
|
574
|
+
}
|
|
575
|
+
catch (notifyError) {
|
|
576
|
+
canisterTransactionId = parseInt(blockIndex, 10);
|
|
577
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'notify failed, using blockIndex as tx id', { canisterTransactionId });
|
|
578
|
+
}
|
|
579
|
+
// Poll for transaction status until completed
|
|
580
|
+
// Use the transaction ID returned by the notification, not the block index
|
|
581
|
+
let status = null;
|
|
582
|
+
if (notifyStatus && notifyStatus.status) {
|
|
583
|
+
status = { status: notifyStatus.status };
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
try {
|
|
587
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'polling transaction status (public)', { canisterTransactionId });
|
|
588
|
+
status = await this.pollTransactionStatus(this.icpayCanisterId, canisterTransactionId, accountCanisterId, Number(blockIndex), 2000, 30);
|
|
589
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'poll done', { status });
|
|
590
|
+
}
|
|
591
|
+
catch (e) {
|
|
592
|
+
status = { status: 'pending' };
|
|
593
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'poll failed, falling back to pending');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// Extract the status string from the transaction object
|
|
597
|
+
let statusString = 'pending';
|
|
598
|
+
if (status) {
|
|
599
|
+
if (typeof status === 'object' && status.status) {
|
|
600
|
+
// Handle variant status like {Completed: null}
|
|
601
|
+
if (typeof status.status === 'object') {
|
|
602
|
+
const statusKeys = Object.keys(status.status);
|
|
603
|
+
if (statusKeys.length > 0) {
|
|
604
|
+
const rawStatus = statusKeys[0].toLowerCase();
|
|
605
|
+
if (rawStatus === 'completed' || rawStatus === 'failed') {
|
|
606
|
+
statusString = rawStatus;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
const rawStatus = status.status;
|
|
612
|
+
if (rawStatus === 'completed' || rawStatus === 'failed') {
|
|
613
|
+
statusString = rawStatus;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// 5) Notify API about completion with intent and transaction id
|
|
619
|
+
// Optionally await based on config.awaitServerNotification
|
|
620
|
+
let publicNotify = undefined;
|
|
621
|
+
const notifyApi = async () => {
|
|
622
|
+
const notifyClient = this.publicApiClient;
|
|
623
|
+
const notifyPath = '/sdk/public/payments/notify';
|
|
624
|
+
const maxNotifyAttempts = 5;
|
|
625
|
+
const notifyDelayMs = 1000;
|
|
626
|
+
for (let attempt = 1; attempt <= maxNotifyAttempts; attempt++) {
|
|
627
|
+
try {
|
|
628
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'notifying API about completion', { attempt, notifyPath, paymentIntentId, canisterTransactionId });
|
|
629
|
+
const resp = await notifyClient.post(notifyPath, {
|
|
630
|
+
paymentIntentId,
|
|
631
|
+
canisterTxId: canisterTransactionId,
|
|
632
|
+
});
|
|
633
|
+
return resp.data;
|
|
634
|
+
}
|
|
635
|
+
catch (e) {
|
|
636
|
+
const status = e?.response?.status;
|
|
637
|
+
const data = e?.response?.data;
|
|
638
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'API notify attempt failed', { attempt, status, data });
|
|
639
|
+
// Proactively trigger a transaction sync if we get not found
|
|
640
|
+
try {
|
|
641
|
+
await this.triggerTransactionSync(canisterTransactionId);
|
|
642
|
+
}
|
|
643
|
+
catch { }
|
|
644
|
+
if (attempt < maxNotifyAttempts) {
|
|
645
|
+
await new Promise(r => setTimeout(r, notifyDelayMs));
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'API notify failed after retries (non-fatal)');
|
|
650
|
+
return undefined;
|
|
651
|
+
};
|
|
652
|
+
if (this.config.awaitServerNotification) {
|
|
653
|
+
publicNotify = await notifyApi();
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
// fire-and-forget
|
|
657
|
+
notifyApi()
|
|
658
|
+
.then((data) => {
|
|
659
|
+
// Optionally emit a success for the async notify step
|
|
660
|
+
this.emitMethodSuccess('sendFunds.notifyApi', { paymentIntentId, canisterTransactionId, data });
|
|
661
|
+
})
|
|
662
|
+
.catch((err) => {
|
|
663
|
+
this.emitMethodError('sendFunds.notifyApi', err);
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
const response = {
|
|
667
|
+
transactionId: canisterTransactionId,
|
|
668
|
+
status: statusString,
|
|
669
|
+
amount: amount.toString(),
|
|
670
|
+
recipientCanister: ledgerCanisterId,
|
|
671
|
+
timestamp: new Date(),
|
|
672
|
+
description: 'Fund transfer',
|
|
673
|
+
metadata: request.metadata,
|
|
674
|
+
payment: publicNotify
|
|
675
|
+
};
|
|
676
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'sendFunds done', response);
|
|
677
|
+
if (statusString === 'completed') {
|
|
678
|
+
this.emit('icpay-sdk-transaction-completed', response);
|
|
679
|
+
}
|
|
680
|
+
else if (statusString === 'failed') {
|
|
681
|
+
this.emit('icpay-sdk-transaction-failed', response);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
this.emit('icpay-sdk-transaction-updated', response);
|
|
685
|
+
}
|
|
686
|
+
this.emitMethodSuccess('sendFunds', response);
|
|
687
|
+
return response;
|
|
688
|
+
}
|
|
689
|
+
catch (error) {
|
|
690
|
+
if (error instanceof errors_1.IcpayError) {
|
|
691
|
+
this.emitMethodError('sendFunds', error);
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
const err = new errors_1.IcpayError({
|
|
695
|
+
code: 'TRANSACTION_FAILED',
|
|
696
|
+
message: 'Failed to send funds',
|
|
697
|
+
details: error
|
|
698
|
+
});
|
|
699
|
+
this.emitMethodError('sendFunds', err);
|
|
700
|
+
throw err;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Disconnect from wallet
|
|
705
|
+
*/
|
|
706
|
+
async disconnectWallet() {
|
|
707
|
+
return await this.wallet.disconnect();
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Check if wallet is connected
|
|
711
|
+
*/
|
|
712
|
+
isWalletConnected() {
|
|
713
|
+
return this.wallet.isConnected();
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Get the connected wallet provider
|
|
717
|
+
*/
|
|
718
|
+
getConnectedWalletProvider() {
|
|
719
|
+
return this.wallet.getConnectedProvider();
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Poll for transaction status using anonymous actor (no signature required)
|
|
723
|
+
*/
|
|
724
|
+
async pollTransactionStatus(canisterId, transactionId, accountCanisterId, indexReceived, intervalMs = 2000, maxAttempts = 30) {
|
|
725
|
+
this.emitMethodStart('pollTransactionStatus', { canisterId, transactionId, accountCanisterId, indexReceived, intervalMs, maxAttempts });
|
|
726
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
727
|
+
try {
|
|
728
|
+
// Use public-only method
|
|
729
|
+
let status = await this.getTransactionStatusPublic(canisterId, transactionId, indexReceived, accountCanisterId);
|
|
730
|
+
// If we get an array (unexpected), try the alternative method
|
|
731
|
+
if (Array.isArray(status) && status.length > 0) {
|
|
732
|
+
const transaction = status[0];
|
|
733
|
+
if (transaction && typeof transaction === 'object') {
|
|
734
|
+
// Check if we have a valid status
|
|
735
|
+
if (transaction.status) {
|
|
736
|
+
// Check if transaction is completed
|
|
737
|
+
const transactionStatus = transaction.status;
|
|
738
|
+
if (this.isTransactionCompleted(transactionStatus)) {
|
|
739
|
+
return transaction; // Return immediately when completed
|
|
740
|
+
}
|
|
741
|
+
// If not completed, continue polling
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// If we get null or no valid result, try the alternative method
|
|
746
|
+
// No secondary fallback to controller-only methods
|
|
747
|
+
if (status && status.status) {
|
|
748
|
+
if (this.isTransactionCompleted(status.status)) {
|
|
749
|
+
this.emitMethodSuccess('pollTransactionStatus', { attempt, status });
|
|
750
|
+
return status; // Return immediately when completed
|
|
751
|
+
}
|
|
752
|
+
// If not completed, continue polling
|
|
753
|
+
}
|
|
754
|
+
// Check if status is an object with Ok/Err pattern
|
|
755
|
+
if (status && typeof status === 'object' && (status.Ok || status.Err)) {
|
|
756
|
+
this.emitMethodSuccess('pollTransactionStatus', { attempt, status });
|
|
757
|
+
return status; // Return immediately when we find a valid status
|
|
758
|
+
}
|
|
759
|
+
// Wait before next attempt (unless this is the last attempt)
|
|
760
|
+
if (attempt < maxAttempts - 1) {
|
|
761
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
if (attempt === maxAttempts - 1) {
|
|
766
|
+
this.emitMethodError('pollTransactionStatus', error);
|
|
767
|
+
}
|
|
768
|
+
// Wait before next attempt
|
|
769
|
+
if (attempt < maxAttempts - 1) {
|
|
770
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
const err = new Error('Transaction status polling timed out');
|
|
775
|
+
this.emitMethodError('pollTransactionStatus', err);
|
|
776
|
+
throw err;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Check if transaction status indicates completion
|
|
780
|
+
*/
|
|
781
|
+
isTransactionCompleted(status) {
|
|
782
|
+
if (!status)
|
|
783
|
+
return false;
|
|
784
|
+
// Handle variant status like {Completed: null}
|
|
785
|
+
if (typeof status === 'object') {
|
|
786
|
+
const statusKeys = Object.keys(status);
|
|
787
|
+
if (statusKeys.length > 0) {
|
|
788
|
+
const rawStatus = statusKeys[0].toLowerCase();
|
|
789
|
+
return rawStatus === 'completed';
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
// Handle string status
|
|
793
|
+
if (typeof status === 'string') {
|
|
794
|
+
return status.toLowerCase() === 'completed';
|
|
795
|
+
}
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Notify canister about ledger transaction using anonymous actor (no signature required)
|
|
800
|
+
*/
|
|
801
|
+
async notifyLedgerTransaction(canisterId, ledgerCanisterId, blockIndex) {
|
|
802
|
+
this.emitMethodStart('notifyLedgerTransaction', { canisterId, ledgerCanisterId, blockIndex: blockIndex.toString() });
|
|
803
|
+
// Create anonymous actor for canister notifications (no signature required)
|
|
804
|
+
const agent = new agent_1.HttpAgent({ host: this.icHost });
|
|
805
|
+
const actor = agent_1.Actor.createActor(icpay_canister_backend_did_js_1.idlFactory, { agent, canisterId });
|
|
806
|
+
const result = await actor.notify_ledger_transaction({
|
|
807
|
+
ledger_canister_id: ledgerCanisterId,
|
|
808
|
+
block_index: blockIndex
|
|
809
|
+
});
|
|
810
|
+
if (result && result.Ok) {
|
|
811
|
+
this.emitMethodSuccess('notifyLedgerTransaction', { result: result.Ok });
|
|
812
|
+
return result.Ok;
|
|
813
|
+
}
|
|
814
|
+
else if (result && result.Err) {
|
|
815
|
+
const err = new Error(result.Err);
|
|
816
|
+
this.emitMethodError('notifyLedgerTransaction', err);
|
|
817
|
+
throw err;
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
const err = new Error('Unexpected canister notify result');
|
|
821
|
+
this.emitMethodError('notifyLedgerTransaction', err);
|
|
822
|
+
throw err;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
async getTransactionStatusPublic(canisterId, canisterTransactionId, indexReceived, accountCanisterId) {
|
|
826
|
+
this.emitMethodStart('getTransactionStatusPublic', { canisterId, canisterTransactionId, indexReceived, accountCanisterId });
|
|
827
|
+
const agent = new agent_1.HttpAgent({ host: this.icHost });
|
|
828
|
+
const actor = agent_1.Actor.createActor(icpay_canister_backend_did_js_1.idlFactory, { agent, canisterId });
|
|
829
|
+
const acctIdNum = parseInt(accountCanisterId);
|
|
830
|
+
const res = await actor.get_transaction_status_public(acctIdNum, BigInt(canisterTransactionId), [indexReceived]);
|
|
831
|
+
const result = res || { status: 'pending' };
|
|
832
|
+
this.emitMethodSuccess('getTransactionStatusPublic', result);
|
|
833
|
+
return result;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Send funds to a ledger canister using agent-js
|
|
837
|
+
* Now uses host from config
|
|
838
|
+
*/
|
|
839
|
+
async sendFundsToLedger(ledgerCanisterId, toPrincipal, amount, memo, host) {
|
|
840
|
+
this.emitMethodStart('sendFundsToLedger', { ledgerCanisterId, toPrincipal, amount: amount.toString(), hasMemo: !!memo });
|
|
841
|
+
let actor;
|
|
842
|
+
if (this.actorProvider) {
|
|
843
|
+
actor = this.actorProvider(ledgerCanisterId, ledger_did_js_1.idlFactory);
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
const err = new Error('actorProvider is required for sending funds');
|
|
847
|
+
this.emitMethodError('sendFundsToLedger', err);
|
|
848
|
+
throw err;
|
|
849
|
+
}
|
|
850
|
+
// ICRC-1 transfer
|
|
851
|
+
const res = await actor.icrc1_transfer({
|
|
852
|
+
to: { owner: principal_1.Principal.fromText(toPrincipal), subaccount: [] },
|
|
853
|
+
amount,
|
|
854
|
+
fee: [], // Always include fee, even if empty
|
|
855
|
+
memo: memo ? [memo] : [],
|
|
856
|
+
from_subaccount: [],
|
|
857
|
+
created_at_time: [],
|
|
858
|
+
});
|
|
859
|
+
this.emitMethodSuccess('sendFundsToLedger', res);
|
|
860
|
+
return res;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Get transaction by ID using get_transactions filter (alternative to get_transaction)
|
|
864
|
+
*/
|
|
865
|
+
async getTransactionByFilter(transactionId) {
|
|
866
|
+
this.emitMethodStart('getTransactionByFilter', { transactionId });
|
|
867
|
+
try {
|
|
868
|
+
if (!this.icpayCanisterId) {
|
|
869
|
+
await this.fetchAccountInfo();
|
|
870
|
+
}
|
|
871
|
+
// Create anonymous actor for canister queries
|
|
872
|
+
const agent = new agent_1.HttpAgent({ host: this.icHost });
|
|
873
|
+
const actor = agent_1.Actor.createActor(icpay_canister_backend_did_js_1.idlFactory, { agent, canisterId: this.icpayCanisterId });
|
|
874
|
+
// Convert string transaction ID to Nat
|
|
875
|
+
const transactionIdNat = BigInt(transactionId);
|
|
876
|
+
// Get all transactions and filter by ID
|
|
877
|
+
const result = await actor.get_transactions({
|
|
878
|
+
account_canister_id: [], // Use empty array instead of null
|
|
879
|
+
ledger_canister_id: [], // Use empty array instead of null
|
|
880
|
+
from_timestamp: [], // Use empty array instead of null
|
|
881
|
+
to_timestamp: [], // Use empty array instead of null
|
|
882
|
+
from_id: [], // Use empty array instead of null
|
|
883
|
+
status: [], // Use empty array instead of null
|
|
884
|
+
limit: [], // Use empty array instead of 100 for optional nat32
|
|
885
|
+
offset: [] // Use empty array instead of 0 for optional nat32
|
|
886
|
+
});
|
|
887
|
+
if (result && result.transactions) {
|
|
888
|
+
const transaction = result.transactions.find((tx) => tx.id.toString() === transactionId.toString());
|
|
889
|
+
this.emitMethodSuccess('getTransactionByFilter', { found: !!transaction });
|
|
890
|
+
return transaction;
|
|
891
|
+
}
|
|
892
|
+
this.emitMethodSuccess('getTransactionByFilter', { found: false });
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
catch (error) {
|
|
896
|
+
this.emitMethodError('getTransactionByFilter', error);
|
|
897
|
+
throw error;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
// ===== NEW ENHANCED SDK FUNCTIONS =====
|
|
901
|
+
/**
|
|
902
|
+
* Get balance for all verified ledgers for the connected wallet (public method)
|
|
903
|
+
*/
|
|
904
|
+
async getAllLedgerBalances() {
|
|
905
|
+
this.emitMethodStart('getAllLedgerBalances');
|
|
906
|
+
try {
|
|
907
|
+
if (!this.isWalletConnected()) {
|
|
908
|
+
throw new errors_1.IcpayError({
|
|
909
|
+
code: 'WALLET_NOT_CONNECTED',
|
|
910
|
+
message: 'Wallet must be connected to fetch balances'
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
const verifiedLedgers = await this.getVerifiedLedgers();
|
|
914
|
+
const balances = [];
|
|
915
|
+
let totalBalancesUSD = 0;
|
|
916
|
+
for (const ledger of verifiedLedgers) {
|
|
917
|
+
try {
|
|
918
|
+
const rawBalance = await this.getLedgerBalance(ledger.canisterId);
|
|
919
|
+
const formattedBalance = this.formatBalance(rawBalance.toString(), ledger.decimals);
|
|
920
|
+
const balance = {
|
|
921
|
+
ledgerId: ledger.id,
|
|
922
|
+
ledgerName: ledger.name,
|
|
923
|
+
ledgerSymbol: ledger.symbol,
|
|
924
|
+
canisterId: ledger.canisterId,
|
|
925
|
+
balance: rawBalance.toString(),
|
|
926
|
+
formattedBalance,
|
|
927
|
+
decimals: ledger.decimals,
|
|
928
|
+
currentPrice: ledger.currentPrice || undefined,
|
|
929
|
+
lastPriceUpdate: ledger.lastPriceUpdate ? new Date(ledger.lastPriceUpdate) : undefined,
|
|
930
|
+
lastUpdated: new Date()
|
|
931
|
+
};
|
|
932
|
+
balances.push(balance);
|
|
933
|
+
// Calculate USD value if price is available
|
|
934
|
+
if (ledger.currentPrice && rawBalance > 0) {
|
|
935
|
+
const humanReadableBalance = parseFloat(formattedBalance);
|
|
936
|
+
totalBalancesUSD += humanReadableBalance * ledger.currentPrice;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
catch (error) {
|
|
940
|
+
this.emit('icpay-sdk-method-error', {
|
|
941
|
+
name: 'getAllLedgerBalances.getLedgerBalance',
|
|
942
|
+
error,
|
|
943
|
+
ledgerSymbol: ledger.symbol,
|
|
944
|
+
ledgerCanisterId: ledger.canisterId
|
|
945
|
+
});
|
|
946
|
+
// Continue with other ledgers even if one fails
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
const result = {
|
|
950
|
+
balances,
|
|
951
|
+
totalBalancesUSD: totalBalancesUSD > 0 ? totalBalancesUSD : undefined,
|
|
952
|
+
lastUpdated: new Date()
|
|
953
|
+
};
|
|
954
|
+
this.emitMethodSuccess('getAllLedgerBalances', { count: balances.length, totalUSD: result.totalBalancesUSD });
|
|
955
|
+
return result;
|
|
956
|
+
}
|
|
957
|
+
catch (error) {
|
|
958
|
+
const err = new errors_1.IcpayError({
|
|
959
|
+
code: 'BALANCES_FETCH_FAILED',
|
|
960
|
+
message: 'Failed to fetch all ledger balances',
|
|
961
|
+
details: error
|
|
962
|
+
});
|
|
963
|
+
this.emitMethodError('getAllLedgerBalances', err);
|
|
964
|
+
throw err;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Get balance for a specific ledger by canister ID (public method)
|
|
969
|
+
*/
|
|
970
|
+
async getSingleLedgerBalance(ledgerCanisterId) {
|
|
971
|
+
this.emitMethodStart('getSingleLedgerBalance', { ledgerCanisterId });
|
|
972
|
+
try {
|
|
973
|
+
if (!this.isWalletConnected()) {
|
|
974
|
+
throw new errors_1.IcpayError({
|
|
975
|
+
code: 'WALLET_NOT_CONNECTED',
|
|
976
|
+
message: 'Wallet must be connected to fetch balance'
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
// Get ledger info to include price data
|
|
980
|
+
const verifiedLedgers = await this.getVerifiedLedgers();
|
|
981
|
+
const ledger = verifiedLedgers.find(l => l.canisterId === ledgerCanisterId);
|
|
982
|
+
if (!ledger) {
|
|
983
|
+
throw new errors_1.IcpayError({
|
|
984
|
+
code: 'LEDGER_NOT_FOUND',
|
|
985
|
+
message: `Ledger with canister ID ${ledgerCanisterId} not found or not verified`
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
const rawBalance = await this.getLedgerBalance(ledgerCanisterId);
|
|
989
|
+
const formattedBalance = this.formatBalance(rawBalance.toString(), ledger.decimals);
|
|
990
|
+
const result = {
|
|
991
|
+
ledgerId: ledger.id,
|
|
992
|
+
ledgerName: ledger.name,
|
|
993
|
+
ledgerSymbol: ledger.symbol,
|
|
994
|
+
canisterId: ledger.canisterId,
|
|
995
|
+
balance: rawBalance.toString(),
|
|
996
|
+
formattedBalance,
|
|
997
|
+
decimals: ledger.decimals,
|
|
998
|
+
currentPrice: ledger.currentPrice || undefined,
|
|
999
|
+
lastPriceUpdate: ledger.lastPriceUpdate ? new Date(ledger.lastPriceUpdate) : undefined,
|
|
1000
|
+
lastUpdated: new Date()
|
|
1001
|
+
};
|
|
1002
|
+
this.emitMethodSuccess('getSingleLedgerBalance', { ledgerCanisterId, balance: result.balance });
|
|
1003
|
+
return result;
|
|
1004
|
+
}
|
|
1005
|
+
catch (error) {
|
|
1006
|
+
const err = new errors_1.IcpayError({
|
|
1007
|
+
code: 'SINGLE_BALANCE_FETCH_FAILED',
|
|
1008
|
+
message: `Failed to fetch balance for ledger ${ledgerCanisterId}`,
|
|
1009
|
+
details: error
|
|
1010
|
+
});
|
|
1011
|
+
this.emitMethodError('getSingleLedgerBalance', err);
|
|
1012
|
+
throw err;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Calculate token amount from USD price for a specific ledger (public method)
|
|
1017
|
+
*/
|
|
1018
|
+
async calculateTokenAmountFromUSD(request) {
|
|
1019
|
+
this.emitMethodStart('calculateTokenAmountFromUSD', { usdAmount: request.usdAmount, ledgerCanisterId: request.ledgerCanisterId, ledgerSymbol: request.ledgerSymbol });
|
|
1020
|
+
try {
|
|
1021
|
+
const { usdAmount, ledgerCanisterId, ledgerSymbol } = request;
|
|
1022
|
+
if (usdAmount <= 0) {
|
|
1023
|
+
throw new errors_1.IcpayError({
|
|
1024
|
+
code: 'INVALID_USD_AMOUNT',
|
|
1025
|
+
message: 'USD amount must be greater than 0'
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
// Get ledger info
|
|
1029
|
+
const verifiedLedgers = await this.getVerifiedLedgers();
|
|
1030
|
+
const ledger = verifiedLedgers.find(l => l.canisterId === ledgerCanisterId ||
|
|
1031
|
+
(ledgerSymbol && l.symbol === ledgerSymbol));
|
|
1032
|
+
if (!ledger) {
|
|
1033
|
+
throw new errors_1.IcpayError({
|
|
1034
|
+
code: 'LEDGER_NOT_FOUND',
|
|
1035
|
+
message: `Ledger not found for canister ID ${ledgerCanisterId} or symbol ${ledgerSymbol}`
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
if (!ledger.currentPrice || ledger.currentPrice <= 0) {
|
|
1039
|
+
throw new errors_1.IcpayError({
|
|
1040
|
+
code: 'PRICE_NOT_AVAILABLE',
|
|
1041
|
+
message: `Current price not available for ledger ${ledger.symbol}`
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
// Calculate token amount
|
|
1045
|
+
const tokenAmountHuman = usdAmount / ledger.currentPrice;
|
|
1046
|
+
// Convert to smallest unit and truncate decimals to get whole number for blockchain
|
|
1047
|
+
const tokenAmountDecimals = Math.floor(tokenAmountHuman * Math.pow(10, ledger.decimals)).toString();
|
|
1048
|
+
const result = {
|
|
1049
|
+
usdAmount,
|
|
1050
|
+
ledgerCanisterId: ledger.canisterId,
|
|
1051
|
+
ledgerSymbol: ledger.symbol,
|
|
1052
|
+
ledgerName: ledger.name,
|
|
1053
|
+
currentPrice: ledger.currentPrice,
|
|
1054
|
+
priceTimestamp: ledger.lastPriceUpdate ? new Date(ledger.lastPriceUpdate) : new Date(),
|
|
1055
|
+
tokenAmountHuman: tokenAmountHuman.toFixed(ledger.decimals),
|
|
1056
|
+
tokenAmountDecimals,
|
|
1057
|
+
decimals: ledger.decimals
|
|
1058
|
+
};
|
|
1059
|
+
this.emitMethodSuccess('calculateTokenAmountFromUSD', { ledgerCanisterId: result.ledgerCanisterId, tokenAmountDecimals });
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
const err = new errors_1.IcpayError({
|
|
1064
|
+
code: 'PRICE_CALCULATION_FAILED',
|
|
1065
|
+
message: 'Failed to calculate token amount from USD',
|
|
1066
|
+
details: error
|
|
1067
|
+
});
|
|
1068
|
+
this.emitMethodError('calculateTokenAmountFromUSD', err);
|
|
1069
|
+
throw err;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Get detailed ledger information including price data (public method)
|
|
1074
|
+
*/
|
|
1075
|
+
async getLedgerInfo(ledgerCanisterId) {
|
|
1076
|
+
this.emitMethodStart('getLedgerInfo', { ledgerCanisterId });
|
|
1077
|
+
try {
|
|
1078
|
+
const response = await this.publicApiClient.get(`/sdk/public/ledgers/${ledgerCanisterId}`);
|
|
1079
|
+
const ledger = response.data;
|
|
1080
|
+
const result = {
|
|
1081
|
+
id: ledger.id,
|
|
1082
|
+
name: ledger.name,
|
|
1083
|
+
symbol: ledger.symbol,
|
|
1084
|
+
canisterId: ledger.canisterId,
|
|
1085
|
+
standard: ledger.standard,
|
|
1086
|
+
decimals: ledger.decimals,
|
|
1087
|
+
logoUrl: ledger.logoUrl ?? null,
|
|
1088
|
+
verified: ledger.verified,
|
|
1089
|
+
fee: ledger.fee ?? null,
|
|
1090
|
+
network: ledger.network,
|
|
1091
|
+
description: ledger.description ?? null,
|
|
1092
|
+
lastBlockIndex: ledger.lastBlockIndex ?? null,
|
|
1093
|
+
coingeckoId: ledger.coingeckoId ?? null,
|
|
1094
|
+
currentPrice: ledger.currentPrice ?? null,
|
|
1095
|
+
priceFetchMethod: ledger.priceFetchMethod ?? null,
|
|
1096
|
+
lastPriceUpdate: ledger.lastPriceUpdate ?? null,
|
|
1097
|
+
createdAt: ledger.createdAt,
|
|
1098
|
+
updatedAt: ledger.updatedAt,
|
|
1099
|
+
};
|
|
1100
|
+
this.emitMethodSuccess('getLedgerInfo', { ledgerCanisterId });
|
|
1101
|
+
return result;
|
|
1102
|
+
}
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
const err = new errors_1.IcpayError({
|
|
1105
|
+
code: 'LEDGER_INFO_FETCH_FAILED',
|
|
1106
|
+
message: `Failed to fetch ledger info for ${ledgerCanisterId}`,
|
|
1107
|
+
details: error
|
|
1108
|
+
});
|
|
1109
|
+
this.emitMethodError('getLedgerInfo', err);
|
|
1110
|
+
throw err;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Send funds from USD to a specific ledger (public method)
|
|
1115
|
+
*/
|
|
1116
|
+
async sendFundsUsd(request) {
|
|
1117
|
+
this.emitMethodStart('sendFundsUsd', { request });
|
|
1118
|
+
try {
|
|
1119
|
+
// Convert usdAmount to number if it's a string
|
|
1120
|
+
const usdAmount = typeof request.usdAmount === 'string' ? parseFloat(request.usdAmount) : request.usdAmount;
|
|
1121
|
+
const priceCalculationResult = await this.calculateTokenAmountFromUSD({
|
|
1122
|
+
usdAmount: usdAmount,
|
|
1123
|
+
ledgerCanisterId: request.ledgerCanisterId
|
|
1124
|
+
});
|
|
1125
|
+
const createTransactionRequest = {
|
|
1126
|
+
ledgerCanisterId: request.ledgerCanisterId,
|
|
1127
|
+
amount: priceCalculationResult.tokenAmountDecimals,
|
|
1128
|
+
accountCanisterId: request.accountCanisterId,
|
|
1129
|
+
metadata: request.metadata
|
|
1130
|
+
};
|
|
1131
|
+
const res = await this.sendFunds(createTransactionRequest);
|
|
1132
|
+
this.emitMethodSuccess('sendFundsUsd', res);
|
|
1133
|
+
return res;
|
|
1134
|
+
}
|
|
1135
|
+
catch (error) {
|
|
1136
|
+
if (error instanceof errors_1.IcpayError) {
|
|
1137
|
+
this.emitMethodError('sendFundsUsd', error);
|
|
1138
|
+
throw error;
|
|
1139
|
+
}
|
|
1140
|
+
const err = new errors_1.IcpayError({
|
|
1141
|
+
code: 'SEND_FUNDS_USD_FAILED',
|
|
1142
|
+
message: 'Failed to send funds from USD',
|
|
1143
|
+
details: error
|
|
1144
|
+
});
|
|
1145
|
+
this.emitMethodError('sendFundsUsd', err);
|
|
1146
|
+
throw err;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Get all ledgers with price information (public method)
|
|
1151
|
+
*/
|
|
1152
|
+
async getAllLedgersWithPrices() {
|
|
1153
|
+
this.emitMethodStart('getAllLedgersWithPrices');
|
|
1154
|
+
try {
|
|
1155
|
+
const response = await this.publicApiClient.get('/sdk/public/ledgers/all-with-prices');
|
|
1156
|
+
const result = response.data.map((ledger) => ({
|
|
1157
|
+
id: ledger.id,
|
|
1158
|
+
name: ledger.name,
|
|
1159
|
+
symbol: ledger.symbol,
|
|
1160
|
+
canisterId: ledger.canisterId,
|
|
1161
|
+
standard: ledger.standard,
|
|
1162
|
+
decimals: ledger.decimals,
|
|
1163
|
+
logoUrl: ledger.logoUrl ?? null,
|
|
1164
|
+
verified: ledger.verified,
|
|
1165
|
+
fee: ledger.fee ?? null,
|
|
1166
|
+
network: ledger.network,
|
|
1167
|
+
description: ledger.description ?? null,
|
|
1168
|
+
lastBlockIndex: ledger.lastBlockIndex ?? null,
|
|
1169
|
+
coingeckoId: ledger.coingeckoId ?? null,
|
|
1170
|
+
currentPrice: ledger.currentPrice ?? null,
|
|
1171
|
+
priceFetchMethod: ledger.priceFetchMethod ?? null,
|
|
1172
|
+
lastPriceUpdate: ledger.lastPriceUpdate ?? null,
|
|
1173
|
+
createdAt: ledger.createdAt,
|
|
1174
|
+
updatedAt: ledger.updatedAt,
|
|
1175
|
+
}));
|
|
1176
|
+
this.emitMethodSuccess('getAllLedgersWithPrices', { count: result.length });
|
|
1177
|
+
return result;
|
|
1178
|
+
}
|
|
1179
|
+
catch (error) {
|
|
1180
|
+
const err = new errors_1.IcpayError({
|
|
1181
|
+
code: 'LEDGERS_WITH_PRICES_FETCH_FAILED',
|
|
1182
|
+
message: 'Failed to fetch ledgers with price information',
|
|
1183
|
+
details: error
|
|
1184
|
+
});
|
|
1185
|
+
this.emitMethodError('getAllLedgersWithPrices', err);
|
|
1186
|
+
throw err;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Utility function to format balance from smallest unit to human readable
|
|
1191
|
+
*/
|
|
1192
|
+
formatBalance(balance, decimals) {
|
|
1193
|
+
const balanceNum = parseFloat(balance);
|
|
1194
|
+
const divisor = Math.pow(10, decimals);
|
|
1195
|
+
const whole = Math.floor(balanceNum / divisor);
|
|
1196
|
+
const fraction = balanceNum % divisor;
|
|
1197
|
+
const fractionStr = fraction.toString().padStart(decimals, '0').replace(/0+$/, '');
|
|
1198
|
+
return `${whole}${fractionStr ? '.' + fractionStr : ''}`;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
exports.Icpay = Icpay;
|
|
1202
|
+
// Export types and classes
|
|
1203
|
+
__exportStar(require("./types"), exports);
|
|
1204
|
+
var errors_2 = require("./errors");
|
|
1205
|
+
Object.defineProperty(exports, "IcpayError", { enumerable: true, get: function () { return errors_2.IcpayError; } });
|
|
1206
|
+
var wallet_2 = require("./wallet");
|
|
1207
|
+
Object.defineProperty(exports, "IcpayWallet", { enumerable: true, get: function () { return wallet_2.IcpayWallet; } });
|
|
1208
|
+
__exportStar(require("./events"), exports);
|
|
1209
|
+
// Default export
|
|
1210
|
+
exports.default = Icpay;
|
|
1211
|
+
//# sourceMappingURL=index.js.map
|