@cityofzion/bs-neox 1.1.2 → 1.2.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/dist/BSNeoX.d.ts +3 -2
- package/dist/BSNeoX.js +2 -0
- package/dist/assets/abis/bridge.d.ts +95 -0
- package/dist/assets/abis/bridge.js +1880 -0
- package/dist/constants/BSNeoXConstants.d.ts +3 -2
- package/dist/constants/BSNeoXConstants.js +30 -13
- package/dist/helpers/BSNeoXHelper.d.ts +5 -0
- package/dist/helpers/BSNeoXHelper.js +10 -0
- package/dist/services/blockchain-data/BlockscoutBDSNeoX.js +14 -11
- package/dist/services/neo3neoXBridge/Neo3NeoXBridgeService.d.ts +16 -0
- package/dist/services/neo3neoXBridge/Neo3NeoXBridgeService.js +314 -0
- package/package.json +3 -3
|
@@ -2,12 +2,13 @@ import { Network, NetworkId, Token } from '@cityofzion/blockchain-service';
|
|
|
2
2
|
export type BSNeoXNetworkId = NetworkId<'47763' | '12227332'>;
|
|
3
3
|
export declare class BSNeoXConstants {
|
|
4
4
|
static NATIVE_ASSET: Token;
|
|
5
|
+
static NEO_TOKEN: Token;
|
|
5
6
|
static RPC_LIST_BY_NETWORK_ID: Record<BSNeoXNetworkId, string[]>;
|
|
6
7
|
static MAINNET_NETWORK_IDS: BSNeoXNetworkId[];
|
|
7
8
|
static TESTNET_NETWORK_IDS: BSNeoXNetworkId[];
|
|
8
9
|
static ALL_NETWORK_IDS: BSNeoXNetworkId[];
|
|
9
|
-
static
|
|
10
|
-
static
|
|
10
|
+
static MAINNET_NETWORKS: Network<BSNeoXNetworkId>[];
|
|
11
|
+
static TESTNET_NETWORKS: Network<BSNeoXNetworkId>[];
|
|
11
12
|
static ALL_NETWORK: Network<BSNeoXNetworkId>[];
|
|
12
13
|
static DEFAULT_NETWORK: Network<BSNeoXNetworkId>;
|
|
13
14
|
}
|
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.BSNeoXConstants = void 0;
|
|
5
|
+
const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
|
|
5
6
|
class BSNeoXConstants {
|
|
6
7
|
}
|
|
7
8
|
exports.BSNeoXConstants = BSNeoXConstants;
|
|
8
9
|
_a = BSNeoXConstants;
|
|
9
|
-
BSNeoXConstants.NATIVE_ASSET = {
|
|
10
|
+
BSNeoXConstants.NATIVE_ASSET = bs_ethereum_1.BSEthereumTokenHelper.normalizeToken({
|
|
11
|
+
symbol: 'GAS',
|
|
12
|
+
name: 'GAS',
|
|
13
|
+
decimals: 18,
|
|
14
|
+
hash: '0x',
|
|
15
|
+
});
|
|
16
|
+
BSNeoXConstants.NEO_TOKEN = bs_ethereum_1.BSEthereumTokenHelper.normalizeToken({
|
|
17
|
+
name: 'NEO',
|
|
18
|
+
symbol: 'NEO',
|
|
19
|
+
decimals: 18,
|
|
20
|
+
hash: '0xc28736dc83f4fd43d6fb832Fd93c3eE7bB26828f',
|
|
21
|
+
});
|
|
10
22
|
BSNeoXConstants.RPC_LIST_BY_NETWORK_ID = {
|
|
11
23
|
'47763': ['https://mainnet-1.rpc.banelabs.org'],
|
|
12
24
|
'12227332': ['https://neoxt4seed1.ngd.network'],
|
|
@@ -14,15 +26,20 @@ BSNeoXConstants.RPC_LIST_BY_NETWORK_ID = {
|
|
|
14
26
|
BSNeoXConstants.MAINNET_NETWORK_IDS = ['47763'];
|
|
15
27
|
BSNeoXConstants.TESTNET_NETWORK_IDS = ['12227332'];
|
|
16
28
|
BSNeoXConstants.ALL_NETWORK_IDS = [..._a.MAINNET_NETWORK_IDS, ..._a.TESTNET_NETWORK_IDS];
|
|
17
|
-
BSNeoXConstants.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
BSNeoXConstants.MAINNET_NETWORKS = [
|
|
30
|
+
{
|
|
31
|
+
id: '47763',
|
|
32
|
+
name: 'NeoX Mainnet',
|
|
33
|
+
url: _a.RPC_LIST_BY_NETWORK_ID['47763'][0],
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
BSNeoXConstants.TESTNET_NETWORKS = [
|
|
37
|
+
{
|
|
38
|
+
id: '12227332',
|
|
39
|
+
name: 'NeoX Testnet',
|
|
40
|
+
url: _a.RPC_LIST_BY_NETWORK_ID['12227332'][0],
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
BSNeoXConstants.ALL_NETWORK = [..._a.MAINNET_NETWORKS, ..._a.TESTNET_NETWORKS];
|
|
44
|
+
// If tou change this, make sure to update the tests accordingly
|
|
45
|
+
BSNeoXConstants.DEFAULT_NETWORK = _a.MAINNET_NETWORKS[0];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BSNeoXHelper = void 0;
|
|
4
|
+
const BSNeoXConstants_1 = require("../constants/BSNeoXConstants");
|
|
5
|
+
class BSNeoXHelper {
|
|
6
|
+
static isMainnet(network) {
|
|
7
|
+
return BSNeoXConstants_1.BSNeoXConstants.MAINNET_NETWORK_IDS.includes(network.id);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.BSNeoXHelper = BSNeoXHelper;
|
|
@@ -74,12 +74,12 @@ class BlockscoutBDSNeoX extends bs_ethereum_1.DoraBDSEthereum {
|
|
|
74
74
|
to: tokenTransfer.to.hash,
|
|
75
75
|
type: 'token',
|
|
76
76
|
contractHash: tokenTransfer.token.address,
|
|
77
|
-
token: {
|
|
77
|
+
token: bs_ethereum_1.BSEthereumTokenHelper.normalizeToken({
|
|
78
78
|
symbol: tokenTransfer.token.symbol,
|
|
79
79
|
name: tokenTransfer.token.name,
|
|
80
80
|
hash: tokenTransfer.token.address,
|
|
81
81
|
decimals: Number(tokenTransfer.total.decimals),
|
|
82
|
-
},
|
|
82
|
+
}),
|
|
83
83
|
});
|
|
84
84
|
continue;
|
|
85
85
|
}
|
|
@@ -145,7 +145,7 @@ class BlockscoutBDSNeoX extends bs_ethereum_1.DoraBDSEthereum {
|
|
|
145
145
|
from: item.from.hash,
|
|
146
146
|
to,
|
|
147
147
|
type: 'token',
|
|
148
|
-
contractHash:
|
|
148
|
+
contractHash: token.hash,
|
|
149
149
|
token,
|
|
150
150
|
});
|
|
151
151
|
}
|
|
@@ -231,13 +231,14 @@ class BlockscoutBDSNeoX extends bs_ethereum_1.DoraBDSEthereum {
|
|
|
231
231
|
}
|
|
232
232
|
getTokenInfo(tokenHash) {
|
|
233
233
|
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
-
const normalizedHash =
|
|
234
|
+
const normalizedHash = bs_ethereum_1.BSEthereumTokenHelper.normalizeHash(tokenHash);
|
|
235
235
|
const nativeAsset = BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET;
|
|
236
236
|
if (nativeAsset.hash === normalizedHash) {
|
|
237
237
|
return nativeAsset;
|
|
238
238
|
}
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
const cachedToken = this._tokenCache.get(tokenHash);
|
|
240
|
+
if (cachedToken) {
|
|
241
|
+
return cachedToken;
|
|
241
242
|
}
|
|
242
243
|
const client = BlockscoutBDSNeoX.getClient(this._network);
|
|
243
244
|
const { data } = yield client.get(`/tokens/${tokenHash}`);
|
|
@@ -247,12 +248,14 @@ class BlockscoutBDSNeoX extends bs_ethereum_1.DoraBDSEthereum {
|
|
|
247
248
|
if (data.type !== 'ERC-20') {
|
|
248
249
|
throw new Error('Token is not an ERC-20 token');
|
|
249
250
|
}
|
|
250
|
-
|
|
251
|
+
const token = bs_ethereum_1.BSEthereumTokenHelper.normalizeToken({
|
|
251
252
|
decimals: data.decimals ? parseInt(data.decimals) : bs_ethereum_1.BSEthereumConstants.DEFAULT_DECIMALS,
|
|
252
253
|
hash: tokenHash,
|
|
253
254
|
name: data.name,
|
|
254
255
|
symbol: data.symbol,
|
|
255
|
-
};
|
|
256
|
+
});
|
|
257
|
+
this._tokenCache.set(tokenHash, token);
|
|
258
|
+
return token;
|
|
256
259
|
});
|
|
257
260
|
}
|
|
258
261
|
getBalance(address) {
|
|
@@ -278,12 +281,12 @@ class BlockscoutBDSNeoX extends bs_ethereum_1.DoraBDSEthereum {
|
|
|
278
281
|
if (balance.token.type !== 'ERC-20') {
|
|
279
282
|
return;
|
|
280
283
|
}
|
|
281
|
-
const token = {
|
|
284
|
+
const token = bs_ethereum_1.BSEthereumTokenHelper.normalizeToken({
|
|
282
285
|
decimals: balance.token.decimals ? parseInt(balance.token.decimals) : bs_ethereum_1.BSEthereumConstants.DEFAULT_DECIMALS,
|
|
283
|
-
hash:
|
|
286
|
+
hash: balance.token.address,
|
|
284
287
|
name: balance.token.symbol,
|
|
285
288
|
symbol: balance.token.symbol,
|
|
286
|
-
};
|
|
289
|
+
});
|
|
287
290
|
balances.push({
|
|
288
291
|
amount: ethers_1.ethers.utils.formatUnits(balance.value, token.decimals),
|
|
289
292
|
token,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { INeo3NeoXBridgeService, TNeo3NeoXBridgeServiceBridgeParam, TNeo3NeoXBridgeServiceCalculateMaxAmountParams, TNeo3NeoXBridgeServiceValidatedInputs, TNeo3NeoXBridgeServiceValidateInputParams, TNeo3NeoXBridgeServiceWaitParams } from '@cityofzion/blockchain-service';
|
|
2
|
+
import { BSNeoX } from '../../BSNeoX';
|
|
3
|
+
export declare class Neo3NeoXBridgeService<BSName extends string> implements INeo3NeoXBridgeService<BSName> {
|
|
4
|
+
#private;
|
|
5
|
+
readonly BRIDGE_SCRIPT_HASH = "0x1212000000000000000000000000000000000004";
|
|
6
|
+
readonly BRIDGE_GAS_FEE = 0.1;
|
|
7
|
+
readonly BRIDGE_MIN_AMOUNT = 1;
|
|
8
|
+
readonly BRIDGE_NEO_BRIDGE_TRANSACTION_FEE = 0.008;
|
|
9
|
+
readonly BRIDGE_NEO3_NEO_TOKEN_HASH = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5";
|
|
10
|
+
constructor(service: BSNeoX<BSName>);
|
|
11
|
+
calculateMaxAmount({ account, balances, receiverAddress, token, }: TNeo3NeoXBridgeServiceCalculateMaxAmountParams<BSName>): Promise<string>;
|
|
12
|
+
calculateFee(params: TNeo3NeoXBridgeServiceBridgeParam<BSName>): Promise<string>;
|
|
13
|
+
bridge(params: TNeo3NeoXBridgeServiceBridgeParam<BSName>): Promise<string>;
|
|
14
|
+
validateInputs(params: TNeo3NeoXBridgeServiceValidateInputParams<BSName>): Promise<TNeo3NeoXBridgeServiceValidatedInputs>;
|
|
15
|
+
wait(params: TNeo3NeoXBridgeServiceWaitParams): Promise<boolean>;
|
|
16
|
+
}
|
|
@@ -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 __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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
var _Neo3NeoXBridgeService_instances, _Neo3NeoXBridgeService_service, _Neo3NeoXBridgeService_buildGasTransactionParams, _Neo3NeoXBridgeService_buildNeoTransactionParams, _Neo3NeoXBridgeService_buildTransactionParams, _Neo3NeoXBridgeService_validateGas, _Neo3NeoXBridgeService_validateNeo;
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.Neo3NeoXBridgeService = void 0;
|
|
28
|
+
const blockchain_service_1 = require("@cityofzion/blockchain-service");
|
|
29
|
+
const BSNeoXConstants_1 = require("../../constants/BSNeoXConstants");
|
|
30
|
+
const ethers_1 = require("ethers");
|
|
31
|
+
const neon_js_1 = require("@cityofzion/neon-js");
|
|
32
|
+
const bridge_1 = require("../../assets/abis/bridge");
|
|
33
|
+
const bs_ethereum_1 = require("@cityofzion/bs-ethereum");
|
|
34
|
+
const axios_1 = __importDefault(require("axios"));
|
|
35
|
+
const BlockscoutBDSNeoX_1 = require("../blockchain-data/BlockscoutBDSNeoX");
|
|
36
|
+
const BSNeoXHelper_1 = require("../../helpers/BSNeoXHelper");
|
|
37
|
+
class Neo3NeoXBridgeService {
|
|
38
|
+
constructor(service) {
|
|
39
|
+
_Neo3NeoXBridgeService_instances.add(this);
|
|
40
|
+
this.BRIDGE_SCRIPT_HASH = '0x1212000000000000000000000000000000000004';
|
|
41
|
+
this.BRIDGE_GAS_FEE = 0.1;
|
|
42
|
+
this.BRIDGE_MIN_AMOUNT = 1;
|
|
43
|
+
this.BRIDGE_NEO_BRIDGE_TRANSACTION_FEE = 0.008;
|
|
44
|
+
this.BRIDGE_NEO3_NEO_TOKEN_HASH = '0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5';
|
|
45
|
+
_Neo3NeoXBridgeService_service.set(this, void 0);
|
|
46
|
+
__classPrivateFieldSet(this, _Neo3NeoXBridgeService_service, service, "f");
|
|
47
|
+
}
|
|
48
|
+
calculateMaxAmount({ account, balances, receiverAddress, token, }) {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
if (!BSNeoXHelper_1.BSNeoXHelper.isMainnet(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network))
|
|
51
|
+
throw new blockchain_service_1.BSError('Bridging to Neo3 is only supported on mainnet', 'UNSUPPORTED_NETWORK');
|
|
52
|
+
const normalizedSelectedToken = bs_ethereum_1.BSEthereumTokenHelper.normalizeToken(token);
|
|
53
|
+
const selectedTokenBalance = balances.find(balance => bs_ethereum_1.BSEthereumTokenHelper.normalizeHash(balance.token.hash) === normalizedSelectedToken.hash);
|
|
54
|
+
if (!selectedTokenBalance) {
|
|
55
|
+
throw new blockchain_service_1.BSError('Token balance not found', 'TOKEN_BALANCE_NOT_FOUND');
|
|
56
|
+
}
|
|
57
|
+
const amountNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(selectedTokenBalance.amount);
|
|
58
|
+
const receiveAmount = amountNumber.minus(this.BRIDGE_MIN_AMOUNT).toString();
|
|
59
|
+
const amount = amountNumber.toString();
|
|
60
|
+
const fee = yield this.calculateFee({
|
|
61
|
+
account,
|
|
62
|
+
receiverAddress,
|
|
63
|
+
validatedInputs: { receiveAmount, token, amount },
|
|
64
|
+
});
|
|
65
|
+
const maxAmount = amountNumber.minus(fee).toString();
|
|
66
|
+
return maxAmount;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
calculateFee(params) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
if (!BSNeoXHelper_1.BSNeoXHelper.isMainnet(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network))
|
|
72
|
+
throw new blockchain_service_1.BSError('Bridging to Neo3 is only supported on mainnet', 'UNSUPPORTED_NETWORK');
|
|
73
|
+
try {
|
|
74
|
+
const signer = yield __classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").generateSigner(params.account);
|
|
75
|
+
const { gasPrice, approveTransactionParams, bridgeTransactionParam } = yield __classPrivateFieldGet(this, _Neo3NeoXBridgeService_instances, "m", _Neo3NeoXBridgeService_buildTransactionParams).call(this, params);
|
|
76
|
+
let fee = ethers_1.ethers.utils.parseEther('0');
|
|
77
|
+
const isGasToken = blockchain_service_1.BSTokenHelper.predicateByHash(params.validatedInputs.token)(BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET);
|
|
78
|
+
if (isGasToken) {
|
|
79
|
+
const estimated = yield signer.estimateGas(bridgeTransactionParam);
|
|
80
|
+
fee = gasPrice.mul(estimated);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
let approvedEstimated = ethers_1.ethers.utils.parseEther('0');
|
|
84
|
+
if (approveTransactionParams) {
|
|
85
|
+
approvedEstimated = yield signer.estimateGas(approveTransactionParams);
|
|
86
|
+
}
|
|
87
|
+
// We can't estimate the gas for the bridge transaction because it requires the approve transaction to be done first
|
|
88
|
+
// if not the gas estimation of bridge transaction will fail so we add a fixed value
|
|
89
|
+
const neoBridgeFee = ethers_1.ethers.utils.parseUnits(this.BRIDGE_NEO_BRIDGE_TRANSACTION_FEE.toString(), BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.decimals);
|
|
90
|
+
fee = gasPrice.mul(approvedEstimated).add(neoBridgeFee);
|
|
91
|
+
}
|
|
92
|
+
return ethers_1.ethers.utils.formatEther(fee);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw new blockchain_service_1.BSError(error.message, 'FEE_CALCULATION_ERROR');
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
bridge(params) {
|
|
100
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
if (!BSNeoXHelper_1.BSNeoXHelper.isMainnet(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network))
|
|
102
|
+
throw new blockchain_service_1.BSError('Bridging to Neo3 is only supported on mainnet', 'UNSUPPORTED_NETWORK');
|
|
103
|
+
const { account } = params;
|
|
104
|
+
const signer = yield __classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").generateSigner(account);
|
|
105
|
+
const { approveTransactionParams, bridgeTransactionParam, gasPrice } = yield __classPrivateFieldGet(this, _Neo3NeoXBridgeService_instances, "m", _Neo3NeoXBridgeService_buildTransactionParams).call(this, params);
|
|
106
|
+
if (approveTransactionParams) {
|
|
107
|
+
const approveTransaction = yield signer.sendTransaction(approveTransactionParams);
|
|
108
|
+
yield approveTransaction.wait();
|
|
109
|
+
}
|
|
110
|
+
let gasLimit;
|
|
111
|
+
try {
|
|
112
|
+
gasLimit = yield signer.estimateGas(bridgeTransactionParam);
|
|
113
|
+
}
|
|
114
|
+
catch (_a) {
|
|
115
|
+
gasLimit = bs_ethereum_1.BSEthereumConstants.DEFAULT_GAS_LIMIT;
|
|
116
|
+
}
|
|
117
|
+
const transaction = yield signer.sendTransaction(Object.assign(Object.assign({}, bridgeTransactionParam), { gasLimit, maxPriorityFeePerGas: gasPrice, maxFeePerGas: gasPrice }));
|
|
118
|
+
return transaction.hash;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
validateInputs(params) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
if (!BSNeoXHelper_1.BSNeoXHelper.isMainnet(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network))
|
|
124
|
+
throw new blockchain_service_1.BSError('Bridging to Neo3 is only supported on mainnet', 'UNSUPPORTED_NETWORK');
|
|
125
|
+
const normalizedSelectedToken = bs_ethereum_1.BSEthereumTokenHelper.normalizeToken(params.token);
|
|
126
|
+
const isGasToken = normalizedSelectedToken.hash === BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.hash;
|
|
127
|
+
const isNeoToken = normalizedSelectedToken.hash === BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.hash;
|
|
128
|
+
if (isGasToken) {
|
|
129
|
+
return __classPrivateFieldGet(this, _Neo3NeoXBridgeService_instances, "m", _Neo3NeoXBridgeService_validateGas).call(this, params);
|
|
130
|
+
}
|
|
131
|
+
else if (isNeoToken) {
|
|
132
|
+
return __classPrivateFieldGet(this, _Neo3NeoXBridgeService_instances, "m", _Neo3NeoXBridgeService_validateNeo).call(this, params);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
throw new blockchain_service_1.BSError('Only GAS and NEO tokens are supported for bridging', 'UNSUPPORTED_TOKEN');
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
wait(params) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
if (!BSNeoXHelper_1.BSNeoXHelper.isMainnet(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network))
|
|
142
|
+
throw new blockchain_service_1.BSError('Bridging to Neo3 is only supported on mainnet', 'UNSUPPORTED_NETWORK');
|
|
143
|
+
try {
|
|
144
|
+
const { transactionHash, validatedInputs } = params;
|
|
145
|
+
let nonce;
|
|
146
|
+
const log = yield blockchain_service_1.BSUtilsHelper.retry(() => __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
const client = BlockscoutBDSNeoX_1.BlockscoutBDSNeoX.getClient(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network);
|
|
148
|
+
const { data } = yield client.get(`/transactions/${transactionHash}/logs`);
|
|
149
|
+
if (!data.items || data.items.length === 0) {
|
|
150
|
+
throw new blockchain_service_1.BSError('Transaction logs not found', 'LOGS_NOT_FOUND');
|
|
151
|
+
}
|
|
152
|
+
return data;
|
|
153
|
+
}), {
|
|
154
|
+
retries: 10,
|
|
155
|
+
delay: 30000,
|
|
156
|
+
});
|
|
157
|
+
const BridgeInterface = new ethers_1.ethers.utils.Interface(bridge_1.BRIDGE_ABI);
|
|
158
|
+
const isGasToken = blockchain_service_1.BSTokenHelper.predicateByHash(validatedInputs.token)(BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET);
|
|
159
|
+
if (isGasToken) {
|
|
160
|
+
const item = log.items[0];
|
|
161
|
+
const response = BridgeInterface.decodeEventLog('NativeWithdrawal', item.data, item.topics.filter(Boolean));
|
|
162
|
+
nonce = response.nonce.toString();
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const item = log.items[1];
|
|
166
|
+
const response = BridgeInterface.decodeEventLog('TokenWithdrawal', item.data, item.topics.filter(Boolean));
|
|
167
|
+
nonce = response.nonce.toString();
|
|
168
|
+
}
|
|
169
|
+
if (!nonce) {
|
|
170
|
+
throw new Error();
|
|
171
|
+
}
|
|
172
|
+
yield blockchain_service_1.BSUtilsHelper.retry(() => __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
var _a, _b;
|
|
174
|
+
const response = yield axios_1.default.post('https://neofura.ngd.network', {
|
|
175
|
+
jsonrpc: '2.0',
|
|
176
|
+
method: 'GetBridgeTxByNonce',
|
|
177
|
+
params: {
|
|
178
|
+
ContractHash: this.BRIDGE_SCRIPT_HASH,
|
|
179
|
+
TokenHash: isGasToken ? '' : this.BRIDGE_NEO3_NEO_TOKEN_HASH,
|
|
180
|
+
Nonce: Number(nonce),
|
|
181
|
+
},
|
|
182
|
+
id: 1,
|
|
183
|
+
});
|
|
184
|
+
if (!((_a = response.data) === null || _a === void 0 ? void 0 : _a.result.Vmstate) || !((_b = response.data) === null || _b === void 0 ? void 0 : _b.result.txid)) {
|
|
185
|
+
throw new blockchain_service_1.BSError('Transaction not found', 'TRANSACTION_NOT_FOUND');
|
|
186
|
+
}
|
|
187
|
+
if (response.data.result.Vmstate !== 'HALT') {
|
|
188
|
+
throw new blockchain_service_1.BSError('Transaction is not in a valid state', 'INVALID_TRANSACTION_STATE');
|
|
189
|
+
}
|
|
190
|
+
}), {
|
|
191
|
+
retries: 10,
|
|
192
|
+
delay: 30000,
|
|
193
|
+
});
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.Neo3NeoXBridgeService = Neo3NeoXBridgeService;
|
|
203
|
+
_Neo3NeoXBridgeService_service = new WeakMap(), _Neo3NeoXBridgeService_instances = new WeakSet(), _Neo3NeoXBridgeService_buildGasTransactionParams = function _Neo3NeoXBridgeService_buildGasTransactionParams(params) {
|
|
204
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
205
|
+
const provider = new ethers_1.ethers.providers.JsonRpcProvider(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network.url);
|
|
206
|
+
const gasPrice = yield provider.getGasPrice();
|
|
207
|
+
const to = '0x' + neon_js_1.wallet.getScriptHashFromAddress(params.receiverAddress);
|
|
208
|
+
const bridgeContract = new ethers_1.ethers.Contract(this.BRIDGE_SCRIPT_HASH, bridge_1.BRIDGE_ABI);
|
|
209
|
+
const bridgeFee = ethers_1.ethers.utils.parseUnits(this.BRIDGE_GAS_FEE.toString(), BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.decimals);
|
|
210
|
+
const populatedTransaction = yield bridgeContract.populateTransaction.withdrawNative(to, bridgeFee);
|
|
211
|
+
const bridgeTransactionParam = Object.assign(Object.assign({}, populatedTransaction), { value: ethers_1.ethers.utils.parseUnits(params.validatedInputs.amount, params.validatedInputs.token.decimals), type: 2 });
|
|
212
|
+
return {
|
|
213
|
+
bridgeTransactionParam,
|
|
214
|
+
approveTransactionParams: undefined,
|
|
215
|
+
gasPrice,
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
}, _Neo3NeoXBridgeService_buildNeoTransactionParams = function _Neo3NeoXBridgeService_buildNeoTransactionParams(params) {
|
|
219
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
220
|
+
const provider = new ethers_1.ethers.providers.JsonRpcProvider(__classPrivateFieldGet(this, _Neo3NeoXBridgeService_service, "f").network.url);
|
|
221
|
+
const gasPrice = yield provider.getGasPrice();
|
|
222
|
+
const to = '0x' + neon_js_1.wallet.getScriptHashFromAddress(params.receiverAddress);
|
|
223
|
+
const bridgeContract = new ethers_1.ethers.Contract(this.BRIDGE_SCRIPT_HASH, bridge_1.BRIDGE_ABI);
|
|
224
|
+
const bridgeFee = ethers_1.ethers.utils.parseUnits(this.BRIDGE_GAS_FEE.toString(), BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET.decimals);
|
|
225
|
+
// We are using 0 as the decimals because the NEO token in Neo3 has 0 decimals
|
|
226
|
+
const fixedAmount = blockchain_service_1.BSBigNumberHelper.format(params.validatedInputs.amount, { decimals: 0 });
|
|
227
|
+
const amount = ethers_1.ethers.utils.parseUnits(fixedAmount, params.validatedInputs.token.decimals);
|
|
228
|
+
const erc20Contract = new ethers_1.ethers.Contract(BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.hash, bs_ethereum_1.ERC20_ABI, provider);
|
|
229
|
+
const allowance = yield erc20Contract.allowance(params.account.address, this.BRIDGE_SCRIPT_HASH);
|
|
230
|
+
const allowanceNumber = blockchain_service_1.BSBigNumberHelper.fromDecimals(allowance.toString(), BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN.decimals);
|
|
231
|
+
let approveTransactionParams;
|
|
232
|
+
if (allowanceNumber.isLessThan(fixedAmount)) {
|
|
233
|
+
const populatedApproveTransaction = yield erc20Contract.populateTransaction.approve(this.BRIDGE_SCRIPT_HASH, amount);
|
|
234
|
+
approveTransactionParams = Object.assign(Object.assign({}, populatedApproveTransaction), { type: 2 });
|
|
235
|
+
}
|
|
236
|
+
const populatedTransaction = yield bridgeContract.populateTransaction.withdrawToken(params.validatedInputs.token.hash, to, amount);
|
|
237
|
+
const bridgeTransactionParam = Object.assign(Object.assign({}, populatedTransaction), { type: 2, value: bridgeFee });
|
|
238
|
+
return {
|
|
239
|
+
bridgeTransactionParam,
|
|
240
|
+
approveTransactionParams,
|
|
241
|
+
gasPrice,
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
}, _Neo3NeoXBridgeService_buildTransactionParams = function _Neo3NeoXBridgeService_buildTransactionParams(params) {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
const isGasToken = blockchain_service_1.BSTokenHelper.predicateByHash(params.validatedInputs.token)(BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET);
|
|
247
|
+
if (isGasToken) {
|
|
248
|
+
return __classPrivateFieldGet(this, _Neo3NeoXBridgeService_instances, "m", _Neo3NeoXBridgeService_buildGasTransactionParams).call(this, params);
|
|
249
|
+
}
|
|
250
|
+
return __classPrivateFieldGet(this, _Neo3NeoXBridgeService_instances, "m", _Neo3NeoXBridgeService_buildNeoTransactionParams).call(this, params);
|
|
251
|
+
});
|
|
252
|
+
}, _Neo3NeoXBridgeService_validateGas = function _Neo3NeoXBridgeService_validateGas({ amount, balances, token, account, receiverAddress, }) {
|
|
253
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
254
|
+
const gasBalance = balances.find(balance => bs_ethereum_1.BSEthereumTokenHelper.predicateByHash(balance.token)(BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET));
|
|
255
|
+
if (!gasBalance) {
|
|
256
|
+
throw new blockchain_service_1.BSError('GAS is necessary to bridge', 'GAS_BALANCE_NOT_FOUND');
|
|
257
|
+
}
|
|
258
|
+
const amountNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(amount);
|
|
259
|
+
const gasBalanceNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(gasBalance.amount);
|
|
260
|
+
const validatedInputs = {
|
|
261
|
+
receiveAmount: amountNumber.minus(this.BRIDGE_GAS_FEE).toString(),
|
|
262
|
+
token,
|
|
263
|
+
amount,
|
|
264
|
+
};
|
|
265
|
+
if (amountNumber.isLessThan(this.BRIDGE_MIN_AMOUNT + this.BRIDGE_GAS_FEE)) {
|
|
266
|
+
throw new blockchain_service_1.BSError('Amount is less than the minimum amount plus bridge fee', 'AMOUNT_TOO_LOW');
|
|
267
|
+
}
|
|
268
|
+
if (amountNumber.isGreaterThan(gasBalanceNumber)) {
|
|
269
|
+
throw new blockchain_service_1.BSError('Amount is greater than your balance', 'INSUFFICIENT_GAS_BALANCE');
|
|
270
|
+
}
|
|
271
|
+
const fee = yield this.calculateFee({
|
|
272
|
+
account,
|
|
273
|
+
receiverAddress,
|
|
274
|
+
validatedInputs,
|
|
275
|
+
});
|
|
276
|
+
if (amountNumber.plus(fee).isGreaterThan(gasBalanceNumber)) {
|
|
277
|
+
throw new blockchain_service_1.BSError('Amount is greater than your balance plus fee', 'INSUFFICIENT_GAS_BALANCE_FEE');
|
|
278
|
+
}
|
|
279
|
+
return validatedInputs;
|
|
280
|
+
});
|
|
281
|
+
}, _Neo3NeoXBridgeService_validateNeo = function _Neo3NeoXBridgeService_validateNeo({ amount, balances, token, account, receiverAddress, }) {
|
|
282
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
283
|
+
const gasBalance = balances.find(balance => bs_ethereum_1.BSEthereumTokenHelper.predicateByHash(balance.token)(BSNeoXConstants_1.BSNeoXConstants.NATIVE_ASSET));
|
|
284
|
+
if (!gasBalance) {
|
|
285
|
+
throw new blockchain_service_1.BSError('GAS is necessary to bridge', 'GAS_BALANCE_NOT_FOUND');
|
|
286
|
+
}
|
|
287
|
+
const neoBalance = balances.find(balance => bs_ethereum_1.BSEthereumTokenHelper.predicateByHash(balance.token)(BSNeoXConstants_1.BSNeoXConstants.NEO_TOKEN));
|
|
288
|
+
if (!neoBalance) {
|
|
289
|
+
throw new blockchain_service_1.BSError('NEO balance not found', 'NEO_BALANCE_NOT_FOUND');
|
|
290
|
+
}
|
|
291
|
+
const amountNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(amount);
|
|
292
|
+
const gasBalanceNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(gasBalance.amount);
|
|
293
|
+
const minGasBalanceNumber = blockchain_service_1.BSBigNumberHelper.fromNumber(this.BRIDGE_GAS_FEE);
|
|
294
|
+
const validatedInputs = { receiveAmount: amount, token, amount };
|
|
295
|
+
if (amountNumber.isLessThan(this.BRIDGE_MIN_AMOUNT)) {
|
|
296
|
+
throw new blockchain_service_1.BSError('Amount is less than the minimum amount', 'AMOUNT_TOO_LOW');
|
|
297
|
+
}
|
|
298
|
+
if (amountNumber.isGreaterThan(neoBalance.amount)) {
|
|
299
|
+
throw new blockchain_service_1.BSError('Amount is greater than your balance', 'INSUFFICIENT_NEO_BALANCE');
|
|
300
|
+
}
|
|
301
|
+
if (gasBalanceNumber.isLessThan(minGasBalanceNumber)) {
|
|
302
|
+
throw new blockchain_service_1.BSError('GAS balance is less than bridge fee', 'INSUFFICIENT_GAS_BALANCE_BRIDGE_FEE');
|
|
303
|
+
}
|
|
304
|
+
const fee = yield this.calculateFee({
|
|
305
|
+
account,
|
|
306
|
+
receiverAddress,
|
|
307
|
+
validatedInputs,
|
|
308
|
+
});
|
|
309
|
+
if (minGasBalanceNumber.plus(fee).isGreaterThan(gasBalanceNumber)) {
|
|
310
|
+
throw new blockchain_service_1.BSError('GAS balance is less than fees', 'INSUFFICIENT_GAS_BALANCE_FEES');
|
|
311
|
+
}
|
|
312
|
+
return validatedInputs;
|
|
313
|
+
});
|
|
314
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cityofzion/bs-neox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"repository": "https://github.com/CityOfZion/blockchain-services",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"axios": "1.8.2",
|
|
17
17
|
"ethers": "5.7.2",
|
|
18
18
|
"date-fns": "~4.1.0",
|
|
19
|
-
"@cityofzion/
|
|
20
|
-
"@cityofzion/
|
|
19
|
+
"@cityofzion/blockchain-service": "1.19.0",
|
|
20
|
+
"@cityofzion/bs-ethereum": "2.12.3"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@ledgerhq/hw-transport-node-hid": "~6.28.5",
|