@cityofzion/bs-neox 0.0.1 → 1.1.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.
@@ -0,0 +1,7 @@
1
+ import { BSEthereum } from '@cityofzion/bs-ethereum';
2
+ import { BSNeoXNetworkId } from './constants/BSNeoXConstants';
3
+ import { GetLedgerTransport, Network } from '@cityofzion/blockchain-service';
4
+ export declare class BSNeoX<BSName extends string = string> extends BSEthereum<BSName, BSNeoXNetworkId> {
5
+ constructor(name: BSName, network?: Network<BSNeoXNetworkId>, getLedgerTransport?: GetLedgerTransport<BSName>);
6
+ setNetwork(network: Network<BSNeoXNetworkId>): void;
7
+ }
package/dist/BSNeoX.js ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BSNeoX = void 0;
4
+ const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
5
+ const BSNeoXConstants_1 = require("./constants/BSNeoXConstants");
6
+ const BlockscoutBDSNeoX_1 = require("./services/blockchain-data/BlockscoutBDSNeoX");
7
+ const FlamingoForthewinEDSNeoX_1 = require("./services/exchange-data/FlamingoForthewinEDSNeoX");
8
+ const BlockscoutESNeoX_1 = require("./services/explorer/BlockscoutESNeoX");
9
+ const GhostMarketNDSNeoX_1 = require("./services/nft-data/GhostMarketNDSNeoX");
10
+ class BSNeoX extends bs_ethereum_1.BSEthereum {
11
+ constructor(name, network, getLedgerTransport) {
12
+ network = network !== null && network !== void 0 ? network : BSNeoXConstants_1.BSNeoXConstants.DEFAULT_NETWORK;
13
+ super(name, network, getLedgerTransport);
14
+ this.tokens = [BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET];
15
+ this.nativeTokens = [BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET];
16
+ this.feeToken = BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET;
17
+ }
18
+ setNetwork(network) {
19
+ this.network = network;
20
+ this.nftDataService = new GhostMarketNDSNeoX_1.GhostMarketNDSNeoX(network);
21
+ this.explorerService = new BlockscoutESNeoX_1.BlockscoutESNeoX(network);
22
+ this.exchangeDataService = new FlamingoForthewinEDSNeoX_1.FlamingoForthewinEDSNeoX(network);
23
+ this.blockchainDataService = new BlockscoutBDSNeoX_1.BlockscoutBDSNeoX(network, this.nftDataService, this.explorerService);
24
+ }
25
+ }
26
+ exports.BSNeoX = BSNeoX;
@@ -0,0 +1,13 @@
1
+ import { Network, NetworkId, Token } from '@cityofzion/blockchain-service';
2
+ export type BSNeoXNetworkId = NetworkId<'47763' | '12227332'>;
3
+ export declare class BSNeoXConstants {
4
+ static NATIVE_ASSET: Token;
5
+ static RPC_LIST_BY_NETWORK_ID: Record<BSNeoXNetworkId, string[]>;
6
+ static MAINNET_NETWORK_IDS: BSNeoXNetworkId[];
7
+ static TESTNET_NETWORK_IDS: BSNeoXNetworkId[];
8
+ static ALL_NETWORK_IDS: BSNeoXNetworkId[];
9
+ static MAINNET_NETWORK: Network<BSNeoXNetworkId>;
10
+ static TESTNET_NETWORK: Network<BSNeoXNetworkId>;
11
+ static ALL_NETWORK: Network<BSNeoXNetworkId>[];
12
+ static DEFAULT_NETWORK: Network<BSNeoXNetworkId>;
13
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.BSNeoXConstants = void 0;
5
+ class BSNeoXConstants {
6
+ }
7
+ exports.BSNeoXConstants = BSNeoXConstants;
8
+ _a = BSNeoXConstants;
9
+ BSNeoXConstants.NATIVE_ASSET = { symbol: 'GAS', name: 'GAS', decimals: 18, hash: '-' };
10
+ BSNeoXConstants.RPC_LIST_BY_NETWORK_ID = {
11
+ '47763': ['https://mainnet-1.rpc.banelabs.org'],
12
+ '12227332': ['https://neoxt4seed1.ngd.network'],
13
+ };
14
+ BSNeoXConstants.MAINNET_NETWORK_IDS = ['47763'];
15
+ BSNeoXConstants.TESTNET_NETWORK_IDS = ['12227332'];
16
+ BSNeoXConstants.ALL_NETWORK_IDS = [..._a.MAINNET_NETWORK_IDS, ..._a.TESTNET_NETWORK_IDS];
17
+ BSNeoXConstants.MAINNET_NETWORK = {
18
+ id: '47763',
19
+ name: 'NeoX Testnet',
20
+ url: _a.RPC_LIST_BY_NETWORK_ID['47763'][0],
21
+ };
22
+ BSNeoXConstants.TESTNET_NETWORK = {
23
+ id: '12227332',
24
+ name: 'NeoX Mainnet',
25
+ url: _a.RPC_LIST_BY_NETWORK_ID['12227332'][0],
26
+ };
27
+ BSNeoXConstants.ALL_NETWORK = [_a.TESTNET_NETWORK, _a.MAINNET_NETWORK];
28
+ BSNeoXConstants.DEFAULT_NETWORK = _a.MAINNET_NETWORK;
@@ -0,0 +1,6 @@
1
+ export * from './BSNeoX';
2
+ export * from './constants/BSNeoXConstants';
3
+ export * from './services/blockchain-data/BlockscoutBDSNeoX';
4
+ export * from './services/exchange-data/FlamingoForthewinEDSNeoX';
5
+ export * from './services/explorer/BlockscoutESNeoX';
6
+ export * from './services/nft-data/GhostMarketNDSNeoX';
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./BSNeoX"), exports);
18
+ __exportStar(require("./constants/BSNeoXConstants"), exports);
19
+ __exportStar(require("./services/blockchain-data/BlockscoutBDSNeoX"), exports);
20
+ __exportStar(require("./services/exchange-data/FlamingoForthewinEDSNeoX"), exports);
21
+ __exportStar(require("./services/explorer/BlockscoutESNeoX"), exports);
22
+ __exportStar(require("./services/nft-data/GhostMarketNDSNeoX"), exports);
@@ -0,0 +1,17 @@
1
+ import { BalanceResponse, ContractResponse, ExplorerService, ExportTransactionsByAddressParams, FullTransactionsByAddressParams, FullTransactionsByAddressResponse, Network, NftDataService, Token, TransactionResponse, TransactionsByAddressParams, TransactionsByAddressResponse } from '@cityofzion/blockchain-service';
2
+ import { DoraBDSEthereum } from '@cityofzion/bs-ethereum';
3
+ import { BSNeoXNetworkId } from '../../constants/BSNeoXConstants';
4
+ export declare class BlockscoutBDSNeoX extends DoraBDSEthereum<BSNeoXNetworkId> {
5
+ static BASE_URL_BY_CHAIN_ID: Partial<Record<BSNeoXNetworkId, string>>;
6
+ static getClient(network: Network<BSNeoXNetworkId>): import("axios").AxiosInstance;
7
+ constructor(network: Network<BSNeoXNetworkId>, nftDataService: NftDataService, explorerService: ExplorerService);
8
+ maxTimeToConfirmTransactionInMs: number;
9
+ getTransaction(txid: string): Promise<TransactionResponse>;
10
+ getTransactionsByAddress(params: TransactionsByAddressParams): Promise<TransactionsByAddressResponse>;
11
+ getFullTransactionsByAddress({ nextCursor, ...params }: FullTransactionsByAddressParams): Promise<FullTransactionsByAddressResponse>;
12
+ exportFullTransactionsByAddress(params: ExportTransactionsByAddressParams): Promise<string>;
13
+ getContract(contractHash: string): Promise<ContractResponse>;
14
+ getTokenInfo(tokenHash: string): Promise<Token>;
15
+ getBalance(address: string): Promise<BalanceResponse[]>;
16
+ getBlockHeight(): Promise<number>;
17
+ }
@@ -0,0 +1,314 @@
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 __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.BlockscoutBDSNeoX = void 0;
27
+ const blockchain_service_1 = require("@cityofzion/blockchain-service");
28
+ const axios_1 = __importDefault(require("axios"));
29
+ const ethers_1 = require("ethers");
30
+ const dora_ts_1 = require("@cityofzion/dora-ts");
31
+ const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
32
+ const BSNeoXConstants_1 = require("../../constants/BSNeoXConstants");
33
+ class BlockscoutBDSNeoX extends bs_ethereum_1.DoraBDSEthereum {
34
+ static getClient(network) {
35
+ const baseURL = BlockscoutBDSNeoX.BASE_URL_BY_CHAIN_ID[network.id];
36
+ if (!baseURL) {
37
+ throw new Error('Unsupported network');
38
+ }
39
+ return axios_1.default.create({
40
+ baseURL,
41
+ });
42
+ }
43
+ constructor(network, nftDataService, explorerService) {
44
+ super(network, BSNeoXConstants_1.BSNeoXConstants.ALL_NETWORK_IDS, nftDataService, explorerService);
45
+ this.maxTimeToConfirmTransactionInMs = 1000 * 60 * 5;
46
+ }
47
+ getTransaction(txid) {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ const client = BlockscoutBDSNeoX.getClient(this._network);
50
+ const { data } = yield client.get(`/transactions/${txid}`);
51
+ if (!data || 'message' in data) {
52
+ throw new Error('Transaction not found');
53
+ }
54
+ const nativeToken = BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET;
55
+ const transfers = [];
56
+ const hasNativeTokenBeingTransferred = data.value !== '0';
57
+ if (hasNativeTokenBeingTransferred) {
58
+ transfers.push({
59
+ amount: ethers_1.ethers.utils.formatUnits(data.value, nativeToken.decimals),
60
+ from: data.from.hash,
61
+ to: data.to.hash,
62
+ type: 'token',
63
+ contractHash: nativeToken.hash,
64
+ token: nativeToken,
65
+ });
66
+ }
67
+ const hasTokenTransfers = data.token_transfers && data.token_transfers.length > 0;
68
+ if (hasTokenTransfers) {
69
+ for (const tokenTransfer of data.token_transfers) {
70
+ if (tokenTransfer.token.type === 'ERC-20') {
71
+ transfers.push({
72
+ amount: ethers_1.ethers.utils.formatUnits(tokenTransfer.total.value, tokenTransfer.total.decimals),
73
+ from: tokenTransfer.from.hash,
74
+ to: tokenTransfer.to.hash,
75
+ type: 'token',
76
+ contractHash: tokenTransfer.token.address,
77
+ token: {
78
+ symbol: tokenTransfer.token.symbol,
79
+ name: tokenTransfer.token.name,
80
+ hash: tokenTransfer.token.address,
81
+ decimals: Number(tokenTransfer.total.decimals),
82
+ },
83
+ });
84
+ continue;
85
+ }
86
+ if (tokenTransfer.token.type === 'ERC-721') {
87
+ transfers.push({
88
+ tokenId: tokenTransfer.total.token_id,
89
+ from: tokenTransfer.from.hash,
90
+ to: tokenTransfer.to.hash,
91
+ type: 'nft',
92
+ contractHash: tokenTransfer.token.address,
93
+ });
94
+ }
95
+ }
96
+ }
97
+ return {
98
+ block: data.block,
99
+ hash: data.hash,
100
+ fee: ethers_1.ethers.utils.formatUnits(data.fee.value, nativeToken.decimals),
101
+ time: new Date(data.timestamp).getTime() / 1000,
102
+ notifications: [],
103
+ transfers,
104
+ };
105
+ });
106
+ }
107
+ getTransactionsByAddress(params) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ const client = BlockscoutBDSNeoX.getClient(this._network);
110
+ const { data } = yield client.get(`/addresses/${params.address}/transactions`, {
111
+ params: {
112
+ next_page_params: params.nextPageParams,
113
+ },
114
+ });
115
+ if (!data || 'message' in data) {
116
+ throw new Error('Transactions not found');
117
+ }
118
+ const nativeToken = BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET;
119
+ const transactions = [];
120
+ const promises = data.items.map((item) => __awaiter(this, void 0, void 0, function* () {
121
+ const transfers = [];
122
+ const hasNativeTokenBeingTransferred = item.value !== '0';
123
+ if (hasNativeTokenBeingTransferred) {
124
+ transfers.push({
125
+ amount: ethers_1.ethers.utils.formatUnits(item.value, nativeToken.decimals),
126
+ from: item.from.hash,
127
+ to: item.to.hash,
128
+ type: 'token',
129
+ contractHash: nativeToken.hash,
130
+ token: nativeToken,
131
+ });
132
+ }
133
+ if (item.raw_input) {
134
+ try {
135
+ const ERC20Interface = new ethers_1.ethers.utils.Interface(bs_ethereum_1.ERC20_ABI);
136
+ const result = ERC20Interface.decodeFunctionData('transfer', item.raw_input);
137
+ if (!result)
138
+ throw new Error('Invalid ERC20 transfer');
139
+ const contractHash = item.to.hash;
140
+ const token = yield this.getTokenInfo(contractHash);
141
+ const to = result[0];
142
+ const value = result[1];
143
+ transfers.push({
144
+ amount: ethers_1.ethers.utils.formatUnits(value, token.decimals),
145
+ from: item.from.hash,
146
+ to,
147
+ type: 'token',
148
+ contractHash: item.to.hash,
149
+ token,
150
+ });
151
+ }
152
+ catch (error) {
153
+ /* empty */
154
+ }
155
+ }
156
+ transactions.push({
157
+ block: item.block,
158
+ hash: item.hash,
159
+ fee: ethers_1.ethers.utils.formatUnits(item.fee.value, nativeToken.decimals),
160
+ time: new Date(item.timestamp).getTime() / 1000,
161
+ notifications: [],
162
+ transfers,
163
+ });
164
+ }));
165
+ yield Promise.allSettled(promises);
166
+ return {
167
+ transactions,
168
+ nextPageParams: data.next_page_params,
169
+ };
170
+ });
171
+ }
172
+ getFullTransactionsByAddress(_a) {
173
+ var _b;
174
+ var { nextCursor } = _a, params = __rest(_a, ["nextCursor"]);
175
+ return __awaiter(this, void 0, void 0, function* () {
176
+ this._validateGetFullTransactionsByAddressParams(params);
177
+ const response = yield dora_ts_1.api.NeoXREST.getFullTransactionsByAddress({
178
+ address: params.address,
179
+ timestampFrom: params.dateFrom,
180
+ timestampTo: params.dateTo,
181
+ network: BSNeoXConstants_1.BSNeoXConstants.TESTNET_NETWORK_IDS.includes(this._network.id) ? 'testnet' : 'mainnet',
182
+ cursor: nextCursor,
183
+ pageLimit: (_b = params.pageSize) !== null && _b !== void 0 ? _b : 50,
184
+ });
185
+ return yield this._transformFullTransactionsByAddressResponse(response);
186
+ });
187
+ }
188
+ exportFullTransactionsByAddress(params) {
189
+ return __awaiter(this, void 0, void 0, function* () {
190
+ this._validateFullTransactionsByAddressParams(params);
191
+ return yield dora_ts_1.api.NeoXREST.exportFullTransactionsByAddress({
192
+ address: params.address,
193
+ timestampFrom: params.dateFrom,
194
+ timestampTo: params.dateTo,
195
+ network: BSNeoXConstants_1.BSNeoXConstants.TESTNET_NETWORK_IDS.includes(this._network.id) ? 'testnet' : 'mainnet',
196
+ });
197
+ });
198
+ }
199
+ getContract(contractHash) {
200
+ return __awaiter(this, void 0, void 0, function* () {
201
+ try {
202
+ const client = BlockscoutBDSNeoX.getClient(this._network);
203
+ const { data } = yield client.get(`/smart-contracts/${contractHash}`);
204
+ if (!data || 'message' in data) {
205
+ throw new Error('Contract not found');
206
+ }
207
+ const methods = [];
208
+ data.abi.forEach(abi => {
209
+ var _a, _b;
210
+ if (abi.type !== 'function')
211
+ return;
212
+ const parameters = (_a = abi.inputs) === null || _a === void 0 ? void 0 : _a.map(param => ({
213
+ name: param.name,
214
+ type: param.type,
215
+ }));
216
+ methods.push({
217
+ name: (_b = abi.name) !== null && _b !== void 0 ? _b : '',
218
+ parameters: parameters !== null && parameters !== void 0 ? parameters : [],
219
+ });
220
+ });
221
+ return {
222
+ hash: contractHash,
223
+ name: data.name,
224
+ methods,
225
+ };
226
+ }
227
+ catch (error) {
228
+ throw new Error('Contract not found or not supported');
229
+ }
230
+ });
231
+ }
232
+ getTokenInfo(tokenHash) {
233
+ return __awaiter(this, void 0, void 0, function* () {
234
+ const normalizedHash = (0, blockchain_service_1.normalizeHash)(tokenHash);
235
+ const nativeAsset = BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET;
236
+ if (nativeAsset.hash === normalizedHash) {
237
+ return nativeAsset;
238
+ }
239
+ if (this._tokenCache.has(tokenHash)) {
240
+ return this._tokenCache.get(tokenHash);
241
+ }
242
+ const client = BlockscoutBDSNeoX.getClient(this._network);
243
+ const { data } = yield client.get(`/tokens/${tokenHash}`);
244
+ if (!data || 'message' in data) {
245
+ throw new Error('Token not found');
246
+ }
247
+ if (data.type !== 'ERC-20') {
248
+ throw new Error('Token is not an ERC-20 token');
249
+ }
250
+ return {
251
+ decimals: data.decimals ? parseInt(data.decimals) : bs_ethereum_1.BSEthereumConstants.DEFAULT_DECIMALS,
252
+ hash: tokenHash,
253
+ name: data.name,
254
+ symbol: data.symbol,
255
+ };
256
+ });
257
+ }
258
+ getBalance(address) {
259
+ return __awaiter(this, void 0, void 0, function* () {
260
+ const client = BlockscoutBDSNeoX.getClient(this._network);
261
+ const { data: nativeBalance } = yield client.get(`/addresses/${address}`);
262
+ if (!nativeBalance || 'message' in nativeBalance) {
263
+ throw new Error('Native balance not found');
264
+ }
265
+ const nativeToken = BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET;
266
+ const balances = [
267
+ {
268
+ amount: ethers_1.ethers.utils.formatUnits(nativeBalance.coin_balance, nativeToken.decimals),
269
+ token: nativeToken,
270
+ },
271
+ ];
272
+ const { data: erc20Balances } = yield client.get(`/addresses/${address}/token-balances`);
273
+ if (!erc20Balances || 'message' in erc20Balances) {
274
+ throw new Error('ERC20 balance not found');
275
+ }
276
+ erc20Balances.forEach(balance => {
277
+ try {
278
+ if (balance.token.type !== 'ERC-20') {
279
+ return;
280
+ }
281
+ const token = {
282
+ decimals: balance.token.decimals ? parseInt(balance.token.decimals) : bs_ethereum_1.BSEthereumConstants.DEFAULT_DECIMALS,
283
+ hash: (0, blockchain_service_1.normalizeHash)(balance.token.address),
284
+ name: balance.token.symbol,
285
+ symbol: balance.token.symbol,
286
+ };
287
+ balances.push({
288
+ amount: ethers_1.ethers.utils.formatUnits(balance.value, token.decimals),
289
+ token,
290
+ });
291
+ }
292
+ catch (_a) {
293
+ /* empty */
294
+ }
295
+ });
296
+ return balances;
297
+ });
298
+ }
299
+ getBlockHeight() {
300
+ return __awaiter(this, void 0, void 0, function* () {
301
+ const client = BlockscoutBDSNeoX.getClient(this._network);
302
+ const { data } = yield client.get('/blocks');
303
+ if (!data || 'message' in data) {
304
+ throw new Error('Block not found');
305
+ }
306
+ return data.items[0].height;
307
+ });
308
+ }
309
+ }
310
+ exports.BlockscoutBDSNeoX = BlockscoutBDSNeoX;
311
+ BlockscoutBDSNeoX.BASE_URL_BY_CHAIN_ID = {
312
+ '47763': `${blockchain_service_1.BSCommonConstants.DORA_URL}/api/neox/mainnet`,
313
+ '12227332': 'https://dora-stage.coz.io/api/neox/testnet',
314
+ };
@@ -0,0 +1,8 @@
1
+ import { ExchangeDataService, FlamingoForthewinEDS, GetTokenPriceHistoryParams, GetTokenPricesParams, Network, TokenPricesResponse } from '@cityofzion/blockchain-service';
2
+ import { BSNeoXNetworkId } from '../../constants/BSNeoXConstants';
3
+ export declare class FlamingoForthewinEDSNeoX extends FlamingoForthewinEDS implements ExchangeDataService {
4
+ #private;
5
+ constructor(network: Network<BSNeoXNetworkId>);
6
+ getTokenPrices({ tokens }: GetTokenPricesParams): Promise<TokenPricesResponse[]>;
7
+ getTokenPriceHistory(params: GetTokenPriceHistoryParams): Promise<import("@cityofzion/blockchain-service").TokenPricesHistoryResponse[]>;
8
+ }
@@ -0,0 +1,68 @@
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 __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
12
+ if (kind === "m") throw new TypeError("Private method is not writable");
13
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
14
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
15
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
16
+ };
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
+ var _FlamingoForthewinEDSNeoX_instances, _FlamingoForthewinEDSNeoX_network, _FlamingoForthewinEDSNeoX_validate;
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.FlamingoForthewinEDSNeoX = void 0;
25
+ const blockchain_service_1 = require("@cityofzion/blockchain-service");
26
+ const BSNeoXConstants_1 = require("../../constants/BSNeoXConstants");
27
+ class FlamingoForthewinEDSNeoX extends blockchain_service_1.FlamingoForthewinEDS {
28
+ constructor(network) {
29
+ super();
30
+ _FlamingoForthewinEDSNeoX_instances.add(this);
31
+ _FlamingoForthewinEDSNeoX_network.set(this, void 0);
32
+ __classPrivateFieldSet(this, _FlamingoForthewinEDSNeoX_network, network, "f");
33
+ }
34
+ getTokenPrices({ tokens }) {
35
+ const _super = Object.create(null, {
36
+ getTokenPrices: { get: () => super.getTokenPrices }
37
+ });
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ __classPrivateFieldGet(this, _FlamingoForthewinEDSNeoX_instances, "m", _FlamingoForthewinEDSNeoX_validate).call(this);
40
+ const gasToken = tokens.find(({ symbol }) => symbol === 'GAS');
41
+ const neoToken = tokens.find(({ symbol }) => symbol === 'NEO');
42
+ if (!gasToken && !neoToken)
43
+ return [];
44
+ return yield _super.getTokenPrices.call(this, {
45
+ tokens: tokens.filter(({ symbol }) => symbol === 'GAS' || symbol === 'NEO'),
46
+ });
47
+ });
48
+ }
49
+ getTokenPriceHistory(params) {
50
+ const _super = Object.create(null, {
51
+ getTokenPriceHistory: { get: () => super.getTokenPriceHistory }
52
+ });
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ __classPrivateFieldGet(this, _FlamingoForthewinEDSNeoX_instances, "m", _FlamingoForthewinEDSNeoX_validate).call(this);
55
+ const { symbol } = params.token;
56
+ if (symbol !== 'GAS' && symbol !== 'NEO')
57
+ throw new Error('Invalid token, it should be GAS or NEO');
58
+ return yield _super.getTokenPriceHistory.call(this, params);
59
+ });
60
+ }
61
+ }
62
+ exports.FlamingoForthewinEDSNeoX = FlamingoForthewinEDSNeoX;
63
+ _FlamingoForthewinEDSNeoX_network = new WeakMap(), _FlamingoForthewinEDSNeoX_instances = new WeakSet(), _FlamingoForthewinEDSNeoX_validate = function _FlamingoForthewinEDSNeoX_validate() {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ if (!BSNeoXConstants_1.BSNeoXConstants.MAINNET_NETWORK_IDS.includes(__classPrivateFieldGet(this, _FlamingoForthewinEDSNeoX_network, "f").id))
66
+ throw new Error('Exchange is only available on Neo X Mainnet');
67
+ });
68
+ };
@@ -0,0 +1,6 @@
1
+ import { Network } from '@cityofzion/blockchain-service';
2
+ import { BlockscoutESEthereum } from '@cityofzion/bs-ethereum';
3
+ import { BSNeoXNetworkId } from '../../constants/BSNeoXConstants';
4
+ export declare class BlockscoutESNeoX extends BlockscoutESEthereum<BSNeoXNetworkId> {
5
+ constructor(network: Network<BSNeoXNetworkId>);
6
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlockscoutESNeoX = void 0;
4
+ const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
5
+ class BlockscoutESNeoX extends bs_ethereum_1.BlockscoutESEthereum {
6
+ constructor(network) {
7
+ super(network, {
8
+ '47763': 'https://xexplorer.neo.org',
9
+ '12227332': 'https://xt4scan.ngd.network',
10
+ });
11
+ }
12
+ }
13
+ exports.BlockscoutESNeoX = BlockscoutESNeoX;
@@ -0,0 +1,34 @@
1
+ import { Account, BalanceResponse, Token } from '@cityofzion/blockchain-service';
2
+ import { BSNeoX } from '../../BSNeoX';
3
+ export type TValidateBridgeToNeo3Param<BSName extends string> = {
4
+ account: Account<BSName>;
5
+ neo3Address: string;
6
+ amount: string;
7
+ token: Token;
8
+ balances: BalanceResponse[];
9
+ };
10
+ export type TValidateBridgeToNeo3 = {
11
+ amount: string;
12
+ receiveAmount: string;
13
+ token: Token;
14
+ isGasToken?: boolean;
15
+ isNeoToken?: boolean;
16
+ };
17
+ export type TBridgeToNeo3Param<BSName extends string> = {
18
+ account: Account<BSName>;
19
+ neo3Address: string;
20
+ validateResult: TValidateBridgeToNeo3;
21
+ };
22
+ export type TCalculateMaxAmountToBridgeToNeo3Param<BSName extends string> = {
23
+ account: Account<BSName>;
24
+ neo3Address: string;
25
+ token: Token;
26
+ balances: BalanceResponse[];
27
+ };
28
+ export declare class BridgeNeoXService<BSName extends string> {
29
+ #private;
30
+ constructor(service: BSNeoX<BSName>);
31
+ calculateBridgeToNeo3Fee(params: TBridgeToNeo3Param<BSName>): Promise<string>;
32
+ validateBridgeToNeo3({ amount, balances, token, account, neo3Address, }: TValidateBridgeToNeo3Param<BSName>): Promise<TValidateBridgeToNeo3>;
33
+ calculateMaxAmountToBridgeToNeoX({ account, balances, neo3Address, token, }: TCalculateMaxAmountToBridgeToNeo3Param<BSName>): Promise<any>;
34
+ }
@@ -0,0 +1,177 @@
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 __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
12
+ if (kind === "m") throw new TypeError("Private method is not writable");
13
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
14
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
15
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
16
+ };
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
+ var _BridgeNeoXService_instances, _BridgeNeoXService_service, _BridgeNeoXService_buildBridgeParams;
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.BridgeNeoXService = void 0;
25
+ const blockchain_service_1 = require("@cityofzion/blockchain-service");
26
+ const BSNeoXConstants_1 = require("../../constants/BSNeoXConstants");
27
+ const ethers_1 = require("ethers");
28
+ const neon_js_1 = require("@cityofzion/neon-js");
29
+ const bridge_1 = require("../../assets/abis/bridge");
30
+ const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
31
+ class BridgeNeoXService {
32
+ constructor(service) {
33
+ _BridgeNeoXService_instances.add(this);
34
+ _BridgeNeoXService_service.set(this, void 0);
35
+ __classPrivateFieldSet(this, _BridgeNeoXService_service, service, "f");
36
+ }
37
+ calculateBridgeToNeo3Fee(params) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ try {
40
+ const signer = yield __classPrivateFieldGet(this, _BridgeNeoXService_service, "f").generateSigner(params.account);
41
+ const { gasPrice, approveTransactionParams, bridgeTransactionParam } = yield __classPrivateFieldGet(this, _BridgeNeoXService_instances, "m", _BridgeNeoXService_buildBridgeParams).call(this, params);
42
+ let fee = ethers_1.ethers.utils.parseEther('0');
43
+ if (params.validateResult.isGasToken) {
44
+ const estimated = yield signer.estimateGas(bridgeTransactionParam);
45
+ fee = gasPrice.mul(estimated);
46
+ }
47
+ else {
48
+ let approvedEstimated = ethers_1.ethers.utils.parseEther('0');
49
+ if (approveTransactionParams) {
50
+ approvedEstimated = yield signer.estimateGas(approveTransactionParams);
51
+ }
52
+ // We can't estimate the gas for the bridge transaction because it requires the approve transaction to be done first
53
+ // if not the gas estimation of bridge transaction will fail so we add a fixed value
54
+ const neoBridgeFee = ethers_1.ethers.utils.parseUnits(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_NEO_BRIDGE_TRANSACTION_FEE.toString(), BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.decimals);
55
+ fee = gasPrice.mul(approvedEstimated).add(neoBridgeFee);
56
+ }
57
+ return ethers_1.ethers.utils.formatEther(fee);
58
+ }
59
+ catch (error) {
60
+ throw new blockchain_service_1.BSError(error.message, 'FEE_CALCULATION_ERROR');
61
+ }
62
+ });
63
+ }
64
+ validateBridgeToNeo3({ amount, balances, token, account, neo3Address, }) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ const normalizedSelectedToken = blockchain_service_1.BSTokenHelper.normalizeToken(token);
67
+ const isGasToken = normalizedSelectedToken.hash === BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.hash;
68
+ const isNeoToken = normalizedSelectedToken.hash === BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.hash;
69
+ if (!isGasToken && !isNeoToken) {
70
+ throw new blockchain_service_1.BSError('Only GAS and NEO tokens are supported for bridging', 'UNSUPPORTED_TOKEN');
71
+ }
72
+ const gasBalance = balances.find(balance => blockchain_service_1.BSTokenHelper.normalizeHash(balance.token.hash) === BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.hash);
73
+ if (!gasBalance) {
74
+ throw new blockchain_service_1.BSError('GAS is necessary to bridge', 'GAS_BALANCE_NOT_FOUND');
75
+ }
76
+ const amountNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(amount);
77
+ const gasBalanceNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(gasBalance.amount);
78
+ if (isGasToken) {
79
+ const receiveAmount = amountNumber.minus(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_GAS_FEE).toString();
80
+ if (amountNumber.isLessThan(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_MIN_AMOUNT + BSNeoXConstants_1.BSNeoXConstants.BRIDGE_GAS_FEE)) {
81
+ throw new blockchain_service_1.BSError('Amount is less than the minimum amount plus bridge fee', 'AMOUNT_TOO_LOW');
82
+ }
83
+ if (amountNumber.isGreaterThan(gasBalanceNumber)) {
84
+ throw new blockchain_service_1.BSError('Amount is greater than your balance', 'INSUFFICIENT_GAS_BALANCE');
85
+ }
86
+ const validateResult = { receiveAmount, token, isGasToken, isNeoToken, amount };
87
+ const fee = yield this.calculateBridgeToNeo3Fee({
88
+ account,
89
+ neo3Address,
90
+ validateResult,
91
+ });
92
+ if (amountNumber.plus(fee).isGreaterThan(gasBalanceNumber)) {
93
+ throw new blockchain_service_1.BSError('Amount is greater than your balance plus fee', 'INSUFFICIENT_GAS_BALANCE_FEE');
94
+ }
95
+ return validateResult;
96
+ }
97
+ const neoBalance = balances.find(balance => blockchain_service_1.BSTokenHelper.normalizeHash(balance.token.hash) === BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.hash);
98
+ if (!neoBalance) {
99
+ throw new blockchain_service_1.BSError('NEO balance not found', 'NEO_BALANCE_NOT_FOUND');
100
+ }
101
+ if (amountNumber.isLessThan(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_MIN_AMOUNT)) {
102
+ throw new blockchain_service_1.BSError('Amount is less than the minimum amount', 'AMOUNT_TOO_LOW');
103
+ }
104
+ if (amountNumber.isGreaterThan(neoBalance.amount)) {
105
+ throw new blockchain_service_1.BSError('Amount is greater than your balance', 'INSUFFICIENT_NEO_BALANCE');
106
+ }
107
+ const minGasBalanceNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_GAS_FEE);
108
+ if (gasBalanceNumber.isLessThan(minGasBalanceNumber)) {
109
+ throw new blockchain_service_1.BSError('GAS balance is less than bridge fee', 'INSUFFICIENT_GAS_BALANCE_BRIDGE_FEE');
110
+ }
111
+ const receiveAmount = amount;
112
+ const validateResult = { receiveAmount, token, isNeoToken, amount };
113
+ const fee = yield this.calculateBridgeToNeo3Fee({
114
+ account,
115
+ neo3Address,
116
+ validateResult,
117
+ });
118
+ if (minGasBalanceNumber.plus(blockchain_service_1.BSBigNumberHelper.fromNumber(fee)).isGreaterThan(gasBalanceNumber)) {
119
+ throw new blockchain_service_1.BSError('GAS balance is less than fees', 'INSUFFICIENT_GAS_BALANCE_FEES');
120
+ }
121
+ return validateResult;
122
+ });
123
+ }
124
+ calculateMaxAmountToBridgeToNeoX({ account, balances, neo3Address, token, }) {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const normalizedSelectedToken = blockchain_service_1.BSTokenHelper.normalizeToken(token);
127
+ const selectedTokenBalance = balances.find(balance => blockchain_service_1.BSTokenHelper.normalizeHash(balance.token.hash) === normalizedSelectedToken.hash);
128
+ if (!selectedTokenBalance) {
129
+ throw new blockchain_service_1.BSError('Token balance not found', 'TOKEN_BALANCE_NOT_FOUND');
130
+ }
131
+ const selectedTokenBalanceNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(selectedTokenBalance.amount);
132
+ const amount = blockchain_service_1.BSBigNumberHelper.fromNumber(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_MIN_AMOUNT).toString();
133
+ const receiveAmount = selectedTokenBalanceNumber.minus(amount).toString();
134
+ const isGasToken = normalizedSelectedToken.hash === BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.hash;
135
+ const isNeoToken = normalizedSelectedToken.hash === BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.hash;
136
+ const fee = yield this.calculateBridgeToNeo3Fee({
137
+ account,
138
+ neo3Address,
139
+ validateResult: { receiveAmount, token, isGasToken, isNeoToken, amount },
140
+ });
141
+ const maxAmount = selectedTokenBalanceNumber.minus(blockchain_service_1.BSBigNumberHelper.fromNumber(fee)).toString();
142
+ return maxAmount;
143
+ });
144
+ }
145
+ }
146
+ exports.BridgeNeoXService = BridgeNeoXService;
147
+ _BridgeNeoXService_service = new WeakMap(), _BridgeNeoXService_instances = new WeakSet(), _BridgeNeoXService_buildBridgeParams = function _BridgeNeoXService_buildBridgeParams(params) {
148
+ return __awaiter(this, void 0, void 0, function* () {
149
+ const { validateResult } = params;
150
+ const provider = new ethers_1.ethers.providers.JsonRpcProvider(__classPrivateFieldGet(this, _BridgeNeoXService_service, "f").network.url);
151
+ const gasPrice = yield provider.getGasPrice();
152
+ let bridgeTransactionParam;
153
+ let approveTransactionParams;
154
+ const to = '0x' + neon_js_1.wallet.getScriptHashFromAddress(params.neo3Address);
155
+ const bridgeContract = new ethers_1.ethers.Contract(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_SCRIPT_HASH, bridge_1.BRIDGE_ABI);
156
+ const bridgeFee = ethers_1.ethers.utils.parseUnits(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_GAS_FEE.toString(), BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.decimals);
157
+ if (validateResult.isGasToken) {
158
+ const populatedTransaction = yield bridgeContract.populateTransaction.withdrawNative(to, bridgeFee);
159
+ bridgeTransactionParam = Object.assign(Object.assign({}, populatedTransaction), { value: ethers_1.ethers.utils.parseUnits(validateResult.amount, params.validateResult.token.decimals), type: 2 });
160
+ }
161
+ else {
162
+ const amount = ethers_1.ethers.utils.parseUnits(blockchain_service_1.BSBigNumberHelper.format(params.validateResult.amount, 0), params.validateResult.token.decimals);
163
+ const erc20Contract = new ethers_1.ethers.Contract(BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.hash, bs_ethereum_1.ERC20_ABI);
164
+ const populatedApproveTransaction = yield erc20Contract.populateTransaction.approve(BSNeoXConstants_1.BSNeoXConstants.BRIDGE_SCRIPT_HASH, amount);
165
+ approveTransactionParams = Object.assign(Object.assign({}, populatedApproveTransaction), { type: 2 });
166
+ const populatedTransaction = yield bridgeContract.populateTransaction.withdrawToken(validateResult.token.hash, to,
167
+ // We are using 0 as the decimals because the NEO token in Neo3 has 0 decimals
168
+ amount);
169
+ bridgeTransactionParam = Object.assign(Object.assign({}, populatedTransaction), { type: 2, value: bridgeFee });
170
+ }
171
+ return {
172
+ bridgeTransactionParam,
173
+ approveTransactionParams,
174
+ gasPrice,
175
+ };
176
+ });
177
+ };
@@ -0,0 +1,6 @@
1
+ import { GhostMarketNDSEthereum } from '@cityofzion/bs-ethereum';
2
+ import { BSNeoXNetworkId } from '../../constants/BSNeoXConstants';
3
+ import { Network } from '@cityofzion/blockchain-service';
4
+ export declare class GhostMarketNDSNeoX extends GhostMarketNDSEthereum<BSNeoXNetworkId> {
5
+ constructor(network: Network<BSNeoXNetworkId>);
6
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GhostMarketNDSNeoX = void 0;
4
+ const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
5
+ class GhostMarketNDSNeoX extends bs_ethereum_1.GhostMarketNDSEthereum {
6
+ constructor(network) {
7
+ super(network, {
8
+ '47763': 'neox',
9
+ });
10
+ }
11
+ }
12
+ exports.GhostMarketNDSNeoX = GhostMarketNDSNeoX;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cityofzion/bs-neox",
3
- "version": "0.0.1",
3
+ "version": "1.1.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": "https://github.com/CityOfZion/blockchain-services",
@@ -9,21 +9,15 @@
9
9
  "files": [
10
10
  "/dist"
11
11
  ],
12
- "scripts": {
13
- "build": "tsc --project tsconfig.build.json",
14
- "test": "jest --config jest.config.ts",
15
- "lint": "eslint .",
16
- "format": "eslint --fix"
17
- },
18
12
  "dependencies": {
19
- "@cityofzion/blockchain-service": "workspace:*",
20
- "@cityofzion/bs-ethereum": "workspace:*",
21
13
  "@cityofzion/dora-ts": "0.5.0",
22
14
  "@cityofzion/neon-js": "5.7.0",
23
15
  "@ledgerhq/hw-transport": "~6.30.5",
24
16
  "axios": "1.8.2",
25
17
  "ethers": "5.7.2",
26
- "date-fns": "~4.1.0"
18
+ "date-fns": "~4.1.0",
19
+ "@cityofzion/bs-ethereum": "2.12.0",
20
+ "@cityofzion/blockchain-service": "1.18.0"
27
21
  },
28
22
  "devDependencies": {
29
23
  "@ledgerhq/hw-transport-node-hid": "~6.28.5",
@@ -36,5 +30,11 @@
36
30
  "ts-jest": "29.1.1",
37
31
  "ts-node": "10.9.1",
38
32
  "typescript": "4.9.5"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc --project tsconfig.build.json",
36
+ "test": "jest --config jest.config.ts",
37
+ "lint": "eslint .",
38
+ "format": "eslint --fix"
39
39
  }
40
- }
40
+ }