@atomiqlabs/chain-evm 1.0.0-dev.22
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/LICENSE +201 -0
- package/dist/chains/citrea/CitreaChainType.d.ts +13 -0
- package/dist/chains/citrea/CitreaChainType.js +2 -0
- package/dist/chains/citrea/CitreaInitializer.d.ts +30 -0
- package/dist/chains/citrea/CitreaInitializer.js +120 -0
- package/dist/evm/btcrelay/BtcRelayAbi.d.ts +198 -0
- package/dist/evm/btcrelay/BtcRelayAbi.js +261 -0
- package/dist/evm/btcrelay/BtcRelayTypechain.d.ts +172 -0
- package/dist/evm/btcrelay/BtcRelayTypechain.js +2 -0
- package/dist/evm/btcrelay/EVMBtcRelay.d.ts +188 -0
- package/dist/evm/btcrelay/EVMBtcRelay.js +419 -0
- package/dist/evm/btcrelay/headers/EVMBtcHeader.d.ts +33 -0
- package/dist/evm/btcrelay/headers/EVMBtcHeader.js +84 -0
- package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.d.ts +56 -0
- package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.js +123 -0
- package/dist/evm/chain/EVMChainInterface.d.ts +51 -0
- package/dist/evm/chain/EVMChainInterface.js +90 -0
- package/dist/evm/chain/EVMModule.d.ts +9 -0
- package/dist/evm/chain/EVMModule.js +13 -0
- package/dist/evm/chain/modules/ERC20Abi.d.ts +168 -0
- package/dist/evm/chain/modules/ERC20Abi.js +225 -0
- package/dist/evm/chain/modules/EVMAddresses.d.ts +9 -0
- package/dist/evm/chain/modules/EVMAddresses.js +26 -0
- package/dist/evm/chain/modules/EVMBlocks.d.ts +20 -0
- package/dist/evm/chain/modules/EVMBlocks.js +64 -0
- package/dist/evm/chain/modules/EVMEvents.d.ts +36 -0
- package/dist/evm/chain/modules/EVMEvents.js +122 -0
- package/dist/evm/chain/modules/EVMFees.d.ts +35 -0
- package/dist/evm/chain/modules/EVMFees.js +73 -0
- package/dist/evm/chain/modules/EVMSignatures.d.ts +29 -0
- package/dist/evm/chain/modules/EVMSignatures.js +68 -0
- package/dist/evm/chain/modules/EVMTokens.d.ts +49 -0
- package/dist/evm/chain/modules/EVMTokens.js +105 -0
- package/dist/evm/chain/modules/EVMTransactions.d.ts +89 -0
- package/dist/evm/chain/modules/EVMTransactions.js +216 -0
- package/dist/evm/contract/EVMContractBase.d.ts +22 -0
- package/dist/evm/contract/EVMContractBase.js +34 -0
- package/dist/evm/contract/EVMContractModule.d.ts +8 -0
- package/dist/evm/contract/EVMContractModule.js +11 -0
- package/dist/evm/contract/modules/EVMContractEvents.d.ts +42 -0
- package/dist/evm/contract/modules/EVMContractEvents.js +75 -0
- package/dist/evm/events/EVMChainEvents.d.ts +22 -0
- package/dist/evm/events/EVMChainEvents.js +67 -0
- package/dist/evm/events/EVMChainEventsBrowser.d.ts +86 -0
- package/dist/evm/events/EVMChainEventsBrowser.js +294 -0
- package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +64 -0
- package/dist/evm/spv_swap/EVMSpvVaultContract.js +410 -0
- package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +38 -0
- package/dist/evm/spv_swap/EVMSpvVaultData.js +159 -0
- package/dist/evm/spv_swap/EVMSpvWithdrawalData.d.ts +19 -0
- package/dist/evm/spv_swap/EVMSpvWithdrawalData.js +55 -0
- package/dist/evm/spv_swap/SpvVaultContractAbi.d.ts +91 -0
- package/dist/evm/spv_swap/SpvVaultContractAbi.js +849 -0
- package/dist/evm/spv_swap/SpvVaultContractTypechain.d.ts +450 -0
- package/dist/evm/spv_swap/SpvVaultContractTypechain.js +2 -0
- package/dist/evm/swaps/EVMSwapContract.d.ts +192 -0
- package/dist/evm/swaps/EVMSwapContract.js +373 -0
- package/dist/evm/swaps/EVMSwapData.d.ts +64 -0
- package/dist/evm/swaps/EVMSwapData.js +254 -0
- package/dist/evm/swaps/EVMSwapModule.d.ts +9 -0
- package/dist/evm/swaps/EVMSwapModule.js +11 -0
- package/dist/evm/swaps/EscrowManagerAbi.d.ts +120 -0
- package/dist/evm/swaps/EscrowManagerAbi.js +985 -0
- package/dist/evm/swaps/EscrowManagerTypechain.d.ts +475 -0
- package/dist/evm/swaps/EscrowManagerTypechain.js +2 -0
- package/dist/evm/swaps/handlers/IHandler.d.ts +13 -0
- package/dist/evm/swaps/handlers/IHandler.js +2 -0
- package/dist/evm/swaps/handlers/claim/ClaimHandlers.d.ts +10 -0
- package/dist/evm/swaps/handlers/claim/ClaimHandlers.js +13 -0
- package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.d.ts +20 -0
- package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.js +39 -0
- package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -0
- package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +59 -0
- package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -0
- package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +51 -0
- package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +21 -0
- package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +28 -0
- package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +48 -0
- package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +63 -0
- package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -0
- package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.js +28 -0
- package/dist/evm/swaps/modules/EVMLpVault.d.ts +69 -0
- package/dist/evm/swaps/modules/EVMLpVault.js +131 -0
- package/dist/evm/swaps/modules/EVMSwapClaim.d.ts +53 -0
- package/dist/evm/swaps/modules/EVMSwapClaim.js +101 -0
- package/dist/evm/swaps/modules/EVMSwapInit.d.ts +88 -0
- package/dist/evm/swaps/modules/EVMSwapInit.js +241 -0
- package/dist/evm/swaps/modules/EVMSwapRefund.d.ts +62 -0
- package/dist/evm/swaps/modules/EVMSwapRefund.js +132 -0
- package/dist/evm/typechain/common.d.ts +50 -0
- package/dist/evm/typechain/common.js +2 -0
- package/dist/evm/wallet/EVMSigner.d.ts +9 -0
- package/dist/evm/wallet/EVMSigner.js +16 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +53 -0
- package/dist/utils/Utils.d.ts +15 -0
- package/dist/utils/Utils.js +71 -0
- package/package.json +37 -0
- package/src/chains/citrea/CitreaChainType.ts +28 -0
- package/src/chains/citrea/CitreaInitializer.ts +167 -0
- package/src/evm/btcrelay/BtcRelayAbi.ts +258 -0
- package/src/evm/btcrelay/BtcRelayTypechain.ts +371 -0
- package/src/evm/btcrelay/EVMBtcRelay.ts +517 -0
- package/src/evm/btcrelay/headers/EVMBtcHeader.ts +110 -0
- package/src/evm/btcrelay/headers/EVMBtcStoredHeader.ts +153 -0
- package/src/evm/chain/EVMChainInterface.ts +157 -0
- package/src/evm/chain/EVMModule.ts +21 -0
- package/src/evm/chain/modules/ERC20Abi.ts +222 -0
- package/src/evm/chain/modules/EVMAddresses.ts +24 -0
- package/src/evm/chain/modules/EVMBlocks.ts +75 -0
- package/src/evm/chain/modules/EVMEvents.ts +139 -0
- package/src/evm/chain/modules/EVMFees.ts +105 -0
- package/src/evm/chain/modules/EVMSignatures.ts +76 -0
- package/src/evm/chain/modules/EVMTokens.ts +115 -0
- package/src/evm/chain/modules/EVMTransactions.ts +246 -0
- package/src/evm/contract/EVMContractBase.ts +63 -0
- package/src/evm/contract/EVMContractModule.ts +16 -0
- package/src/evm/contract/modules/EVMContractEvents.ts +102 -0
- package/src/evm/events/EVMChainEvents.ts +81 -0
- package/src/evm/events/EVMChainEventsBrowser.ts +390 -0
- package/src/evm/spv_swap/EVMSpvVaultContract.ts +533 -0
- package/src/evm/spv_swap/EVMSpvVaultData.ts +201 -0
- package/src/evm/spv_swap/EVMSpvWithdrawalData.ts +70 -0
- package/src/evm/spv_swap/SpvVaultContractAbi.ts +846 -0
- package/src/evm/spv_swap/SpvVaultContractTypechain.ts +685 -0
- package/src/evm/swaps/EVMSwapContract.ts +590 -0
- package/src/evm/swaps/EVMSwapData.ts +367 -0
- package/src/evm/swaps/EVMSwapModule.ts +16 -0
- package/src/evm/swaps/EscrowManagerAbi.ts +982 -0
- package/src/evm/swaps/EscrowManagerTypechain.ts +723 -0
- package/src/evm/swaps/handlers/IHandler.ts +17 -0
- package/src/evm/swaps/handlers/claim/ClaimHandlers.ts +20 -0
- package/src/evm/swaps/handlers/claim/HashlockClaimHandler.ts +47 -0
- package/src/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +82 -0
- package/src/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +76 -0
- package/src/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +46 -0
- package/src/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +115 -0
- package/src/evm/swaps/handlers/refund/TimelockRefundHandler.ts +38 -0
- package/src/evm/swaps/modules/EVMLpVault.ts +153 -0
- package/src/evm/swaps/modules/EVMSwapClaim.ts +141 -0
- package/src/evm/swaps/modules/EVMSwapInit.ts +292 -0
- package/src/evm/swaps/modules/EVMSwapRefund.ts +198 -0
- package/src/evm/typechain/common.ts +131 -0
- package/src/evm/wallet/EVMSigner.ts +23 -0
- package/src/index.ts +44 -0
- package/src/utils/Utils.ts +81 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {EVMModule} from "../EVMModule";
|
|
2
|
+
import {Log} from "ethers";
|
|
3
|
+
|
|
4
|
+
export class EVMEvents extends EVMModule<any> {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns the all the events occuring in a block range as identified by the contract and keys
|
|
8
|
+
*
|
|
9
|
+
* @param contract
|
|
10
|
+
* @param topics
|
|
11
|
+
* @param startBlock
|
|
12
|
+
* @param endBlock
|
|
13
|
+
* @param abortSignal
|
|
14
|
+
*/
|
|
15
|
+
public async getBlockEvents(
|
|
16
|
+
contract: string, topics: (string[] | string | null)[], startBlock?: number, endBlock: number = startBlock, abortSignal?: AbortSignal
|
|
17
|
+
): Promise<Log[]> {
|
|
18
|
+
let events: Log[] = [];
|
|
19
|
+
|
|
20
|
+
if(startBlock===endBlock) {
|
|
21
|
+
events = await this.root.provider.getLogs({
|
|
22
|
+
address: contract,
|
|
23
|
+
fromBlock: startBlock==null ? this.root.config.safeBlockTag : startBlock,
|
|
24
|
+
toBlock: endBlock==null ? this.root.config.safeBlockTag : endBlock,
|
|
25
|
+
topics
|
|
26
|
+
});
|
|
27
|
+
} else if(endBlock==null) {
|
|
28
|
+
const safeBlock = await this.root.provider.getBlock(this.root.config.safeBlockTag);
|
|
29
|
+
if(safeBlock.number - startBlock > this.root.config.maxLogsBlockRange) {
|
|
30
|
+
for(let i = startBlock + this.root.config.maxLogsBlockRange; i < safeBlock.number; i += this.root.config.maxLogsBlockRange) {
|
|
31
|
+
events.push(...await this.root.provider.getLogs({
|
|
32
|
+
address: contract,
|
|
33
|
+
fromBlock: i - this.root.config.maxLogsBlockRange,
|
|
34
|
+
toBlock: i,
|
|
35
|
+
topics
|
|
36
|
+
}));
|
|
37
|
+
startBlock = i;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
events.push(...await this.root.provider.getLogs({
|
|
41
|
+
address: contract,
|
|
42
|
+
fromBlock: startBlock==null ? this.root.config.safeBlockTag : startBlock,
|
|
43
|
+
toBlock: endBlock==null ? this.root.config.safeBlockTag : endBlock,
|
|
44
|
+
topics
|
|
45
|
+
}));
|
|
46
|
+
} else {
|
|
47
|
+
//Both numeric
|
|
48
|
+
if(endBlock - startBlock > this.root.config.maxLogsBlockRange) {
|
|
49
|
+
for(let i = startBlock + this.root.config.maxLogsBlockRange; i < endBlock; i += this.root.config.maxLogsBlockRange) {
|
|
50
|
+
events.push(...await this.root.provider.getLogs({
|
|
51
|
+
address: contract,
|
|
52
|
+
fromBlock: i - this.root.config.maxLogsBlockRange,
|
|
53
|
+
toBlock: i,
|
|
54
|
+
topics
|
|
55
|
+
}));
|
|
56
|
+
startBlock = i;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
events.push(...await this.root.provider.getLogs({
|
|
60
|
+
address: contract,
|
|
61
|
+
fromBlock: startBlock,
|
|
62
|
+
toBlock: endBlock,
|
|
63
|
+
topics
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return events.filter(val => !val.removed);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Runs a search backwards in time, processing events from a specific contract and keys
|
|
72
|
+
*
|
|
73
|
+
* @param contract
|
|
74
|
+
* @param topics
|
|
75
|
+
* @param processor called for every batch of returned signatures, should return a value if the correct signature
|
|
76
|
+
* was found, or null if the search should continue
|
|
77
|
+
* @param abortSignal
|
|
78
|
+
* @param genesisHeight Height when the contract was deployed
|
|
79
|
+
*/
|
|
80
|
+
public async findInEvents<T>(
|
|
81
|
+
contract: string, topics: (string[] | string | null)[],
|
|
82
|
+
processor: (signatures: Log[]) => Promise<T>,
|
|
83
|
+
abortSignal?: AbortSignal,
|
|
84
|
+
genesisHeight?: number
|
|
85
|
+
): Promise<T> {
|
|
86
|
+
const {number: latestBlockNumber} = await this.provider.getBlock(this.root.config.safeBlockTag);
|
|
87
|
+
|
|
88
|
+
for(let blockNumber = latestBlockNumber; blockNumber >= (genesisHeight ?? 0); blockNumber-=this.root.config.maxLogsBlockRange) {
|
|
89
|
+
const eventsResult = await this.provider.getLogs({
|
|
90
|
+
address: contract,
|
|
91
|
+
topics,
|
|
92
|
+
fromBlock: Math.max(blockNumber-this.root.config.maxLogsBlockRange, 0),
|
|
93
|
+
toBlock: blockNumber===latestBlockNumber ? this.root.config.safeBlockTag : blockNumber
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
97
|
+
|
|
98
|
+
const result: T = await processor(eventsResult.reverse()); //Newest events first
|
|
99
|
+
if(result!=null) return result;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Runs a search forwards in time, processing events from a specific contract and keys
|
|
106
|
+
*
|
|
107
|
+
* @param contract
|
|
108
|
+
* @param topics
|
|
109
|
+
* @param processor called for every batch of returned signatures, should return a value if the correct signature
|
|
110
|
+
* was found, or null if the search should continue
|
|
111
|
+
* @param abortSignal
|
|
112
|
+
* @param startHeight Blockheight at which to start
|
|
113
|
+
*/
|
|
114
|
+
public async findInEventsForward<T>(
|
|
115
|
+
contract: string, topics: (string[] | string | null)[],
|
|
116
|
+
processor: (signatures: Log[]) => Promise<T>,
|
|
117
|
+
abortSignal?: AbortSignal,
|
|
118
|
+
startHeight?: number
|
|
119
|
+
): Promise<T> {
|
|
120
|
+
const {number: latestBlockNumber} = await this.provider.getBlock(this.root.config.safeBlockTag);
|
|
121
|
+
|
|
122
|
+
for(let blockNumber = startHeight ?? 0; blockNumber < latestBlockNumber; blockNumber += this.root.config.maxLogsBlockRange) {
|
|
123
|
+
const eventsResult = await this.provider.getLogs({
|
|
124
|
+
address: contract,
|
|
125
|
+
topics,
|
|
126
|
+
fromBlock: blockNumber,
|
|
127
|
+
toBlock: (blockNumber + this.root.config.maxLogsBlockRange) > latestBlockNumber ? this.root.config.safeBlockTag : blockNumber + this.root.config.maxLogsBlockRange
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
131
|
+
|
|
132
|
+
const result: T = await processor(eventsResult); //Oldest events first
|
|
133
|
+
if(result!=null) return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { getLogger } from "../../../utils/Utils";
|
|
2
|
+
import {Provider, TransactionRequest} from "ethers";
|
|
3
|
+
|
|
4
|
+
const MAX_FEE_AGE = 5000;
|
|
5
|
+
|
|
6
|
+
export type EVMFeeRate = {
|
|
7
|
+
maxFeePerGas: bigint;
|
|
8
|
+
maxPriorityFee: bigint;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class EVMFees {
|
|
12
|
+
|
|
13
|
+
private readonly logger = getLogger("EVMFees: ");
|
|
14
|
+
|
|
15
|
+
private readonly provider: Provider;
|
|
16
|
+
private readonly maxFeeRatePerGas: bigint;
|
|
17
|
+
private readonly priorityFee: bigint;
|
|
18
|
+
|
|
19
|
+
private readonly feeMultiplierPPM: bigint;
|
|
20
|
+
|
|
21
|
+
private blockFeeCache: {
|
|
22
|
+
timestamp: number,
|
|
23
|
+
feeRate: Promise<bigint>
|
|
24
|
+
} = null;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
provider: Provider,
|
|
28
|
+
maxFeeRatePerGas: bigint = 500n * 1_000_000_000n,
|
|
29
|
+
priorityFee: bigint = 1n * 1_000_000_000n,
|
|
30
|
+
feeMultiplier: number = 1.25,
|
|
31
|
+
) {
|
|
32
|
+
this.provider = provider;
|
|
33
|
+
this.maxFeeRatePerGas = maxFeeRatePerGas;
|
|
34
|
+
this.priorityFee = priorityFee;
|
|
35
|
+
this.feeMultiplierPPM = BigInt(Math.floor(feeMultiplier * 1_000_000));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets evm fee rate
|
|
40
|
+
*
|
|
41
|
+
* @private
|
|
42
|
+
* @returns {Promise<bigint>} L1 gas price denominated in Wei
|
|
43
|
+
*/
|
|
44
|
+
private async _getFeeRate(): Promise<bigint> {
|
|
45
|
+
const block = await this.provider.getBlock("latest");
|
|
46
|
+
|
|
47
|
+
const baseFee = block.baseFeePerGas * this.feeMultiplierPPM / 1_000_000n;
|
|
48
|
+
this.logger.debug("_getFeeRate(): Base fee rate: "+baseFee.toString(10));
|
|
49
|
+
|
|
50
|
+
return baseFee;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets the gas price with caching, format: <gas price in Wei>;<transaction version: v1/v3>
|
|
55
|
+
*
|
|
56
|
+
* @private
|
|
57
|
+
*/
|
|
58
|
+
public async getFeeRate(): Promise<string> {
|
|
59
|
+
if(this.blockFeeCache==null || Date.now() - this.blockFeeCache.timestamp > MAX_FEE_AGE) {
|
|
60
|
+
let obj = {
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
feeRate: null
|
|
63
|
+
};
|
|
64
|
+
obj.feeRate = this._getFeeRate().catch(e => {
|
|
65
|
+
if(this.blockFeeCache===obj) this.blockFeeCache=null;
|
|
66
|
+
throw e;
|
|
67
|
+
});
|
|
68
|
+
this.blockFeeCache = obj;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let baseFee = await this.blockFeeCache.feeRate;
|
|
72
|
+
if(baseFee>this.maxFeeRatePerGas) baseFee = this.maxFeeRatePerGas;
|
|
73
|
+
|
|
74
|
+
const fee = baseFee.toString(10)+","+this.priorityFee.toString(10);
|
|
75
|
+
|
|
76
|
+
this.logger.debug("getFeeRate(): calculated fee: "+fee);
|
|
77
|
+
|
|
78
|
+
return fee;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Calculates the total gas fee paid for a given gas limit at a given fee rate
|
|
83
|
+
*
|
|
84
|
+
* @param gas
|
|
85
|
+
* @param feeRate
|
|
86
|
+
*/
|
|
87
|
+
public static getGasFee(gas: number, feeRate: string): bigint {
|
|
88
|
+
if(feeRate==null) return 0n;
|
|
89
|
+
|
|
90
|
+
const [baseFee, priorityFee] = feeRate.split(",");
|
|
91
|
+
|
|
92
|
+
return BigInt(gas) * (BigInt(baseFee) + BigInt(priorityFee));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public static applyFeeRate(tx: TransactionRequest, gas: number, feeRate: string) {
|
|
96
|
+
if(feeRate==null) return null;
|
|
97
|
+
|
|
98
|
+
const [baseFee, priorityFee] = feeRate.split(",");
|
|
99
|
+
|
|
100
|
+
tx.maxFeePerGas = BigInt(baseFee) + BigInt(priorityFee);
|
|
101
|
+
tx.maxPriorityFeePerGas = BigInt(priorityFee);
|
|
102
|
+
tx.gasLimit = BigInt(gas) + 21_000n;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {EVMSigner} from "../../wallet/EVMSigner";
|
|
2
|
+
import {EVMModule} from "../EVMModule";
|
|
3
|
+
import {EVMChainInterface} from "../EVMChainInterface";
|
|
4
|
+
import {sha256, verifyTypedData, TypedDataField} from "ethers";
|
|
5
|
+
|
|
6
|
+
const DataHash = [
|
|
7
|
+
{ name: "dataHash", type: "bytes32" }
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export class EVMSignatures extends EVMModule<any> {
|
|
11
|
+
|
|
12
|
+
private readonly domainName: string;
|
|
13
|
+
|
|
14
|
+
constructor(root: EVMChainInterface<any>, domainName: string = "atomiq.exchange") {
|
|
15
|
+
super(root);
|
|
16
|
+
this.domainName = domainName;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public async signTypedMessage(contract: string, signer: EVMSigner, type: TypedDataField[], typeName: string, message: object): Promise<string> {
|
|
20
|
+
return signer.account.signTypedData({
|
|
21
|
+
name: this.domainName,
|
|
22
|
+
version: "1",
|
|
23
|
+
chainId: BigInt(this.root.evmChainId),
|
|
24
|
+
verifyingContract: contract
|
|
25
|
+
}, {[typeName]: type}, message);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async isValidSignature(contract: string, signature: string, address: string, type: TypedDataField[], typeName: string, message: object): Promise<boolean> {
|
|
29
|
+
return Promise.resolve(address === verifyTypedData({
|
|
30
|
+
name: this.domainName,
|
|
31
|
+
version: "1",
|
|
32
|
+
chainId: BigInt(this.root.evmChainId),
|
|
33
|
+
verifyingContract: contract
|
|
34
|
+
}, {[typeName]: type}, message, signature));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
///////////////////
|
|
38
|
+
//// Data signatures
|
|
39
|
+
/**
|
|
40
|
+
* Produces a signature over the sha256 of a specified data Buffer, only works with providers which
|
|
41
|
+
* expose their private key (i.e. backend based, not browser wallet based)
|
|
42
|
+
*
|
|
43
|
+
* @param signer
|
|
44
|
+
* @param data data to sign
|
|
45
|
+
*/
|
|
46
|
+
public getDataSignature(signer: EVMSigner, data: Buffer): Promise<string> {
|
|
47
|
+
return signer.account.signTypedData({
|
|
48
|
+
name: this.domainName,
|
|
49
|
+
version: "1",
|
|
50
|
+
chainId: BigInt(this.root.evmChainId),
|
|
51
|
+
verifyingContract: "0x0000000000000000000000000000000000000000"
|
|
52
|
+
}, {DataHash}, {
|
|
53
|
+
dataHash: sha256(data)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Checks whether a signature is a valid signature produced by the account over a data message (computes
|
|
59
|
+
* sha256 hash of the message)
|
|
60
|
+
*
|
|
61
|
+
* @param data signed data
|
|
62
|
+
* @param signature data signature
|
|
63
|
+
* @param address public key of the signer
|
|
64
|
+
*/
|
|
65
|
+
public isValidDataSignature(data: Buffer, signature: string, address: string): Promise<boolean> {
|
|
66
|
+
return Promise.resolve(address === verifyTypedData({
|
|
67
|
+
name: this.domainName,
|
|
68
|
+
version: "1",
|
|
69
|
+
chainId: BigInt(this.root.evmChainId),
|
|
70
|
+
verifyingContract: "0x0000000000000000000000000000000000000000"
|
|
71
|
+
}, {DataHash}, {
|
|
72
|
+
dataHash: sha256(data)
|
|
73
|
+
}, signature));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {EVMModule} from "../EVMModule";
|
|
2
|
+
import {Contract, TransactionRequest} from "ethers";
|
|
3
|
+
import {ERC20Abi} from "./ERC20Abi";
|
|
4
|
+
import {EVMAddresses} from "./EVMAddresses";
|
|
5
|
+
import {EVMFees} from "./EVMFees";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export class EVMTokens extends EVMModule<any> {
|
|
9
|
+
|
|
10
|
+
public static readonly ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
11
|
+
|
|
12
|
+
public static readonly GasCosts = {
|
|
13
|
+
TRANSFER: 80_000,
|
|
14
|
+
APPROVE: 80_000
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
private getContract(address: string) {
|
|
18
|
+
return new Contract(address, ERC20Abi, this.root.provider);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
///////////////////
|
|
22
|
+
//// Tokens
|
|
23
|
+
/**
|
|
24
|
+
* Checks if the provided string is a valid starknet token
|
|
25
|
+
*
|
|
26
|
+
* @param token
|
|
27
|
+
*/
|
|
28
|
+
public isValidToken(token: string) {
|
|
29
|
+
return EVMAddresses.isValidAddress(token);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the token balance of the address
|
|
34
|
+
*
|
|
35
|
+
* @param address
|
|
36
|
+
* @param token
|
|
37
|
+
*/
|
|
38
|
+
public async getTokenBalance(address: string, token: string): Promise<bigint> {
|
|
39
|
+
let balance: bigint;
|
|
40
|
+
if(token === "0x0000000000000000000000000000000000000000") {
|
|
41
|
+
balance = await this.provider.getBalance(address);
|
|
42
|
+
} else {
|
|
43
|
+
const erc20 = this.getContract(token);
|
|
44
|
+
balance = await erc20.balanceOf(address);
|
|
45
|
+
}
|
|
46
|
+
this.logger.debug("getTokenBalance(): token balance fetched, token: "+token+
|
|
47
|
+
" address: "+address+" amount: "+balance.toString(10));
|
|
48
|
+
|
|
49
|
+
return balance;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns the native currency address
|
|
54
|
+
*/
|
|
55
|
+
public getNativeCurrencyAddress(): string {
|
|
56
|
+
return "0x0000000000000000000000000000000000000000";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
///////////////////
|
|
60
|
+
//// Transfers
|
|
61
|
+
/**
|
|
62
|
+
* Creates transactions for sending the over the tokens
|
|
63
|
+
*
|
|
64
|
+
* @param signer
|
|
65
|
+
* @param token token to send
|
|
66
|
+
* @param amount amount of the token to send
|
|
67
|
+
* @param recipient recipient's address
|
|
68
|
+
* @param feeRate fee rate to use for the transactions
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
public async Transfer(signer: string, token: string, amount: bigint, recipient: string, feeRate?: string): Promise<TransactionRequest> {
|
|
72
|
+
let tx: TransactionRequest;
|
|
73
|
+
if(token===this.getNativeCurrencyAddress()) {
|
|
74
|
+
tx = {
|
|
75
|
+
to: recipient,
|
|
76
|
+
value: amount
|
|
77
|
+
};
|
|
78
|
+
} else {
|
|
79
|
+
tx = await this.getContract(token).transfer.populateTransaction(recipient, amount);
|
|
80
|
+
}
|
|
81
|
+
tx.from = signer;
|
|
82
|
+
EVMFees.applyFeeRate(tx, EVMTokens.GasCosts.TRANSFER, feeRate ?? await this.root.Fees.getFeeRate());
|
|
83
|
+
|
|
84
|
+
this.logger.debug("txsTransfer(): transfer TX created, recipient: "+recipient.toString()+
|
|
85
|
+
" token: "+token.toString()+ " amount: "+amount.toString(10));
|
|
86
|
+
|
|
87
|
+
return tx;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
///////////////////
|
|
91
|
+
//// Approval
|
|
92
|
+
/**
|
|
93
|
+
* Creates transactions for approving spending of tokens
|
|
94
|
+
*
|
|
95
|
+
* @param signer
|
|
96
|
+
* @param token token to send
|
|
97
|
+
* @param amount amount of the token to send
|
|
98
|
+
* @param spender recipient's address
|
|
99
|
+
* @param feeRate fee rate to use for the transactions
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
public async Approve(signer: string, token: string, amount: bigint, spender: string, feeRate?: string): Promise<TransactionRequest> {
|
|
103
|
+
if(token===this.getNativeCurrencyAddress()) return null;
|
|
104
|
+
|
|
105
|
+
const tx = await this.getContract(token).approve.populateTransaction(spender, amount);
|
|
106
|
+
tx.from = signer;
|
|
107
|
+
EVMFees.applyFeeRate(tx, EVMTokens.GasCosts.APPROVE, feeRate ?? await this.root.Fees.getFeeRate());
|
|
108
|
+
|
|
109
|
+
this.logger.debug("txsTransfer(): approve TX created, spender: "+spender.toString()+
|
|
110
|
+
" token: "+token.toString()+ " amount: "+amount.toString(10));
|
|
111
|
+
|
|
112
|
+
return tx;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import {EVMModule} from "../EVMModule";
|
|
2
|
+
import {Transaction, TransactionRequest} from "ethers";
|
|
3
|
+
import {timeoutPromise} from "../../../utils/Utils";
|
|
4
|
+
import {EVMSigner} from "../../wallet/EVMSigner";
|
|
5
|
+
|
|
6
|
+
export type EVMTx = TransactionRequest;
|
|
7
|
+
|
|
8
|
+
export type EVMTxTrace = {
|
|
9
|
+
from: string,
|
|
10
|
+
gas: string,
|
|
11
|
+
gasused: string,
|
|
12
|
+
to: string,
|
|
13
|
+
input: string,
|
|
14
|
+
output: string,
|
|
15
|
+
error: string,
|
|
16
|
+
revertReason: string,
|
|
17
|
+
calls: EVMTxTrace[],
|
|
18
|
+
type: "CREATE" | "CALL" | "STATICCALL"
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class EVMTransactions extends EVMModule<any> {
|
|
22
|
+
|
|
23
|
+
private readonly latestConfirmedNonces: {[address: string]: number} = {};
|
|
24
|
+
|
|
25
|
+
private cbkBeforeTxSigned: (tx: TransactionRequest) => Promise<void>;
|
|
26
|
+
private cbkSendTransaction: (tx: string) => Promise<string>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Waits for transaction confirmation using WS subscription and occasional HTTP polling, also re-sends
|
|
30
|
+
* the transaction at regular interval
|
|
31
|
+
*
|
|
32
|
+
* @param tx EVM transaction to wait for confirmation for
|
|
33
|
+
* @param abortSignal signal to abort waiting for tx confirmation
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
private async confirmTransaction(tx: Transaction, abortSignal?: AbortSignal) {
|
|
37
|
+
let state = "pending";
|
|
38
|
+
while(state==="pending" || state==="not_found") {
|
|
39
|
+
await timeoutPromise(3000, abortSignal);
|
|
40
|
+
state = await this.getTxIdStatus(tx.hash);
|
|
41
|
+
//Don't re-send transactions
|
|
42
|
+
// if(state==="not_found") await this.sendSignedTransaction(tx).catch(e => {
|
|
43
|
+
// if(e.baseError?.code === 59) return; //Transaction already in the mempool
|
|
44
|
+
// this.logger.error("confirmTransaction(): Error on transaction re-send: ", e);
|
|
45
|
+
// });
|
|
46
|
+
}
|
|
47
|
+
const nextAccountNonce = tx.nonce + 1;
|
|
48
|
+
const currentNonce = this.latestConfirmedNonces[tx.from];
|
|
49
|
+
if(currentNonce==null || nextAccountNonce > currentNonce) {
|
|
50
|
+
this.latestConfirmedNonces[tx.from] = nextAccountNonce;
|
|
51
|
+
}
|
|
52
|
+
if(state==="reverted") throw new Error("Transaction reverted!");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Prepares starknet transactions, checks if the account is deployed, assigns nonces if needed & calls beforeTxSigned callback
|
|
57
|
+
*
|
|
58
|
+
* @param signer
|
|
59
|
+
* @param txs
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
private async prepareTransactions(signer: EVMSigner, txs: TransactionRequest[]): Promise<void> {
|
|
63
|
+
let nonce: number = (await signer.getNonce()) ?? await this.root.provider.getTransactionCount(signer.getAddress(), "pending");
|
|
64
|
+
const latestConfirmedNonce = this.latestConfirmedNonces[signer.getAddress()];
|
|
65
|
+
if(latestConfirmedNonce!=null && latestConfirmedNonce > nonce) {
|
|
66
|
+
this.logger.debug("prepareTransactions(): Using nonce from local cache!");
|
|
67
|
+
nonce = latestConfirmedNonce;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for(let i=0;i<txs.length;i++) {
|
|
71
|
+
const tx = txs[i];
|
|
72
|
+
tx.chainId = this.root.evmChainId;
|
|
73
|
+
tx.from = signer.getAddress();
|
|
74
|
+
if(tx.nonce!=null) nonce = tx.nonce; //Take the nonce from last tx
|
|
75
|
+
if(nonce==null) nonce = await this.root.provider.getTransactionCount(signer.getAddress(), "pending"); //Fetch the nonce
|
|
76
|
+
if(tx.nonce==null) tx.nonce = nonce;
|
|
77
|
+
|
|
78
|
+
this.logger.debug("sendAndConfirm(): transaction prepared ("+(i+1)+"/"+txs.length+"), nonce: "+tx.nonce);
|
|
79
|
+
|
|
80
|
+
nonce++;
|
|
81
|
+
|
|
82
|
+
if(this.cbkBeforeTxSigned!=null) await this.cbkBeforeTxSigned(tx);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sends out a signed transaction to the RPC
|
|
88
|
+
*
|
|
89
|
+
* @param tx EVM tx to send
|
|
90
|
+
* @param onBeforePublish a callback called before every transaction is published
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
private async sendSignedTransaction(
|
|
94
|
+
tx: Transaction,
|
|
95
|
+
onBeforePublish?: (txId: string, rawTx: string) => Promise<void>,
|
|
96
|
+
): Promise<string> {
|
|
97
|
+
if(onBeforePublish!=null) await onBeforePublish(tx.hash, await this.serializeTx(tx));
|
|
98
|
+
this.logger.debug("sendSignedTransaction(): sending transaction: ", tx.hash);
|
|
99
|
+
|
|
100
|
+
const serializedTx = tx.serialized;
|
|
101
|
+
|
|
102
|
+
let result: string;
|
|
103
|
+
if(this.cbkSendTransaction!=null) result = await this.cbkSendTransaction(serializedTx);
|
|
104
|
+
if(result==null) {
|
|
105
|
+
const broadcastResult = await this.provider.broadcastTransaction(tx.serialized);
|
|
106
|
+
result = broadcastResult.hash;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.logger.info("sendSignedTransaction(): tx sent, txHash: "+result);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Prepares, signs, sends (in parallel or sequentially) & optionally waits for confirmation
|
|
115
|
+
* of a batch of EVM transactions
|
|
116
|
+
*
|
|
117
|
+
* @param signer
|
|
118
|
+
* @param txs transactions to send
|
|
119
|
+
* @param waitForConfirmation whether to wait for transaction confirmations (this also makes sure the transactions
|
|
120
|
+
* are re-sent at regular intervals)
|
|
121
|
+
* @param abortSignal abort signal to abort waiting for transaction confirmations
|
|
122
|
+
* @param parallel whether the send all the transaction at once in parallel or sequentially (such that transactions
|
|
123
|
+
* are executed in order)
|
|
124
|
+
* @param onBeforePublish a callback called before every transaction is published
|
|
125
|
+
*/
|
|
126
|
+
public async sendAndConfirm(signer: EVMSigner, txs: TransactionRequest[], waitForConfirmation?: boolean, abortSignal?: AbortSignal, parallel?: boolean, onBeforePublish?: (txId: string, rawTx: string) => Promise<void>): Promise<string[]> {
|
|
127
|
+
await this.prepareTransactions(signer, txs);
|
|
128
|
+
const signedTxs: Transaction[] = [];
|
|
129
|
+
|
|
130
|
+
//TODO: Maybe don't separate the signing process from the sending when using browser-based wallet,
|
|
131
|
+
// like with Starknet
|
|
132
|
+
for(let i=0;i<txs.length;i++) {
|
|
133
|
+
const tx = txs[i];
|
|
134
|
+
const signedTx = Transaction.from(await signer.account.signTransaction(tx));
|
|
135
|
+
signedTxs.push(signedTx);
|
|
136
|
+
this.logger.debug("sendAndConfirm(): transaction signed ("+(i+1)+"/"+txs.length+"): "+signedTx);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.logger.debug("sendAndConfirm(): sending transactions, count: "+txs.length+
|
|
140
|
+
" waitForConfirmation: "+waitForConfirmation+" parallel: "+parallel);
|
|
141
|
+
|
|
142
|
+
const txIds: string[] = [];
|
|
143
|
+
if(parallel) {
|
|
144
|
+
const promises: Promise<void>[] = [];
|
|
145
|
+
for(let i=0;i<signedTxs.length;i++) {
|
|
146
|
+
const signedTx = signedTxs[i];
|
|
147
|
+
const txId = await this.sendSignedTransaction(signedTx, onBeforePublish);
|
|
148
|
+
if(waitForConfirmation) promises.push(this.confirmTransaction(signedTx, abortSignal));
|
|
149
|
+
txIds.push(txId);
|
|
150
|
+
this.logger.debug("sendAndConfirm(): transaction sent ("+(i+1)+"/"+signedTxs.length+"): "+signedTx.hash);
|
|
151
|
+
}
|
|
152
|
+
if(promises.length>0) await Promise.all(promises);
|
|
153
|
+
} else {
|
|
154
|
+
for(let i=0;i<signedTxs.length;i++) {
|
|
155
|
+
const signedTx = signedTxs[i];
|
|
156
|
+
const txId = await this.sendSignedTransaction(signedTx, onBeforePublish);
|
|
157
|
+
const confirmPromise = this.confirmTransaction(signedTx, abortSignal);
|
|
158
|
+
this.logger.debug("sendAndConfirm(): transaction sent ("+(i+1)+"/"+txs.length+"): "+signedTx.hash);
|
|
159
|
+
//Don't await the last promise when !waitForConfirmation
|
|
160
|
+
if(i<txs.length-1 || waitForConfirmation) await confirmPromise;
|
|
161
|
+
txIds.push(txId);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.logger.info("sendAndConfirm(): sent transactions, count: "+txs.length+
|
|
166
|
+
" waitForConfirmation: "+waitForConfirmation+" parallel: "+parallel);
|
|
167
|
+
|
|
168
|
+
return txIds;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Serializes the signed EVM transaction
|
|
173
|
+
*
|
|
174
|
+
* @param tx
|
|
175
|
+
*/
|
|
176
|
+
public serializeTx(tx: Transaction): Promise<string> {
|
|
177
|
+
return Promise.resolve(tx.serialized);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Deserializes signed EVM transaction
|
|
182
|
+
*
|
|
183
|
+
* @param txData
|
|
184
|
+
*/
|
|
185
|
+
public deserializeTx(txData: string): Promise<Transaction> {
|
|
186
|
+
return Promise.resolve(Transaction.from(txData));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Gets the status of the raw starknet transaction
|
|
191
|
+
*
|
|
192
|
+
* @param tx
|
|
193
|
+
*/
|
|
194
|
+
public async getTxStatus(tx: string): Promise<"pending" | "success" | "not_found" | "reverted"> {
|
|
195
|
+
const parsedTx: Transaction = await this.deserializeTx(tx);
|
|
196
|
+
return await this.getTxIdStatus(parsedTx.hash);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Gets the status of the starknet transaction with a specific txId
|
|
201
|
+
*
|
|
202
|
+
* @param txId
|
|
203
|
+
*/
|
|
204
|
+
public async getTxIdStatus(txId: string): Promise<"pending" | "success" | "not_found" | "reverted"> {
|
|
205
|
+
const txResponse = await this.provider.getTransaction(txId);
|
|
206
|
+
if(txResponse==null) return "not_found";
|
|
207
|
+
if(txResponse.blockHash==null) return "pending";
|
|
208
|
+
|
|
209
|
+
const [safeBlockNumber, txReceipt] = await Promise.all([
|
|
210
|
+
this.root.config.safeBlockTag==="latest" ? Promise.resolve(null) : this.provider.getBlock(this.root.config.safeBlockTag).then(res => res.number),
|
|
211
|
+
this.provider.getTransactionReceipt(txId)
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
if(txReceipt==null || (safeBlockNumber!=null && txReceipt.blockNumber < safeBlockNumber)) return "pending";
|
|
215
|
+
if(txReceipt.status===0) return "reverted";
|
|
216
|
+
return "success";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
public onBeforeTxSigned(callback: (tx: TransactionRequest) => Promise<void>): void {
|
|
220
|
+
this.cbkBeforeTxSigned = callback;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public offBeforeTxSigned(callback: (tx: TransactionRequest) => Promise<void>): boolean {
|
|
224
|
+
this.cbkBeforeTxSigned = null;
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public onSendTransaction(callback: (tx: string) => Promise<string>): void {
|
|
229
|
+
this.cbkSendTransaction = callback;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public offSendTransaction(callback: (tx: string) => Promise<string>): boolean {
|
|
233
|
+
this.cbkSendTransaction = null;
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public traceTransaction(txId: string): Promise<EVMTxTrace> {
|
|
238
|
+
return this.provider.send("debug_traceTransaction", [
|
|
239
|
+
txId,
|
|
240
|
+
{
|
|
241
|
+
tracer: "callTracer"
|
|
242
|
+
}
|
|
243
|
+
]);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
}
|