@atomiqlabs/chain-evm 1.0.0-dev.74 → 1.0.0-dev.76
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/evm/btcrelay/EVMBtcRelay.js +14 -17
- package/dist/evm/spv_swap/EVMSpvVaultContract.js +5 -6
- package/dist/evm/wallet/EVMPersistentSigner.d.ts +1 -0
- package/dist/evm/wallet/EVMPersistentSigner.js +42 -27
- package/package.json +4 -2
- package/src/evm/btcrelay/EVMBtcRelay.ts +14 -17
- package/src/evm/spv_swap/EVMSpvVaultContract.ts +5 -6
- package/src/evm/wallet/EVMPersistentSigner.ts +45 -29
|
@@ -9,6 +9,7 @@ const EVMBtcStoredHeader_1 = require("./headers/EVMBtcStoredHeader");
|
|
|
9
9
|
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
10
10
|
const BtcRelayAbi_1 = require("./BtcRelayAbi");
|
|
11
11
|
const ethers_1 = require("ethers");
|
|
12
|
+
const promise_cache_ts_1 = require("promise-cache-ts");
|
|
12
13
|
function serializeBlockHeader(e) {
|
|
13
14
|
return new EVMBtcHeader_1.EVMBtcHeader({
|
|
14
15
|
version: e.getVersion(),
|
|
@@ -54,8 +55,8 @@ class EVMBtcRelay extends EVMContractBase_1.EVMContractBase {
|
|
|
54
55
|
this.maxHeadersPerTx = 100;
|
|
55
56
|
this.maxForkHeadersPerTx = 50;
|
|
56
57
|
this.maxShortForkHeadersPerTx = 100;
|
|
57
|
-
this.commitHashCache = new
|
|
58
|
-
this.blockHashCache = new
|
|
58
|
+
this.commitHashCache = new promise_cache_ts_1.PromiseLruCache(1000);
|
|
59
|
+
this.blockHashCache = new promise_cache_ts_1.PromiseLruCache(1000);
|
|
59
60
|
this.bitcoinRpc = bitcoinRpc;
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
@@ -153,28 +154,24 @@ class EVMBtcRelay extends EVMContractBase_1.EVMContractBase {
|
|
|
153
154
|
return null;
|
|
154
155
|
}
|
|
155
156
|
getBlock(commitHash, blockHash) {
|
|
156
|
-
if (commitHash != null && this.commitHashCache.has(commitHash)) {
|
|
157
|
-
logger.debug("getBlock(): Returning block from commit hash cache: ", commitHash);
|
|
158
|
-
return Promise.resolve([this.commitHashCache.get(commitHash), commitHash]);
|
|
159
|
-
}
|
|
160
157
|
const blockHashString = blockHash == null ? null : "0x" + Buffer.from([...blockHash]).reverse().toString("hex");
|
|
161
|
-
|
|
162
|
-
logger.debug("getBlock(): Returning block from block hash cache: ", blockHashString);
|
|
163
|
-
const storedBlockheader = this.blockHashCache.get(blockHashString);
|
|
164
|
-
return Promise.resolve([storedBlockheader, storedBlockheader.getCommitHash()]);
|
|
165
|
-
}
|
|
166
|
-
return this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], [
|
|
158
|
+
const generator = () => this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], [
|
|
167
159
|
commitHash,
|
|
168
160
|
blockHashString
|
|
169
161
|
], async (event) => {
|
|
170
162
|
const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
|
|
171
163
|
const storedBlockheader = await this.findStoredBlockheaderInTraces(txTrace, event.args.commitHash);
|
|
172
|
-
if (storedBlockheader
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
164
|
+
if (storedBlockheader == null)
|
|
165
|
+
return null;
|
|
166
|
+
this.commitHashCache.set(event.args.commitHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
167
|
+
this.blockHashCache.set(event.args.blockHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
168
|
+
return [storedBlockheader, event.args.commitHash];
|
|
177
169
|
});
|
|
170
|
+
if (commitHash != null)
|
|
171
|
+
return this.commitHashCache.getOrComputeAsync(commitHash, generator);
|
|
172
|
+
if (blockHashString != null)
|
|
173
|
+
return this.blockHashCache.getOrComputeAsync(blockHashString, generator);
|
|
174
|
+
return null;
|
|
178
175
|
}
|
|
179
176
|
async getBlockHeight() {
|
|
180
177
|
return Number(await this.contract.getBlockheight());
|
|
@@ -12,6 +12,7 @@ const EVMAddresses_1 = require("../chain/modules/EVMAddresses");
|
|
|
12
12
|
const EVMSpvVaultData_1 = require("./EVMSpvVaultData");
|
|
13
13
|
const EVMSpvWithdrawalData_1 = require("./EVMSpvWithdrawalData");
|
|
14
14
|
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
15
|
+
const promise_cache_ts_1 = require("promise-cache-ts");
|
|
15
16
|
function decodeUtxo(utxo) {
|
|
16
17
|
const [txId, vout] = utxo.split(":");
|
|
17
18
|
return {
|
|
@@ -34,7 +35,7 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
34
35
|
super(chainInterface, contractAddress, SpvVaultContractAbi_1.SpvVaultContractAbi, contractDeploymentHeight);
|
|
35
36
|
this.claimTimeout = 180;
|
|
36
37
|
this.logger = (0, Utils_1.getLogger)("EVMSpvVaultContract: ");
|
|
37
|
-
this.vaultParamsCache = new
|
|
38
|
+
this.vaultParamsCache = new promise_cache_ts_1.PromiseLruCache(5000);
|
|
38
39
|
this.btcRelay = btcRelay;
|
|
39
40
|
this.bitcoinRpc = bitcoinRpc;
|
|
40
41
|
}
|
|
@@ -126,8 +127,7 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
126
127
|
}
|
|
127
128
|
async getVaultData(owner, vaultId) {
|
|
128
129
|
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
129
|
-
|
|
130
|
-
if (vaultParams == null) {
|
|
130
|
+
const vaultParams = await this.vaultParamsCache.getOrComputeAsync(vaultState.spvVaultParametersCommitment, async () => {
|
|
131
131
|
const blockheight = Number(vaultState.openBlockheight);
|
|
132
132
|
const events = await this.Events.getContractBlockEvents(["Opened"], [
|
|
133
133
|
"0x" + owner.substring(2).padStart(64, "0"),
|
|
@@ -136,9 +136,8 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
136
136
|
const foundEvent = events.find(event => (0, EVMSpvVaultData_1.getVaultParamsCommitment)(event.args.params) === vaultState.spvVaultParametersCommitment);
|
|
137
137
|
if (foundEvent == null)
|
|
138
138
|
throw new Error("Valid open event not found!");
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
139
|
+
return foundEvent.args.params;
|
|
140
|
+
});
|
|
142
141
|
if (vaultParams.btcRelayContract.toLowerCase() !== this.btcRelay.contractAddress.toLowerCase())
|
|
143
142
|
return null;
|
|
144
143
|
return new EVMSpvVaultData_1.EVMSpvVaultData(owner, vaultId, vaultState, vaultParams);
|
|
@@ -24,5 +24,6 @@ export declare class EVMPersistentSigner extends EVMSigner {
|
|
|
24
24
|
private startFeeBumper;
|
|
25
25
|
init(): Promise<void>;
|
|
26
26
|
stop(): Promise<void>;
|
|
27
|
+
private readonly sendTransactionQueue;
|
|
27
28
|
sendTransaction(transaction: TransactionRequest, onBeforePublish?: (txId: string, rawTx: string) => Promise<void>): Promise<TransactionResponse>;
|
|
28
29
|
}
|
|
@@ -6,6 +6,7 @@ const ethers_1 = require("ethers");
|
|
|
6
6
|
const Utils_1 = require("../../utils/Utils");
|
|
7
7
|
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
8
8
|
const EVMSigner_1 = require("./EVMSigner");
|
|
9
|
+
const promise_queue_ts_1 = require("promise-queue-ts");
|
|
9
10
|
const WAIT_BEFORE_BUMP = 15 * 1000;
|
|
10
11
|
const MIN_FEE_INCREASE_ABSOLUTE = 1n * 1000000000n; //1GWei
|
|
11
12
|
const MIN_FEE_INCREASE_PPM = 100000n; // +10%
|
|
@@ -15,6 +16,7 @@ class EVMPersistentSigner extends EVMSigner_1.EVMSigner {
|
|
|
15
16
|
this.pendingTxs = new Map();
|
|
16
17
|
this.stopped = false;
|
|
17
18
|
this.saveCount = 0;
|
|
19
|
+
this.sendTransactionQueue = new promise_queue_ts_1.PromiseQueue();
|
|
18
20
|
this.signTransaction = null;
|
|
19
21
|
this.chainInterface = chainInterface;
|
|
20
22
|
this.directory = directory;
|
|
@@ -71,7 +73,7 @@ class EVMPersistentSigner extends EVMSigner_1.EVMSigner {
|
|
|
71
73
|
let _gasPrice = null;
|
|
72
74
|
let _safeBlockTxCount = null;
|
|
73
75
|
for (let [nonce, data] of this.pendingTxs) {
|
|
74
|
-
if (data.lastBumped < Date.now() - this.waitBeforeBump) {
|
|
76
|
+
if (!data.sending && data.lastBumped < Date.now() - this.waitBeforeBump) {
|
|
75
77
|
_safeBlockTxCount = await this.chainInterface.provider.getTransactionCount(this.address, this.safeBlockTag);
|
|
76
78
|
this.confirmedNonce = _safeBlockTxCount;
|
|
77
79
|
if (_safeBlockTxCount > nonce) {
|
|
@@ -169,39 +171,52 @@ class EVMPersistentSigner extends EVMSigner_1.EVMSigner {
|
|
|
169
171
|
}
|
|
170
172
|
return Promise.resolve();
|
|
171
173
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (transaction.nonce
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
else {
|
|
179
|
-
this.pendingNonce++;
|
|
180
|
-
transaction.nonce = this.pendingNonce;
|
|
181
|
-
}
|
|
182
|
-
const tx = {};
|
|
183
|
-
for (let key in transaction) {
|
|
184
|
-
if (transaction[key] instanceof Promise) {
|
|
185
|
-
tx[key] = await transaction[key];
|
|
174
|
+
sendTransaction(transaction, onBeforePublish) {
|
|
175
|
+
return this.sendTransactionQueue.enqueue(async () => {
|
|
176
|
+
if (transaction.nonce != null) {
|
|
177
|
+
if (transaction.nonce !== this.pendingNonce + 1)
|
|
178
|
+
throw new Error("Invalid transaction nonce!");
|
|
179
|
+
this.pendingNonce++;
|
|
186
180
|
}
|
|
187
181
|
else {
|
|
188
|
-
|
|
182
|
+
this.pendingNonce++;
|
|
183
|
+
transaction.nonce = this.pendingNonce;
|
|
189
184
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
185
|
+
const tx = {};
|
|
186
|
+
for (let key in transaction) {
|
|
187
|
+
if (transaction[key] instanceof Promise) {
|
|
188
|
+
tx[key] = await transaction[key];
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
tx[key] = transaction[key];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const signedRawTx = await this.account.signTransaction(tx);
|
|
195
|
+
const signedTx = ethers_1.Transaction.from(signedRawTx);
|
|
196
|
+
if (onBeforePublish != null) {
|
|
197
|
+
try {
|
|
198
|
+
await onBeforePublish(signedTx.hash, signedRawTx);
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
this.logger.error("sendTransaction(): Error when calling onBeforePublish function: ", e);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const pendingTxObject = { txs: [signedTx], lastBumped: Date.now(), sending: true };
|
|
205
|
+
this.pendingTxs.set(transaction.nonce, pendingTxObject);
|
|
206
|
+
this.save();
|
|
207
|
+
this.chainInterface.Transactions._knownTxSet.add(signedTx.hash);
|
|
194
208
|
try {
|
|
195
|
-
await
|
|
209
|
+
const result = await this.chainInterface.provider.broadcastTransaction(signedRawTx);
|
|
210
|
+
pendingTxObject.sending = false;
|
|
211
|
+
return result;
|
|
196
212
|
}
|
|
197
213
|
catch (e) {
|
|
198
|
-
this.
|
|
214
|
+
this.chainInterface.Transactions._knownTxSet.delete(signedTx.hash);
|
|
215
|
+
this.pendingTxs.delete(transaction.nonce);
|
|
216
|
+
this.pendingNonce--;
|
|
217
|
+
throw e;
|
|
199
218
|
}
|
|
200
|
-
}
|
|
201
|
-
this.pendingTxs.set(transaction.nonce, { txs: [signedTx], lastBumped: Date.now() });
|
|
202
|
-
this.save();
|
|
203
|
-
this.chainInterface.Transactions._knownTxSet.add(signedTx.hash);
|
|
204
|
-
return await this.chainInterface.provider.broadcastTransaction(signedRawTx);
|
|
219
|
+
});
|
|
205
220
|
}
|
|
206
221
|
}
|
|
207
222
|
exports.EVMPersistentSigner = EVMPersistentSigner;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/chain-evm",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.76",
|
|
4
4
|
"description": "EVM specific base implementation",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types:": "./dist/index.d.ts",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"@atomiqlabs/base": "^10.0.0-dev.10",
|
|
27
27
|
"@noble/hashes": "^1.8.0",
|
|
28
28
|
"@scure/btc-signer": "1.6.0",
|
|
29
|
-
"buffer": "6.0.3"
|
|
29
|
+
"buffer": "6.0.3",
|
|
30
|
+
"promise-cache-ts": "0.0.4",
|
|
31
|
+
"promise-queue-ts": "1.0.0"
|
|
30
32
|
},
|
|
31
33
|
"peerDependencies": {
|
|
32
34
|
"ethers": "^6.15.0"
|
|
@@ -10,6 +10,7 @@ import {EVMFees} from "../chain/modules/EVMFees";
|
|
|
10
10
|
import {EVMChainInterface} from "../chain/EVMChainInterface";
|
|
11
11
|
import {BtcRelayAbi} from "./BtcRelayAbi";
|
|
12
12
|
import {AbiCoder, hexlify} from "ethers";
|
|
13
|
+
import {PromiseLruCache} from "promise-cache-ts";
|
|
13
14
|
|
|
14
15
|
function serializeBlockHeader(e: BtcBlock): EVMBtcHeader {
|
|
15
16
|
return new EVMBtcHeader({
|
|
@@ -191,22 +192,13 @@ export class EVMBtcRelay<B extends BtcBlock>
|
|
|
191
192
|
return null;
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
private commitHashCache:
|
|
195
|
-
private blockHashCache:
|
|
195
|
+
private commitHashCache: PromiseLruCache<string, [EVMBtcStoredHeader, string]> = new PromiseLruCache<string, [EVMBtcStoredHeader, string]>(1000);
|
|
196
|
+
private blockHashCache: PromiseLruCache<string, [EVMBtcStoredHeader, string]> = new PromiseLruCache<string, [EVMBtcStoredHeader, string]>(1000);
|
|
196
197
|
|
|
197
198
|
private getBlock(commitHash?: string, blockHash?: Buffer): Promise<[EVMBtcStoredHeader, string] | null> {
|
|
198
|
-
if(commitHash!=null && this.commitHashCache.has(commitHash)) {
|
|
199
|
-
logger.debug("getBlock(): Returning block from commit hash cache: ", commitHash);
|
|
200
|
-
return Promise.resolve([this.commitHashCache.get(commitHash), commitHash]);
|
|
201
|
-
}
|
|
202
199
|
const blockHashString = blockHash==null ? null : "0x"+Buffer.from([...blockHash]).reverse().toString("hex");
|
|
203
|
-
if(blockHashString!=null && this.blockHashCache.has(blockHashString)) {
|
|
204
|
-
logger.debug("getBlock(): Returning block from block hash cache: ", blockHashString);
|
|
205
|
-
const storedBlockheader = this.blockHashCache.get(blockHashString);
|
|
206
|
-
return Promise.resolve([storedBlockheader, storedBlockheader.getCommitHash()]);
|
|
207
|
-
}
|
|
208
200
|
|
|
209
|
-
|
|
201
|
+
const generator = () => this.Events.findInContractEvents<[EVMBtcStoredHeader, string], "StoreHeader" | "StoreForkHeader">(
|
|
210
202
|
["StoreHeader", "StoreForkHeader"],
|
|
211
203
|
[
|
|
212
204
|
commitHash,
|
|
@@ -215,13 +207,18 @@ export class EVMBtcRelay<B extends BtcBlock>
|
|
|
215
207
|
async (event) => {
|
|
216
208
|
const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
|
|
217
209
|
const storedBlockheader = await this.findStoredBlockheaderInTraces(txTrace, event.args.commitHash);
|
|
218
|
-
if(storedBlockheader
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
210
|
+
if(storedBlockheader==null) return null;
|
|
211
|
+
|
|
212
|
+
this.commitHashCache.set(event.args.commitHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
213
|
+
this.blockHashCache.set(event.args.blockHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
214
|
+
return [storedBlockheader, event.args.commitHash];
|
|
223
215
|
}
|
|
224
216
|
);
|
|
217
|
+
|
|
218
|
+
if(commitHash!=null) return this.commitHashCache.getOrComputeAsync(commitHash, generator);
|
|
219
|
+
if(blockHashString!=null) return this.blockHashCache.getOrComputeAsync(blockHashString, generator);
|
|
220
|
+
|
|
221
|
+
return null;
|
|
225
222
|
}
|
|
226
223
|
|
|
227
224
|
private async getBlockHeight(): Promise<number> {
|
|
@@ -26,6 +26,7 @@ import {EVMSpvWithdrawalData} from "./EVMSpvWithdrawalData";
|
|
|
26
26
|
import {EVMFees} from "../chain/modules/EVMFees";
|
|
27
27
|
import {EVMBtcStoredHeader} from "../btcrelay/headers/EVMBtcStoredHeader";
|
|
28
28
|
import {TypedEventLog} from "../typechain/common";
|
|
29
|
+
import {PromiseLruCache} from "promise-cache-ts";
|
|
29
30
|
|
|
30
31
|
function decodeUtxo(utxo: string): {txHash: string, vout: bigint} {
|
|
31
32
|
const [txId, vout] = utxo.split(":");
|
|
@@ -209,13 +210,12 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
209
210
|
return frontingAddress;
|
|
210
211
|
}
|
|
211
212
|
|
|
212
|
-
private vaultParamsCache:
|
|
213
|
+
private vaultParamsCache: PromiseLruCache<string, SpvVaultParametersStructOutput> = new PromiseLruCache<string, SpvVaultParametersStructOutput>(5000);
|
|
213
214
|
|
|
214
215
|
async getVaultData(owner: string, vaultId: bigint): Promise<EVMSpvVaultData> {
|
|
215
216
|
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
if(vaultParams==null) {
|
|
218
|
+
const vaultParams = await this.vaultParamsCache.getOrComputeAsync(vaultState.spvVaultParametersCommitment, async () => {
|
|
219
219
|
const blockheight = Number(vaultState.openBlockheight);
|
|
220
220
|
const events = await this.Events.getContractBlockEvents(
|
|
221
221
|
["Opened"],
|
|
@@ -231,9 +231,8 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
231
231
|
);
|
|
232
232
|
if(foundEvent==null) throw new Error("Valid open event not found!");
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
234
|
+
return foundEvent.args.params;
|
|
235
|
+
});
|
|
237
236
|
|
|
238
237
|
if(vaultParams.btcRelayContract.toLowerCase()!==this.btcRelay.contractAddress.toLowerCase()) return null;
|
|
239
238
|
|
|
@@ -10,6 +10,7 @@ import {EVMBlockTag} from "../chain/modules/EVMBlocks";
|
|
|
10
10
|
import {EVMChainInterface} from "../chain/EVMChainInterface";
|
|
11
11
|
import {EVMFees} from "../chain/modules/EVMFees";
|
|
12
12
|
import {EVMSigner} from "./EVMSigner";
|
|
13
|
+
import {PromiseQueue} from "promise-queue-ts";
|
|
13
14
|
|
|
14
15
|
const WAIT_BEFORE_BUMP = 15*1000;
|
|
15
16
|
const MIN_FEE_INCREASE_ABSOLUTE = 1n*1_000_000_000n; //1GWei
|
|
@@ -21,7 +22,8 @@ export class EVMPersistentSigner extends EVMSigner {
|
|
|
21
22
|
|
|
22
23
|
private pendingTxs: Map<number, {
|
|
23
24
|
txs: Transaction[],
|
|
24
|
-
lastBumped: number
|
|
25
|
+
lastBumped: number,
|
|
26
|
+
sending?: boolean //Not saved
|
|
25
27
|
}> = new Map();
|
|
26
28
|
|
|
27
29
|
private confirmedNonce: number;
|
|
@@ -127,7 +129,7 @@ export class EVMPersistentSigner extends EVMSigner {
|
|
|
127
129
|
let _safeBlockTxCount: number = null;
|
|
128
130
|
|
|
129
131
|
for(let [nonce, data] of this.pendingTxs) {
|
|
130
|
-
if(data.lastBumped<Date.now()-this.waitBeforeBump) {
|
|
132
|
+
if(!data.sending && data.lastBumped<Date.now()-this.waitBeforeBump) {
|
|
131
133
|
_safeBlockTxCount = await this.chainInterface.provider.getTransactionCount(this.address, this.safeBlockTag);
|
|
132
134
|
this.confirmedNonce = _safeBlockTxCount;
|
|
133
135
|
if(_safeBlockTxCount > nonce) {
|
|
@@ -241,42 +243,56 @@ export class EVMPersistentSigner extends EVMSigner {
|
|
|
241
243
|
return Promise.resolve();
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
|
|
245
|
-
if(transaction.nonce!=null) {
|
|
246
|
-
if(transaction.nonce !== this.pendingNonce + 1)
|
|
247
|
-
throw new Error("Invalid transaction nonce!");
|
|
248
|
-
this.pendingNonce++;
|
|
249
|
-
} else {
|
|
250
|
-
this.pendingNonce++;
|
|
251
|
-
transaction.nonce = this.pendingNonce;
|
|
252
|
-
}
|
|
246
|
+
private readonly sendTransactionQueue: PromiseQueue = new PromiseQueue();
|
|
253
247
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if(transaction
|
|
257
|
-
|
|
248
|
+
sendTransaction(transaction: TransactionRequest, onBeforePublish?: (txId: string, rawTx: string) => Promise<void>): Promise<TransactionResponse> {
|
|
249
|
+
return this.sendTransactionQueue.enqueue(async () => {
|
|
250
|
+
if(transaction.nonce!=null) {
|
|
251
|
+
if(transaction.nonce !== this.pendingNonce + 1)
|
|
252
|
+
throw new Error("Invalid transaction nonce!");
|
|
253
|
+
this.pendingNonce++;
|
|
258
254
|
} else {
|
|
259
|
-
|
|
255
|
+
this.pendingNonce++;
|
|
256
|
+
transaction.nonce = this.pendingNonce;
|
|
260
257
|
}
|
|
261
|
-
}
|
|
262
258
|
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
const tx: TransactionRequest = {};
|
|
260
|
+
for(let key in transaction) {
|
|
261
|
+
if(transaction[key] instanceof Promise) {
|
|
262
|
+
tx[key] = await transaction[key];
|
|
263
|
+
} else {
|
|
264
|
+
tx[key] = transaction[key];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
268
|
+
const signedRawTx = await this.account.signTransaction(tx);
|
|
269
|
+
const signedTx = Transaction.from(signedRawTx);
|
|
270
|
+
|
|
271
|
+
if(onBeforePublish!=null) {
|
|
272
|
+
try {
|
|
273
|
+
await onBeforePublish(signedTx.hash, signedRawTx);
|
|
274
|
+
} catch (e) {
|
|
275
|
+
this.logger.error("sendTransaction(): Error when calling onBeforePublish function: ", e);
|
|
276
|
+
}
|
|
271
277
|
}
|
|
272
|
-
}
|
|
273
278
|
|
|
274
|
-
|
|
275
|
-
|
|
279
|
+
const pendingTxObject = {txs: [signedTx], lastBumped: Date.now(), sending: true};
|
|
280
|
+
this.pendingTxs.set(transaction.nonce, pendingTxObject);
|
|
281
|
+
this.save();
|
|
276
282
|
|
|
277
|
-
|
|
283
|
+
this.chainInterface.Transactions._knownTxSet.add(signedTx.hash);
|
|
278
284
|
|
|
279
|
-
|
|
285
|
+
try {
|
|
286
|
+
const result = await this.chainInterface.provider.broadcastTransaction(signedRawTx);
|
|
287
|
+
pendingTxObject.sending = false;
|
|
288
|
+
return result;
|
|
289
|
+
} catch (e) {
|
|
290
|
+
this.chainInterface.Transactions._knownTxSet.delete(signedTx.hash);
|
|
291
|
+
this.pendingTxs.delete(transaction.nonce);
|
|
292
|
+
this.pendingNonce--;
|
|
293
|
+
throw e;
|
|
294
|
+
}
|
|
295
|
+
});
|
|
280
296
|
}
|
|
281
297
|
|
|
282
298
|
}
|