@ercworldio/blockchain-shared 1.0.0 → 1.0.1-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/chains/networks_dev.json +44 -11
- package/build/chains/networks_prod-bu.json +7 -7
- package/build/chains/networks_prod-dz.json +510 -0
- package/build/chains/networks_prod.json +8 -8
- package/build/chains/networks_stg-bu.json +7 -7
- package/build/chains/networks_stg-dz.json +17 -6
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +13 -2
- package/build/interfaces/config.d.ts +15 -2
- package/build/interfaces/config.d.ts.map +1 -1
- package/build/interfaces/config.js +5 -0
- package/build/interfaces.d.ts +2 -1
- package/build/interfaces.d.ts.map +1 -1
- package/build/services/AlchemyService.d.ts +1 -0
- package/build/services/AlchemyService.d.ts.map +1 -1
- package/build/services/AlchemyService.js +6 -2
- package/build/services/AlchemyWebhookAddressManager.d.ts +38 -0
- package/build/services/AlchemyWebhookAddressManager.d.ts.map +1 -0
- package/build/services/AlchemyWebhookAddressManager.js +123 -0
- package/build/services/AlchemyWebhookSignature.d.ts +3 -3
- package/build/services/AlchemyWebhookSignature.d.ts.map +1 -1
- package/build/services/AlchemyWebhookSignature.js +8 -8
- package/build/services/BalanceService.d.ts +5 -1
- package/build/services/BalanceService.d.ts.map +1 -1
- package/build/services/BalanceService.js +32 -11
- package/build/services/ClaimJobService.d.ts.map +1 -1
- package/build/services/ClaimJobService.js +4 -1
- package/build/services/DepositAddressService.d.ts +8 -0
- package/build/services/DepositAddressService.d.ts.map +1 -1
- package/build/services/DepositAddressService.js +30 -0
- package/build/services/KeyVaultService.d.ts +1 -1
- package/build/services/KeyVaultService.d.ts.map +1 -1
- package/build/services/KeyVaultService.js +3 -30
- package/build/services/QuicknodeWebhookSignature.d.ts +3 -3
- package/build/services/QuicknodeWebhookSignature.d.ts.map +1 -1
- package/build/services/QuicknodeWebhookSignature.js +8 -8
- package/build/services/StorageService.js +2 -2
- package/build/services/SweepJobService.d.ts.map +1 -1
- package/build/services/SweepJobService.js +4 -1
- package/build/services/WalletManager.d.ts +3 -1
- package/build/services/WalletManager.d.ts.map +1 -1
- package/build/services/WalletManager.js +11 -7
- package/build/services/WalletManagerHelper.d.ts +15 -0
- package/build/services/WalletManagerHelper.d.ts.map +1 -0
- package/build/services/WalletManagerHelper.js +65 -0
- package/build/services/quicknode-notifications/QnNotificationsApi.d.ts +20 -0
- package/build/services/quicknode-notifications/QnNotificationsApi.d.ts.map +1 -0
- package/build/services/quicknode-notifications/QnNotificationsApi.js +134 -0
- package/build/services/quicknode-notifications/QnWebhookAddressManager.d.ts +47 -0
- package/build/services/quicknode-notifications/QnWebhookAddressManager.d.ts.map +1 -0
- package/build/services/quicknode-notifications/QnWebhookAddressManager.js +265 -0
- package/build/services/quicknode-notifications/SetupNotifications.d.ts +40 -0
- package/build/services/quicknode-notifications/SetupNotifications.d.ts.map +1 -0
- package/build/services/quicknode-notifications/SetupNotifications.js +253 -0
- package/build/services/quicknode-notifications/types/index.d.ts +2 -0
- package/build/services/quicknode-notifications/types/index.d.ts.map +1 -0
- package/build/services/quicknode-notifications/types/index.js +17 -0
- package/build/services/quicknode-notifications/types/notification_types.d.ts +154 -0
- package/build/services/quicknode-notifications/types/notification_types.d.ts.map +1 -0
- package/build/services/quicknode-notifications/types/notification_types.js +2 -0
- package/build/services/solana/escrow/idl/escrow.json +110 -1
- package/build/services/solana/escrow/types/escrow.d.ts +110 -1
- package/build/services/solana/escrow/types/escrow.d.ts.map +1 -1
- package/build/services/types/chain_manager.d.ts +5 -0
- package/build/services/types/chain_manager.d.ts.map +1 -1
- package/build/services/types/quicknode.d.ts +1 -0
- package/build/services/types/quicknode.d.ts.map +1 -1
- package/build/utils/AsyncTTLCache.d.ts +1 -0
- package/build/utils/AsyncTTLCache.d.ts.map +1 -1
- package/build/utils/AsyncTTLCache.js +3 -0
- package/build/utils/solana.d.ts +1 -0
- package/build/utils/solana.d.ts.map +1 -1
- package/build/utils/solana.js +15 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QnNotificationsApi.d.ts","sourceRoot":"","sources":["../../../src/services/quicknode-notifications/QnNotificationsApi.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACtG,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAC7C,OAAyB,EAAE,kBAAkB,EAAa,MAAM,8BAA8B,CAAC;AAE/F,cAAM,kBAAmB,SAAQ,UAAU;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAqB;IAC5C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAgD;IAChE,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,GAAG,CAA6D;IAExE,OAAO;IAWP,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,kBAAkB,GAAG,kBAAkB;IAOpE,cAAc,CACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EAAE,GAC5B,OAAO,CAAC,4BAA4B,CAAC;IAY3B,gBAAgB,IAAI,OAAO,CAAC,0BAA0B,CAAC;IAIvD,wBAAwB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC;IAQH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIhD,IAAI;CAsCrB;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
const errors_1 = __importDefault(require("../../errors/errors"));
|
|
49
|
+
const AsyncRateLimiter_1 = __importStar(require("../../utils/AsyncRateLimiter"));
|
|
50
|
+
class QnNotificationsApi extends errors_1.default {
|
|
51
|
+
constructor(config, rlc) {
|
|
52
|
+
super();
|
|
53
|
+
this.base_url = "https://api.quicknode.com/webhooks/rest/v1";
|
|
54
|
+
this.rlc = { maxConcurrent: 1, minDelayMs: 1000 };
|
|
55
|
+
this.api_key = config.config.quicknodeApiKey;
|
|
56
|
+
if (!this.api_key)
|
|
57
|
+
throw new Error(`quicknode api key is required`);
|
|
58
|
+
if (rlc) {
|
|
59
|
+
this.rlc = rlc;
|
|
60
|
+
}
|
|
61
|
+
this.rate_limiter = new AsyncRateLimiter_1.default(this.rlc);
|
|
62
|
+
}
|
|
63
|
+
static getInstance(config, rlc) {
|
|
64
|
+
if (!this.instance) {
|
|
65
|
+
this.instance = new QnNotificationsApi(config, rlc);
|
|
66
|
+
}
|
|
67
|
+
return this.instance;
|
|
68
|
+
}
|
|
69
|
+
create_webhook(template, name, network, webhook_url, initial_addresses) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const template_arg_key = template === 'solanaWalletFilter' ? 'accounts' : 'wallets';
|
|
72
|
+
const body = {
|
|
73
|
+
name,
|
|
74
|
+
network,
|
|
75
|
+
status: 'active',
|
|
76
|
+
destination_attributes: { url: webhook_url, compression: 'none' },
|
|
77
|
+
templateArgs: { [template_arg_key]: initial_addresses }
|
|
78
|
+
};
|
|
79
|
+
return yield this.call(`/webhooks/template/${template}`, 'POST', { body });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
get_all_webhooks() {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
return yield this.call('/webhooks', 'GET', { query_params: { limit: 100, offset: 0 } });
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
update_webhook_addresses(webhook_id, template, all_addresses) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
const template_arg_key = template === 'solanaWalletFilter' ? 'accounts' : 'wallets';
|
|
90
|
+
const body = {
|
|
91
|
+
templateArgs: { [template_arg_key]: all_addresses }
|
|
92
|
+
};
|
|
93
|
+
yield this.call(`/webhooks/${webhook_id}/template/${template}`, 'PATCH', { body });
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
delete_webhook(webhook_id) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
yield this.call(`/webhooks/${webhook_id}`, 'DELETE');
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
call(endpoint, method, opts) {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
const request_options = {
|
|
104
|
+
method,
|
|
105
|
+
headers: {
|
|
106
|
+
'accept': 'application/json',
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
'x-api-key': this.api_key
|
|
109
|
+
},
|
|
110
|
+
body: (opts === null || opts === void 0 ? void 0 : opts.body) ? JSON.stringify(opts.body) : undefined,
|
|
111
|
+
};
|
|
112
|
+
let url = `${this.base_url}${endpoint}`;
|
|
113
|
+
if (opts === null || opts === void 0 ? void 0 : opts.query_params) {
|
|
114
|
+
const params = new URLSearchParams();
|
|
115
|
+
Object.entries(opts.query_params).forEach(([k, v]) => {
|
|
116
|
+
if (v !== undefined && v !== null)
|
|
117
|
+
params.append(k, String(v));
|
|
118
|
+
});
|
|
119
|
+
const qs = params.toString();
|
|
120
|
+
if (qs)
|
|
121
|
+
url += `?${qs}`;
|
|
122
|
+
}
|
|
123
|
+
console.log(`QnNotificationsApi: ${method} ${url}`);
|
|
124
|
+
const res = yield this.rate_limiter.execute(() => (0, AsyncRateLimiter_1.withRetry)(() => __awaiter(this, void 0, void 0, function* () { return yield fetch(url, request_options); })));
|
|
125
|
+
if (!res.ok) {
|
|
126
|
+
const err = yield res.text();
|
|
127
|
+
throw new Error(`QnNotificationsApi: ${method} ${endpoint} failed [${res.status}]: ${err}`);
|
|
128
|
+
}
|
|
129
|
+
const text = yield res.text();
|
|
130
|
+
return text ? JSON.parse(text) : null;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.default = QnNotificationsApi;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BlockchainType, ChainId } from "../../interfaces";
|
|
2
|
+
import { IConfig } from "../../interfaces/config";
|
|
3
|
+
import { RateLimiterOptions } from "../../utils/AsyncRateLimiter";
|
|
4
|
+
declare class QnWebhookAddressManager {
|
|
5
|
+
private static instance;
|
|
6
|
+
private api;
|
|
7
|
+
private config;
|
|
8
|
+
private cache;
|
|
9
|
+
private constructor();
|
|
10
|
+
static getInstance(config: IConfig, rlc?: RateLimiterOptions): QnWebhookAddressManager;
|
|
11
|
+
private get_blockchain_for_chain;
|
|
12
|
+
/** Returns the { name, network } pairs for the notification configs of a given chain. */
|
|
13
|
+
private get_webhook_keys_for_chain;
|
|
14
|
+
private get_notif_configs;
|
|
15
|
+
private is_stale;
|
|
16
|
+
private load;
|
|
17
|
+
private ensure_loaded;
|
|
18
|
+
private matches_key;
|
|
19
|
+
add_address(blockchain: BlockchainType, address: string): Promise<boolean>;
|
|
20
|
+
remove_address(blockchain: BlockchainType, address: string): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Add an address to the webhook(s) for a single chain only.
|
|
23
|
+
* Matches by both name and network to avoid cross-service contamination.
|
|
24
|
+
* Use this for the immediate await before responding to the caller.
|
|
25
|
+
*/
|
|
26
|
+
add_address_to_chain(chain_id: ChainId, address: string): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Remove an address from the webhook(s) for a single chain only.
|
|
29
|
+
* Matches by both name and network to avoid cross-service contamination.
|
|
30
|
+
* Use this for the immediate await before responding to the caller.
|
|
31
|
+
*/
|
|
32
|
+
remove_address_from_chain(chain_id: ChainId, address: string): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Add an address to all webhooks for a blockchain except the already-updated chain.
|
|
35
|
+
* Excludes by both name and network so only the exact webhooks for that chain are skipped.
|
|
36
|
+
* Call this fire-and-forget after responding to the caller.
|
|
37
|
+
*/
|
|
38
|
+
sync_add_to_remaining(blockchain: BlockchainType, address: string, exclude_chain_id: ChainId): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Remove an address from all webhooks for a blockchain except the already-updated chain.
|
|
41
|
+
* Excludes by both name and network so only the exact webhooks for that chain are skipped.
|
|
42
|
+
* Call this fire-and-forget after responding to the caller.
|
|
43
|
+
*/
|
|
44
|
+
sync_remove_from_remaining(blockchain: BlockchainType, address: string, exclude_chain_id: ChainId): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
export default QnWebhookAddressManager;
|
|
47
|
+
//# sourceMappingURL=QnWebhookAddressManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QnWebhookAddressManager.d.ts","sourceRoot":"","sources":["../../../src/services/quicknode-notifications/QnWebhookAddressManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAiB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA0BlE,cAAM,uBAAuB;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA0B;IACjD,OAAO,CAAC,GAAG,CAAqB;IAChC,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,KAAK,CAAmD;IAEhE,OAAO;IAKP,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,kBAAkB,GAAG,uBAAuB;IAOtF,OAAO,CAAC,wBAAwB;IAKhC,yFAAyF;IACzF,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,QAAQ;YAMF,IAAI;YAuCJ,aAAa;IAO3B,OAAO,CAAC,WAAW;IAIN,WAAW,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB1E,cAAc,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB1F;;;;OAIG;IACU,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuBvF;;;;OAIG;IACU,yBAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuB5F;;;;OAIG;IACU,qBAAqB,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzH;;;;OAIG;IACU,0BAA0B,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAkBjI;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const ChainManager_1 = __importDefault(require("../ChainManager"));
|
|
16
|
+
const QnNotificationsApi_1 = __importDefault(require("./QnNotificationsApi"));
|
|
17
|
+
const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
18
|
+
class QnWebhookAddressManager {
|
|
19
|
+
constructor(config, rlc) {
|
|
20
|
+
this.cache = new Map();
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.api = QnNotificationsApi_1.default.getInstance(config, rlc);
|
|
23
|
+
}
|
|
24
|
+
static getInstance(config, rlc) {
|
|
25
|
+
if (!this.instance) {
|
|
26
|
+
this.instance = new QnWebhookAddressManager(config, rlc);
|
|
27
|
+
}
|
|
28
|
+
return this.instance;
|
|
29
|
+
}
|
|
30
|
+
get_blockchain_for_chain(chain_id) {
|
|
31
|
+
var _a, _b;
|
|
32
|
+
const cfg = (_a = ChainManager_1.default.getInstance(this.config).contractsMap) === null || _a === void 0 ? void 0 : _a[chain_id.toString()];
|
|
33
|
+
return (_b = cfg === null || cfg === void 0 ? void 0 : cfg.blockchainType) !== null && _b !== void 0 ? _b : null;
|
|
34
|
+
}
|
|
35
|
+
/** Returns the { name, network } pairs for the notification configs of a given chain. */
|
|
36
|
+
get_webhook_keys_for_chain(chain_id) {
|
|
37
|
+
var _a, _b, _c;
|
|
38
|
+
const cfg = (_a = ChainManager_1.default.getInstance(this.config).contractsMap) === null || _a === void 0 ? void 0 : _a[chain_id.toString()];
|
|
39
|
+
return (_c = (_b = cfg === null || cfg === void 0 ? void 0 : cfg.quicknodeNotificationConfig) === null || _b === void 0 ? void 0 : _b.map(n => ({
|
|
40
|
+
name: n.name.toLowerCase(),
|
|
41
|
+
network: n.network.toLowerCase(),
|
|
42
|
+
}))) !== null && _c !== void 0 ? _c : [];
|
|
43
|
+
}
|
|
44
|
+
get_notif_configs(blockchain) {
|
|
45
|
+
const chain_manager = ChainManager_1.default.getInstance(this.config);
|
|
46
|
+
if (!chain_manager.contractsMap)
|
|
47
|
+
return [];
|
|
48
|
+
return Object.entries(chain_manager.contractsMap)
|
|
49
|
+
.filter(([_, cfg]) => {
|
|
50
|
+
var _a;
|
|
51
|
+
return cfg.blockchainType === blockchain &&
|
|
52
|
+
((_a = cfg.quicknodeNotificationConfig) === null || _a === void 0 ? void 0 : _a.length) > 0;
|
|
53
|
+
})
|
|
54
|
+
.flatMap(([_, cfg]) => cfg.quicknodeNotificationConfig.map(n => (Object.assign(Object.assign({}, n), { blockchain: cfg.blockchainType }))))
|
|
55
|
+
.filter((n, i, arr) => arr.findIndex(x => x.name === n.name) === i);
|
|
56
|
+
}
|
|
57
|
+
is_stale(blockchain) {
|
|
58
|
+
const cached = this.cache.get(blockchain);
|
|
59
|
+
if (!cached)
|
|
60
|
+
return true;
|
|
61
|
+
return Date.now() - cached.loaded_at > CACHE_TTL_MS;
|
|
62
|
+
}
|
|
63
|
+
load(blockchain) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
var _a;
|
|
66
|
+
const notif_configs = this.get_notif_configs(blockchain);
|
|
67
|
+
if (notif_configs.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
let all_webhooks = [];
|
|
70
|
+
try {
|
|
71
|
+
const res = yield this.api.get_all_webhooks();
|
|
72
|
+
all_webhooks = res.data || [];
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`QnWebhookAddressManager: Failed to fetch webhooks for blockchain=${blockchain}`, error);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const entries = [];
|
|
79
|
+
for (const notif_config of notif_configs) {
|
|
80
|
+
const existing = all_webhooks.find(w => {
|
|
81
|
+
var _a, _b;
|
|
82
|
+
return ((_a = w.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === notif_config.name.toLowerCase() &&
|
|
83
|
+
((_b = w.network) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === notif_config.network.toLowerCase();
|
|
84
|
+
});
|
|
85
|
+
if (!existing) {
|
|
86
|
+
console.warn(`QnWebhookAddressManager: No webhook found for config=${notif_config.name} network=${notif_config.network}`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const arg_key = notif_config.template === 'solanaWalletFilter' ? 'accounts' : 'wallets';
|
|
90
|
+
const addresses = ((_a = existing.templateArgs) === null || _a === void 0 ? void 0 : _a[arg_key]) || [];
|
|
91
|
+
entries.push({
|
|
92
|
+
webhook_id: existing.id,
|
|
93
|
+
name: notif_config.name.toLowerCase(),
|
|
94
|
+
template: notif_config.template,
|
|
95
|
+
network: notif_config.network.toLowerCase(),
|
|
96
|
+
address_set: new Set(addresses),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
this.cache.set(blockchain, { entries, loaded_at: Date.now() });
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
ensure_loaded(blockchain) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
var _a;
|
|
105
|
+
if (this.is_stale(blockchain)) {
|
|
106
|
+
yield this.load(blockchain);
|
|
107
|
+
}
|
|
108
|
+
return ((_a = this.cache.get(blockchain)) === null || _a === void 0 ? void 0 : _a.entries) || [];
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
matches_key(entry, key) {
|
|
112
|
+
return entry.name === key.name && entry.network === key.network;
|
|
113
|
+
}
|
|
114
|
+
add_address(blockchain, address) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const entries = yield this.ensure_loaded(blockchain);
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.address_set.has(address))
|
|
119
|
+
continue;
|
|
120
|
+
entry.address_set.add(address);
|
|
121
|
+
try {
|
|
122
|
+
yield this.api.update_webhook_addresses(entry.webhook_id, entry.template, Array.from(entry.address_set));
|
|
123
|
+
console.log(`QnWebhookAddressManager: Added ${address} to ${entry.name} webhook`);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
entry.address_set.delete(address);
|
|
127
|
+
console.error(`QnWebhookAddressManager: Failed to add ${address} to ${entry.name}`, error);
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
remove_address(blockchain, address) {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
const entries = yield this.ensure_loaded(blockchain);
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
if (!entry.address_set.has(address))
|
|
139
|
+
continue;
|
|
140
|
+
entry.address_set.delete(address);
|
|
141
|
+
try {
|
|
142
|
+
yield this.api.update_webhook_addresses(entry.webhook_id, entry.template, Array.from(entry.address_set));
|
|
143
|
+
console.log(`QnWebhookAddressManager: Removed ${address} from ${entry.name} webhook`);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
entry.address_set.add(address);
|
|
147
|
+
console.error(`QnWebhookAddressManager: Failed to remove ${address} from ${entry.name}`, error);
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Add an address to the webhook(s) for a single chain only.
|
|
156
|
+
* Matches by both name and network to avoid cross-service contamination.
|
|
157
|
+
* Use this for the immediate await before responding to the caller.
|
|
158
|
+
*/
|
|
159
|
+
add_address_to_chain(chain_id, address) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
const blockchain = this.get_blockchain_for_chain(chain_id);
|
|
162
|
+
if (!blockchain)
|
|
163
|
+
return false;
|
|
164
|
+
const keys = this.get_webhook_keys_for_chain(chain_id);
|
|
165
|
+
const entries = (yield this.ensure_loaded(blockchain))
|
|
166
|
+
.filter(e => keys.some(k => this.matches_key(e, k)));
|
|
167
|
+
for (const entry of entries) {
|
|
168
|
+
if (entry.address_set.has(address))
|
|
169
|
+
continue;
|
|
170
|
+
entry.address_set.add(address);
|
|
171
|
+
try {
|
|
172
|
+
yield this.api.update_webhook_addresses(entry.webhook_id, entry.template, Array.from(entry.address_set));
|
|
173
|
+
console.log(`QnWebhookAddressManager: Added ${address} to ${entry.name} webhook (chain ${chain_id})`);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
entry.address_set.delete(address);
|
|
177
|
+
console.error(`QnWebhookAddressManager: Failed to add ${address} to ${entry.name}`, error);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Remove an address from the webhook(s) for a single chain only.
|
|
186
|
+
* Matches by both name and network to avoid cross-service contamination.
|
|
187
|
+
* Use this for the immediate await before responding to the caller.
|
|
188
|
+
*/
|
|
189
|
+
remove_address_from_chain(chain_id, address) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
const blockchain = this.get_blockchain_for_chain(chain_id);
|
|
192
|
+
if (!blockchain)
|
|
193
|
+
return false;
|
|
194
|
+
const keys = this.get_webhook_keys_for_chain(chain_id);
|
|
195
|
+
const entries = (yield this.ensure_loaded(blockchain))
|
|
196
|
+
.filter(e => keys.some(k => this.matches_key(e, k)));
|
|
197
|
+
for (const entry of entries) {
|
|
198
|
+
if (!entry.address_set.has(address))
|
|
199
|
+
continue;
|
|
200
|
+
entry.address_set.delete(address);
|
|
201
|
+
try {
|
|
202
|
+
yield this.api.update_webhook_addresses(entry.webhook_id, entry.template, Array.from(entry.address_set));
|
|
203
|
+
console.log(`QnWebhookAddressManager: Removed ${address} from ${entry.name} webhook (chain ${chain_id})`);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
entry.address_set.add(address);
|
|
207
|
+
console.error(`QnWebhookAddressManager: Failed to remove ${address} from ${entry.name}`, error);
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Add an address to all webhooks for a blockchain except the already-updated chain.
|
|
216
|
+
* Excludes by both name and network so only the exact webhooks for that chain are skipped.
|
|
217
|
+
* Call this fire-and-forget after responding to the caller.
|
|
218
|
+
*/
|
|
219
|
+
sync_add_to_remaining(blockchain, address, exclude_chain_id) {
|
|
220
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
221
|
+
const exclude_keys = this.get_webhook_keys_for_chain(exclude_chain_id);
|
|
222
|
+
const entries = (yield this.ensure_loaded(blockchain))
|
|
223
|
+
.filter(e => !exclude_keys.some(k => this.matches_key(e, k)));
|
|
224
|
+
for (const entry of entries) {
|
|
225
|
+
if (entry.address_set.has(address))
|
|
226
|
+
continue;
|
|
227
|
+
entry.address_set.add(address);
|
|
228
|
+
try {
|
|
229
|
+
yield this.api.update_webhook_addresses(entry.webhook_id, entry.template, Array.from(entry.address_set));
|
|
230
|
+
console.log(`QnWebhookAddressManager: Synced add ${address} to ${entry.name} webhook`);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
entry.address_set.delete(address);
|
|
234
|
+
console.error(`QnWebhookAddressManager: Sync add failed for ${entry.name}`, error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Remove an address from all webhooks for a blockchain except the already-updated chain.
|
|
241
|
+
* Excludes by both name and network so only the exact webhooks for that chain are skipped.
|
|
242
|
+
* Call this fire-and-forget after responding to the caller.
|
|
243
|
+
*/
|
|
244
|
+
sync_remove_from_remaining(blockchain, address, exclude_chain_id) {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
const exclude_keys = this.get_webhook_keys_for_chain(exclude_chain_id);
|
|
247
|
+
const entries = (yield this.ensure_loaded(blockchain))
|
|
248
|
+
.filter(e => !exclude_keys.some(k => this.matches_key(e, k)));
|
|
249
|
+
for (const entry of entries) {
|
|
250
|
+
if (!entry.address_set.has(address))
|
|
251
|
+
continue;
|
|
252
|
+
entry.address_set.delete(address);
|
|
253
|
+
try {
|
|
254
|
+
yield this.api.update_webhook_addresses(entry.webhook_id, entry.template, Array.from(entry.address_set));
|
|
255
|
+
console.log(`QnWebhookAddressManager: Synced remove ${address} from ${entry.name} webhook`);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
entry.address_set.add(address);
|
|
259
|
+
console.error(`QnWebhookAddressManager: Sync remove failed for ${entry.name}`, error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
exports.default = QnWebhookAddressManager;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import BaseErrors from "../../errors/errors";
|
|
2
|
+
import { BlockchainType } from "../../interfaces";
|
|
3
|
+
import { IConfig } from "../../interfaces/config";
|
|
4
|
+
import { IDatabasePool } from "../../interfaces/database";
|
|
5
|
+
import { RateLimiterOptions } from "../../utils/AsyncRateLimiter";
|
|
6
|
+
export interface NotificationWebhookEntry {
|
|
7
|
+
webhook_id: string;
|
|
8
|
+
template: string;
|
|
9
|
+
network: string;
|
|
10
|
+
blockchain: BlockchainType;
|
|
11
|
+
address_set: Set<string>;
|
|
12
|
+
}
|
|
13
|
+
declare class SetupNotifications extends BaseErrors {
|
|
14
|
+
private static instance;
|
|
15
|
+
private api;
|
|
16
|
+
private webhook_map;
|
|
17
|
+
private by_blockchain;
|
|
18
|
+
private pending_configs;
|
|
19
|
+
private config;
|
|
20
|
+
private db;
|
|
21
|
+
private constructor();
|
|
22
|
+
static getInstance(config: IConfig, db: IDatabasePool, rlc: RateLimiterOptions): SetupNotifications;
|
|
23
|
+
get_all_webhook_ids(): string[];
|
|
24
|
+
get_entry_by_webhook_id(id: string): NotificationWebhookEntry | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Fetch all deposit addresses for a blockchain from the DB (paginated).
|
|
27
|
+
* The `blockchain` filter value in DepositAddressService is the string stored
|
|
28
|
+
* in the DB — e.g. "solana", "evm", "ethereum", etc.
|
|
29
|
+
*/
|
|
30
|
+
private fetch_all_db_addresses;
|
|
31
|
+
/**
|
|
32
|
+
* Register a newly-created webhook entry in the internal maps.
|
|
33
|
+
*/
|
|
34
|
+
private register_entry;
|
|
35
|
+
initialize(): Promise<void>;
|
|
36
|
+
add_address(blockchain: BlockchainType, address: string): Promise<void>;
|
|
37
|
+
remove_address(blockchain: BlockchainType, address: string): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export default SetupNotifications;
|
|
40
|
+
//# sourceMappingURL=SetupNotifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SetupNotifications.d.ts","sourceRoot":"","sources":["../../../src/services/quicknode-notifications/SetupNotifications.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAiB,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAQlE,MAAM,WAAW,wBAAwB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC;IAC3B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAWD,cAAM,kBAAmB,SAAQ,UAAU;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAqB;IAC5C,OAAO,CAAC,GAAG,CAAqB;IAEhC,OAAO,CAAC,WAAW,CAAoD;IAEvE,OAAO,CAAC,aAAa,CAA8D;IAEnF,OAAO,CAAC,eAAe,CAAqD;IAC5E,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO;IAOP,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,kBAAkB,GAAG,kBAAkB;IAO5F,mBAAmB,IAAI,MAAM,EAAE;IAI/B,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS;IAOhF;;;;OAIG;YACW,sBAAsB;IAuBpC;;OAEG;IACH,OAAO,CAAC,cAAc;IAOT,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAwH3B,WAAW,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyDvE,cAAc,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAe1F;AAED,eAAe,kBAAkB,CAAC"}
|