@atomiqlabs/chain-evm 1.0.0-dev.35 → 1.0.0-dev.37

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.
Files changed (161) hide show
  1. package/LICENSE +201 -201
  2. package/dist/chains/citrea/CitreaBtcRelay.d.ts +21 -21
  3. package/dist/chains/citrea/CitreaBtcRelay.js +43 -43
  4. package/dist/chains/citrea/CitreaChainType.d.ts +13 -13
  5. package/dist/chains/citrea/CitreaChainType.js +2 -2
  6. package/dist/chains/citrea/CitreaFees.d.ts +29 -29
  7. package/dist/chains/citrea/CitreaFees.js +67 -67
  8. package/dist/chains/citrea/CitreaInitializer.d.ts +30 -30
  9. package/dist/chains/citrea/CitreaInitializer.js +127 -127
  10. package/dist/chains/citrea/CitreaSpvVaultContract.d.ts +15 -15
  11. package/dist/chains/citrea/CitreaSpvVaultContract.js +74 -74
  12. package/dist/chains/citrea/CitreaSwapContract.d.ts +22 -22
  13. package/dist/chains/citrea/CitreaSwapContract.js +96 -96
  14. package/dist/chains/citrea/CitreaTokens.d.ts +9 -9
  15. package/dist/chains/citrea/CitreaTokens.js +20 -20
  16. package/dist/evm/btcrelay/BtcRelayAbi.d.ts +198 -198
  17. package/dist/evm/btcrelay/BtcRelayAbi.js +261 -261
  18. package/dist/evm/btcrelay/BtcRelayTypechain.d.ts +172 -172
  19. package/dist/evm/btcrelay/BtcRelayTypechain.js +2 -2
  20. package/dist/evm/btcrelay/EVMBtcRelay.d.ts +195 -195
  21. package/dist/evm/btcrelay/EVMBtcRelay.js +423 -423
  22. package/dist/evm/btcrelay/headers/EVMBtcHeader.d.ts +33 -33
  23. package/dist/evm/btcrelay/headers/EVMBtcHeader.js +84 -84
  24. package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.d.ts +56 -56
  25. package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.js +123 -123
  26. package/dist/evm/chain/EVMChainInterface.d.ts +51 -51
  27. package/dist/evm/chain/EVMChainInterface.js +89 -89
  28. package/dist/evm/chain/EVMModule.d.ts +9 -9
  29. package/dist/evm/chain/EVMModule.js +13 -13
  30. package/dist/evm/chain/modules/ERC20Abi.d.ts +168 -168
  31. package/dist/evm/chain/modules/ERC20Abi.js +225 -225
  32. package/dist/evm/chain/modules/EVMAddresses.d.ts +10 -10
  33. package/dist/evm/chain/modules/EVMAddresses.js +30 -30
  34. package/dist/evm/chain/modules/EVMBlocks.d.ts +20 -20
  35. package/dist/evm/chain/modules/EVMBlocks.js +64 -64
  36. package/dist/evm/chain/modules/EVMEvents.d.ts +36 -36
  37. package/dist/evm/chain/modules/EVMEvents.js +122 -122
  38. package/dist/evm/chain/modules/EVMFees.d.ts +36 -36
  39. package/dist/evm/chain/modules/EVMFees.js +73 -73
  40. package/dist/evm/chain/modules/EVMSignatures.d.ts +29 -29
  41. package/dist/evm/chain/modules/EVMSignatures.js +68 -68
  42. package/dist/evm/chain/modules/EVMTokens.d.ts +70 -51
  43. package/dist/evm/chain/modules/EVMTokens.js +142 -113
  44. package/dist/evm/chain/modules/EVMTransactions.d.ts +89 -89
  45. package/dist/evm/chain/modules/EVMTransactions.js +230 -216
  46. package/dist/evm/contract/EVMContractBase.d.ts +22 -22
  47. package/dist/evm/contract/EVMContractBase.js +34 -34
  48. package/dist/evm/contract/EVMContractModule.d.ts +8 -8
  49. package/dist/evm/contract/EVMContractModule.js +11 -11
  50. package/dist/evm/contract/modules/EVMContractEvents.d.ts +42 -42
  51. package/dist/evm/contract/modules/EVMContractEvents.js +75 -75
  52. package/dist/evm/events/EVMChainEvents.d.ts +22 -22
  53. package/dist/evm/events/EVMChainEvents.js +67 -67
  54. package/dist/evm/events/EVMChainEventsBrowser.d.ts +86 -86
  55. package/dist/evm/events/EVMChainEventsBrowser.js +294 -294
  56. package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +78 -78
  57. package/dist/evm/spv_swap/EVMSpvVaultContract.js +478 -480
  58. package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +39 -39
  59. package/dist/evm/spv_swap/EVMSpvVaultData.js +180 -180
  60. package/dist/evm/spv_swap/EVMSpvWithdrawalData.d.ts +19 -19
  61. package/dist/evm/spv_swap/EVMSpvWithdrawalData.js +55 -55
  62. package/dist/evm/spv_swap/SpvVaultContractAbi.d.ts +91 -91
  63. package/dist/evm/spv_swap/SpvVaultContractAbi.js +849 -849
  64. package/dist/evm/spv_swap/SpvVaultContractTypechain.d.ts +450 -450
  65. package/dist/evm/spv_swap/SpvVaultContractTypechain.js +2 -2
  66. package/dist/evm/swaps/EVMSwapContract.d.ts +193 -193
  67. package/dist/evm/swaps/EVMSwapContract.js +374 -374
  68. package/dist/evm/swaps/EVMSwapData.d.ts +66 -66
  69. package/dist/evm/swaps/EVMSwapData.js +260 -260
  70. package/dist/evm/swaps/EVMSwapModule.d.ts +9 -9
  71. package/dist/evm/swaps/EVMSwapModule.js +11 -11
  72. package/dist/evm/swaps/EscrowManagerAbi.d.ts +120 -120
  73. package/dist/evm/swaps/EscrowManagerAbi.js +985 -985
  74. package/dist/evm/swaps/EscrowManagerTypechain.d.ts +475 -475
  75. package/dist/evm/swaps/EscrowManagerTypechain.js +2 -2
  76. package/dist/evm/swaps/handlers/IHandler.d.ts +13 -13
  77. package/dist/evm/swaps/handlers/IHandler.js +2 -2
  78. package/dist/evm/swaps/handlers/claim/ClaimHandlers.d.ts +10 -10
  79. package/dist/evm/swaps/handlers/claim/ClaimHandlers.js +13 -13
  80. package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.d.ts +20 -20
  81. package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.js +39 -39
  82. package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -24
  83. package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +59 -59
  84. package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -25
  85. package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +51 -51
  86. package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +21 -21
  87. package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +28 -28
  88. package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +48 -48
  89. package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +63 -63
  90. package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -17
  91. package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.js +28 -28
  92. package/dist/evm/swaps/modules/EVMLpVault.d.ts +69 -69
  93. package/dist/evm/swaps/modules/EVMLpVault.js +134 -131
  94. package/dist/evm/swaps/modules/EVMSwapClaim.d.ts +54 -54
  95. package/dist/evm/swaps/modules/EVMSwapClaim.js +137 -137
  96. package/dist/evm/swaps/modules/EVMSwapInit.d.ts +88 -88
  97. package/dist/evm/swaps/modules/EVMSwapInit.js +274 -275
  98. package/dist/evm/swaps/modules/EVMSwapRefund.d.ts +62 -62
  99. package/dist/evm/swaps/modules/EVMSwapRefund.js +167 -167
  100. package/dist/evm/typechain/common.d.ts +50 -50
  101. package/dist/evm/typechain/common.js +2 -2
  102. package/dist/evm/wallet/EVMSigner.d.ts +10 -9
  103. package/dist/evm/wallet/EVMSigner.js +17 -16
  104. package/dist/index.d.ts +38 -38
  105. package/dist/index.js +54 -54
  106. package/dist/utils/Utils.d.ts +15 -15
  107. package/dist/utils/Utils.js +71 -71
  108. package/package.json +37 -37
  109. package/src/chains/citrea/CitreaBtcRelay.ts +57 -57
  110. package/src/chains/citrea/CitreaChainType.ts +28 -28
  111. package/src/chains/citrea/CitreaFees.ts +77 -77
  112. package/src/chains/citrea/CitreaInitializer.ts +178 -178
  113. package/src/chains/citrea/CitreaSpvVaultContract.ts +75 -75
  114. package/src/chains/citrea/CitreaSwapContract.ts +102 -102
  115. package/src/chains/citrea/CitreaTokens.ts +21 -21
  116. package/src/evm/btcrelay/BtcRelayAbi.ts +258 -258
  117. package/src/evm/btcrelay/BtcRelayTypechain.ts +371 -371
  118. package/src/evm/btcrelay/EVMBtcRelay.ts +522 -522
  119. package/src/evm/btcrelay/headers/EVMBtcHeader.ts +109 -109
  120. package/src/evm/btcrelay/headers/EVMBtcStoredHeader.ts +152 -152
  121. package/src/evm/chain/EVMChainInterface.ts +155 -155
  122. package/src/evm/chain/EVMModule.ts +21 -21
  123. package/src/evm/chain/modules/ERC20Abi.ts +222 -222
  124. package/src/evm/chain/modules/EVMAddresses.ts +28 -28
  125. package/src/evm/chain/modules/EVMBlocks.ts +75 -75
  126. package/src/evm/chain/modules/EVMEvents.ts +139 -139
  127. package/src/evm/chain/modules/EVMFees.ts +104 -104
  128. package/src/evm/chain/modules/EVMSignatures.ts +76 -76
  129. package/src/evm/chain/modules/EVMTokens.ts +155 -126
  130. package/src/evm/chain/modules/EVMTransactions.ts +257 -246
  131. package/src/evm/contract/EVMContractBase.ts +63 -63
  132. package/src/evm/contract/EVMContractModule.ts +16 -16
  133. package/src/evm/contract/modules/EVMContractEvents.ts +102 -102
  134. package/src/evm/events/EVMChainEvents.ts +81 -81
  135. package/src/evm/events/EVMChainEventsBrowser.ts +390 -390
  136. package/src/evm/spv_swap/EVMSpvVaultContract.ts +608 -603
  137. package/src/evm/spv_swap/EVMSpvVaultData.ts +224 -224
  138. package/src/evm/spv_swap/EVMSpvWithdrawalData.ts +70 -70
  139. package/src/evm/spv_swap/SpvVaultContractAbi.ts +846 -846
  140. package/src/evm/spv_swap/SpvVaultContractTypechain.ts +685 -685
  141. package/src/evm/swaps/EVMSwapContract.ts +592 -592
  142. package/src/evm/swaps/EVMSwapData.ts +378 -378
  143. package/src/evm/swaps/EVMSwapModule.ts +16 -16
  144. package/src/evm/swaps/EscrowManagerAbi.ts +982 -982
  145. package/src/evm/swaps/EscrowManagerTypechain.ts +723 -723
  146. package/src/evm/swaps/handlers/IHandler.ts +17 -17
  147. package/src/evm/swaps/handlers/claim/ClaimHandlers.ts +20 -20
  148. package/src/evm/swaps/handlers/claim/HashlockClaimHandler.ts +46 -46
  149. package/src/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +82 -82
  150. package/src/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +76 -76
  151. package/src/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +46 -46
  152. package/src/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +115 -115
  153. package/src/evm/swaps/handlers/refund/TimelockRefundHandler.ts +37 -37
  154. package/src/evm/swaps/modules/EVMLpVault.ts +154 -152
  155. package/src/evm/swaps/modules/EVMSwapClaim.ts +172 -172
  156. package/src/evm/swaps/modules/EVMSwapInit.ts +328 -325
  157. package/src/evm/swaps/modules/EVMSwapRefund.ts +229 -229
  158. package/src/evm/typechain/common.ts +131 -131
  159. package/src/evm/wallet/EVMSigner.ts +25 -23
  160. package/src/index.ts +45 -45
  161. package/src/utils/Utils.ts +81 -81
@@ -1,423 +1,423 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EVMBtcRelay = void 0;
4
- const base_1 = require("@atomiqlabs/base");
5
- const EVMBtcHeader_1 = require("./headers/EVMBtcHeader");
6
- const Utils_1 = require("../../utils/Utils");
7
- const EVMContractBase_1 = require("../contract/EVMContractBase");
8
- const EVMBtcStoredHeader_1 = require("./headers/EVMBtcStoredHeader");
9
- const EVMFees_1 = require("../chain/modules/EVMFees");
10
- const BtcRelayAbi_1 = require("./BtcRelayAbi");
11
- const ethers_1 = require("ethers");
12
- function serializeBlockHeader(e) {
13
- return new EVMBtcHeader_1.EVMBtcHeader({
14
- version: e.getVersion(),
15
- previousBlockhash: Buffer.from(e.getPrevBlockhash(), "hex").reverse(),
16
- merkleRoot: Buffer.from(e.getMerkleRoot(), "hex").reverse(),
17
- timestamp: e.getTimestamp(),
18
- nbits: e.getNbits(),
19
- nonce: e.getNonce(),
20
- hash: Buffer.from(e.getHash(), "hex").reverse()
21
- });
22
- }
23
- const logger = (0, Utils_1.getLogger)("EVMBtcRelay: ");
24
- class EVMBtcRelay extends EVMContractBase_1.EVMContractBase {
25
- async SaveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
26
- const tx = await this.contract.submitMainBlockheaders.populateTransaction(Buffer.concat([
27
- storedHeader.serialize(),
28
- Buffer.concat(mainHeaders.map(header => header.serializeCompact()))
29
- ]));
30
- tx.from = signer;
31
- EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * mainHeaders.length), feeRate);
32
- return tx;
33
- }
34
- async SaveShortForkHeaders(signer, forkHeaders, storedHeader, feeRate) {
35
- const tx = await this.contract.submitShortForkBlockheaders.populateTransaction(Buffer.concat([
36
- storedHeader.serialize(),
37
- Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
38
- ]));
39
- tx.from = signer;
40
- EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * forkHeaders.length), feeRate);
41
- return tx;
42
- }
43
- async SaveLongForkHeaders(signer, forkId, forkHeaders, storedHeader, feeRate, totalForkHeaders = 100) {
44
- const tx = await this.contract.submitForkBlockheaders.populateTransaction(forkId, Buffer.concat([
45
- storedHeader.serialize(),
46
- Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
47
- ]));
48
- tx.from = signer;
49
- EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_FORK + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORK * forkHeaders.length) + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORKED * totalForkHeaders), feeRate);
50
- return tx;
51
- }
52
- constructor(chainInterface, bitcoinRpc, bitcoinNetwork, contractAddress, contractDeploymentHeight) {
53
- super(chainInterface, contractAddress, BtcRelayAbi_1.BtcRelayAbi, contractDeploymentHeight);
54
- this.maxHeadersPerTx = 100;
55
- this.maxForkHeadersPerTx = 50;
56
- this.maxShortForkHeadersPerTx = 100;
57
- this.bitcoinRpc = bitcoinRpc;
58
- }
59
- /**
60
- * Computes subsequent commited headers as they will appear on the blockchain when transactions
61
- * are submitted & confirmed
62
- *
63
- * @param initialStoredHeader
64
- * @param syncedHeaders
65
- * @private
66
- */
67
- computeCommitedHeaders(initialStoredHeader, syncedHeaders) {
68
- const computedCommitedHeaders = [initialStoredHeader];
69
- for (let blockHeader of syncedHeaders) {
70
- computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length - 1].computeNext(blockHeader));
71
- }
72
- return computedCommitedHeaders;
73
- }
74
- /**
75
- * A common logic for submitting blockheaders in a transaction
76
- *
77
- * @param signer
78
- * @param headers headers to sync to the btc relay
79
- * @param storedHeader current latest stored block header for a given fork
80
- * @param tipWork work of the current tip in a given fork
81
- * @param forkId forkId to submit to, forkId=0 means main chain, forkId=-1 means short fork
82
- * @param feeRate feeRate for the transaction
83
- * @param totalForkHeaders Total number of headers in a fork
84
- * @private
85
- */
86
- async _saveHeaders(signer, headers, storedHeader, tipWork, forkId, feeRate, totalForkHeaders) {
87
- const blockHeaderObj = headers.map(serializeBlockHeader);
88
- let tx;
89
- switch (forkId) {
90
- case -1:
91
- tx = await this.SaveShortForkHeaders(signer, blockHeaderObj, storedHeader, feeRate);
92
- break;
93
- case 0:
94
- tx = await this.SaveMainHeaders(signer, blockHeaderObj, storedHeader, feeRate);
95
- break;
96
- default:
97
- tx = await this.SaveLongForkHeaders(signer, forkId, blockHeaderObj, storedHeader, feeRate, totalForkHeaders);
98
- break;
99
- }
100
- const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
101
- const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length - 1];
102
- if (forkId !== 0 && base_1.StatePredictorUtils.gtBuffer(lastStoredHeader.getBlockHash(), tipWork)) {
103
- //Fork's work is higher than main chain's work, this fork will become a main chain
104
- forkId = 0;
105
- }
106
- return {
107
- forkId: forkId,
108
- lastStoredHeader,
109
- tx,
110
- computedCommitedHeaders
111
- };
112
- }
113
- async findStoredBlockheaderInTraces(txTrace, commitHash) {
114
- if (txTrace.to.toLowerCase() === (await this.contract.getAddress()).toLowerCase()) {
115
- let dataBuffer;
116
- if (txTrace.type === "CREATE") {
117
- dataBuffer = Buffer.from(txTrace.input.substring(txTrace.input.length - 384, txTrace.input.length - 64), "hex");
118
- }
119
- else {
120
- const result = this.parseCalldata(txTrace.input);
121
- if (result != null) {
122
- if (result.name === "submitMainBlockheaders" || result.name === "submitShortForkBlockheaders") {
123
- const functionCall = result;
124
- dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[0]).substring(2), "hex");
125
- }
126
- else if (result.name === "submitForkBlockheaders") {
127
- const functionCall = result;
128
- dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[1]).substring(2), "hex");
129
- }
130
- }
131
- }
132
- if (dataBuffer != null) {
133
- let storedHeader = EVMBtcStoredHeader_1.EVMBtcStoredHeader.deserialize(dataBuffer.subarray(0, 160));
134
- if (storedHeader.getCommitHash() === commitHash)
135
- return storedHeader;
136
- for (let i = 160; i < dataBuffer.length; i += 48) {
137
- const blockHeader = EVMBtcHeader_1.EVMBtcHeader.deserialize(dataBuffer.subarray(i, i + 48));
138
- storedHeader = storedHeader.computeNext(blockHeader);
139
- if (storedHeader.getCommitHash() === commitHash)
140
- return storedHeader;
141
- }
142
- }
143
- }
144
- if (txTrace.calls != null) {
145
- for (let call of txTrace.calls) {
146
- const result = await this.findStoredBlockheaderInTraces(call, commitHash);
147
- if (result != null)
148
- return result;
149
- }
150
- }
151
- return null;
152
- }
153
- getBlock(commitHash, blockHash) {
154
- return this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], [
155
- commitHash,
156
- blockHash == null ? null : "0x" + Buffer.from([...blockHash]).reverse().toString("hex")
157
- ], async (event) => {
158
- const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
159
- const storedBlockheader = await this.findStoredBlockheaderInTraces(txTrace, event.args.commitHash);
160
- if (storedBlockheader != null)
161
- return [storedBlockheader, event.args.commitHash];
162
- });
163
- }
164
- async getBlockHeight() {
165
- return Number(await this.contract.getBlockheight());
166
- }
167
- /**
168
- * Returns data about current main chain tip stored in the btc relay
169
- */
170
- async getTipData() {
171
- const commitHash = await this.contract.getTipCommitHash();
172
- if (commitHash == null || BigInt(commitHash) === BigInt(0))
173
- return null;
174
- const result = await this.getBlock(commitHash);
175
- if (result == null)
176
- return null;
177
- const storedBlockHeader = result[0];
178
- return {
179
- blockheight: storedBlockHeader.getBlockheight(),
180
- commitHash: commitHash,
181
- blockhash: storedBlockHeader.getBlockHash().toString("hex"),
182
- chainWork: storedBlockHeader.getChainWork()
183
- };
184
- }
185
- /**
186
- * Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
187
- * btc relay contract is not synced up to the desired blockheight
188
- *
189
- * @param blockData
190
- * @param requiredBlockheight
191
- */
192
- async retrieveLogAndBlockheight(blockData, requiredBlockheight) {
193
- //TODO: we can fetch the blockheight and events in parallel
194
- const blockHeight = await this.getBlockHeight();
195
- if (requiredBlockheight != null && blockHeight < requiredBlockheight) {
196
- return null;
197
- }
198
- const result = await this.getBlock(null, Buffer.from(blockData.blockhash, "hex"));
199
- if (result == null)
200
- return null;
201
- const [storedBlockHeader, commitHash] = result;
202
- //Check if block is part of the main chain
203
- const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
204
- if (chainCommitment !== commitHash)
205
- return null;
206
- logger.debug("retrieveLogAndBlockheight(): block found," +
207
- " commit hash: " + commitHash + " blockhash: " + blockData.blockhash + " current btc relay height: " + blockHeight);
208
- return { header: storedBlockHeader, height: blockHeight };
209
- }
210
- /**
211
- * Retrieves blockheader data by blockheader's commit hash,
212
- *
213
- * @param commitmentHashStr
214
- * @param blockData
215
- */
216
- async retrieveLogByCommitHash(commitmentHashStr, blockData) {
217
- const result = await this.getBlock(commitmentHashStr, Buffer.from(blockData.blockhash, "hex"));
218
- if (result == null)
219
- return null;
220
- const [storedBlockHeader, commitHash] = result;
221
- //Check if block is part of the main chain
222
- const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
223
- if (chainCommitment !== commitHash)
224
- return null;
225
- logger.debug("retrieveLogByCommitHash(): block found," +
226
- " commit hash: " + commitmentHashStr + " blockhash: " + blockData.blockhash + " height: " + storedBlockHeader.blockHeight);
227
- return storedBlockHeader;
228
- }
229
- /**
230
- * Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
231
- */
232
- async retrieveLatestKnownBlockLog() {
233
- const data = await this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], null, async (event) => {
234
- const blockHashHex = Buffer.from(event.args.blockHash.substring(2), "hex").reverse().toString("hex");
235
- const commitHash = event.args.commitHash;
236
- const isInBtcMainChain = await this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false);
237
- if (!isInBtcMainChain)
238
- return null;
239
- const blockHeader = await this.bitcoinRpc.getBlockHeader(blockHashHex);
240
- if (commitHash !== await this.contract.getCommitHash(blockHeader.getHeight()))
241
- return null;
242
- const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
243
- const storedHeader = await this.findStoredBlockheaderInTraces(txTrace, commitHash);
244
- if (storedHeader == null)
245
- return null;
246
- return {
247
- resultStoredHeader: storedHeader,
248
- resultBitcoinHeader: blockHeader,
249
- commitHash: commitHash
250
- };
251
- });
252
- if (data != null)
253
- logger.debug("retrieveLatestKnownBlockLog(): block found," +
254
- " commit hash: " + data.commitHash + " blockhash: " + data.resultBitcoinHeader.getHash() +
255
- " height: " + data.resultStoredHeader.getBlockheight());
256
- return data;
257
- }
258
- /**
259
- * Saves blockheaders as a bitcoin main chain to the btc relay
260
- *
261
- * @param signer
262
- * @param mainHeaders
263
- * @param storedHeader
264
- * @param feeRate
265
- */
266
- async saveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
267
- feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
268
- logger.debug("saveMainHeaders(): submitting main blockheaders, count: " + mainHeaders.length);
269
- return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate, 0);
270
- }
271
- /**
272
- * Creates a new long fork and submits the headers to it
273
- *
274
- * @param signer
275
- * @param forkHeaders
276
- * @param storedHeader
277
- * @param tipWork
278
- * @param feeRate
279
- */
280
- async saveNewForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
281
- let forkId = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
282
- feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
283
- logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
284
- " count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
285
- return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, forkHeaders.length);
286
- }
287
- /**
288
- * Continues submitting blockheaders to a given fork
289
- *
290
- * @param signer
291
- * @param forkHeaders
292
- * @param storedHeader
293
- * @param forkId
294
- * @param tipWork
295
- * @param feeRate
296
- */
297
- async saveForkHeaders(signer, forkHeaders, storedHeader, forkId, tipWork, feeRate) {
298
- feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
299
- logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
300
- " count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
301
- return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, 100);
302
- }
303
- /**
304
- * Submits short fork with given blockheaders
305
- *
306
- * @param signer
307
- * @param forkHeaders
308
- * @param storedHeader
309
- * @param tipWork
310
- * @param feeRate
311
- */
312
- async saveShortForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
313
- feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
314
- logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
315
- " count: " + forkHeaders.length);
316
- return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate, 0);
317
- }
318
- /**
319
- * Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
320
- *
321
- * @param requiredBlockheight
322
- * @param feeRate
323
- */
324
- async estimateSynchronizeFee(requiredBlockheight, feeRate) {
325
- feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
326
- const tipData = await this.getTipData();
327
- const currBlockheight = tipData.blockheight;
328
- const blockheightDelta = requiredBlockheight - currBlockheight;
329
- if (blockheightDelta <= 0)
330
- return 0n;
331
- const synchronizationFee = (BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate))
332
- + EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_BASE_MAIN * Math.ceil(blockheightDelta / this.maxHeadersPerTx), feeRate);
333
- logger.debug("estimateSynchronizeFee(): required blockheight: " + requiredBlockheight +
334
- " blockheight delta: " + blockheightDelta + " fee: " + synchronizationFee.toString(10));
335
- return synchronizationFee;
336
- }
337
- /**
338
- * Returns fee required (in native token) to synchronize a single block to btc relay
339
- *
340
- * @param feeRate
341
- */
342
- async getFeePerBlock(feeRate) {
343
- feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
344
- return EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER, feeRate);
345
- }
346
- /**
347
- * Gets fee rate required for submitting blockheaders to the main chain
348
- */
349
- getMainFeeRate(signer) {
350
- return this.Chain.Fees.getFeeRate();
351
- }
352
- /**
353
- * Gets fee rate required for submitting blockheaders to the specific fork
354
- */
355
- getForkFeeRate(signer, forkId) {
356
- return this.Chain.Fees.getFeeRate();
357
- }
358
- saveInitialHeader(signer, header, epochStart, pastBlocksTimestamps, feeRate) {
359
- throw new Error("Not supported, EVM contract is initialized with constructor!");
360
- }
361
- /**
362
- * Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
363
- * requiredConfirmations
364
- * If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
365
- * current chain tip & adds them to the txs array
366
- *
367
- * @param signer
368
- * @param btcRelay
369
- * @param btcTxs
370
- * @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
371
- * txns are added here
372
- * @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
373
- * to the required blockheight
374
- * @param feeRate Fee rate to use for synchronization transactions
375
- * @private
376
- */
377
- static async getCommitedHeadersAndSynchronize(signer, btcRelay, btcTxs, txs, synchronizer, feeRate) {
378
- const leavesTxs = [];
379
- const blockheaders = {};
380
- for (let btcTx of btcTxs) {
381
- const requiredBlockheight = btcTx.blockheight + btcTx.requiredConfirmations - 1;
382
- const result = await (0, Utils_1.tryWithRetries)(() => btcRelay.retrieveLogAndBlockheight({
383
- blockhash: btcTx.blockhash
384
- }, requiredBlockheight));
385
- if (result != null) {
386
- blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
387
- }
388
- else {
389
- leavesTxs.push(btcTx);
390
- }
391
- }
392
- if (leavesTxs.length === 0)
393
- return blockheaders;
394
- //Need to synchronize
395
- if (synchronizer == null)
396
- return null;
397
- //TODO: We don't have to synchronize to tip, only to our required blockheight
398
- const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
399
- logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
400
- "synchronizing ourselves in " + resp.txs.length + " txs");
401
- logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
402
- txs.push(...resp.txs);
403
- for (let key in resp.computedHeaderMap) {
404
- const header = resp.computedHeaderMap[key];
405
- blockheaders[header.getBlockHash().toString("hex")] = header;
406
- }
407
- //Check that blockhashes of all the rest txs are included
408
- for (let btcTx of leavesTxs) {
409
- if (blockheaders[btcTx.blockhash] == null)
410
- return null;
411
- }
412
- //Retrieve computed headers
413
- return blockheaders;
414
- }
415
- }
416
- exports.EVMBtcRelay = EVMBtcRelay;
417
- EVMBtcRelay.GasCosts = {
418
- GAS_PER_BLOCKHEADER: 30000,
419
- GAS_BASE_MAIN: 15000 + 21000,
420
- GAS_PER_BLOCKHEADER_FORK: 65000,
421
- GAS_PER_BLOCKHEADER_FORKED: 10000,
422
- GAS_BASE_FORK: 25000 + 21000
423
- };
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EVMBtcRelay = void 0;
4
+ const base_1 = require("@atomiqlabs/base");
5
+ const EVMBtcHeader_1 = require("./headers/EVMBtcHeader");
6
+ const Utils_1 = require("../../utils/Utils");
7
+ const EVMContractBase_1 = require("../contract/EVMContractBase");
8
+ const EVMBtcStoredHeader_1 = require("./headers/EVMBtcStoredHeader");
9
+ const EVMFees_1 = require("../chain/modules/EVMFees");
10
+ const BtcRelayAbi_1 = require("./BtcRelayAbi");
11
+ const ethers_1 = require("ethers");
12
+ function serializeBlockHeader(e) {
13
+ return new EVMBtcHeader_1.EVMBtcHeader({
14
+ version: e.getVersion(),
15
+ previousBlockhash: Buffer.from(e.getPrevBlockhash(), "hex").reverse(),
16
+ merkleRoot: Buffer.from(e.getMerkleRoot(), "hex").reverse(),
17
+ timestamp: e.getTimestamp(),
18
+ nbits: e.getNbits(),
19
+ nonce: e.getNonce(),
20
+ hash: Buffer.from(e.getHash(), "hex").reverse()
21
+ });
22
+ }
23
+ const logger = (0, Utils_1.getLogger)("EVMBtcRelay: ");
24
+ class EVMBtcRelay extends EVMContractBase_1.EVMContractBase {
25
+ async SaveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
26
+ const tx = await this.contract.submitMainBlockheaders.populateTransaction(Buffer.concat([
27
+ storedHeader.serialize(),
28
+ Buffer.concat(mainHeaders.map(header => header.serializeCompact()))
29
+ ]));
30
+ tx.from = signer;
31
+ EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * mainHeaders.length), feeRate);
32
+ return tx;
33
+ }
34
+ async SaveShortForkHeaders(signer, forkHeaders, storedHeader, feeRate) {
35
+ const tx = await this.contract.submitShortForkBlockheaders.populateTransaction(Buffer.concat([
36
+ storedHeader.serialize(),
37
+ Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
38
+ ]));
39
+ tx.from = signer;
40
+ EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * forkHeaders.length), feeRate);
41
+ return tx;
42
+ }
43
+ async SaveLongForkHeaders(signer, forkId, forkHeaders, storedHeader, feeRate, totalForkHeaders = 100) {
44
+ const tx = await this.contract.submitForkBlockheaders.populateTransaction(forkId, Buffer.concat([
45
+ storedHeader.serialize(),
46
+ Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
47
+ ]));
48
+ tx.from = signer;
49
+ EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_FORK + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORK * forkHeaders.length) + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORKED * totalForkHeaders), feeRate);
50
+ return tx;
51
+ }
52
+ constructor(chainInterface, bitcoinRpc, bitcoinNetwork, contractAddress, contractDeploymentHeight) {
53
+ super(chainInterface, contractAddress, BtcRelayAbi_1.BtcRelayAbi, contractDeploymentHeight);
54
+ this.maxHeadersPerTx = 100;
55
+ this.maxForkHeadersPerTx = 50;
56
+ this.maxShortForkHeadersPerTx = 100;
57
+ this.bitcoinRpc = bitcoinRpc;
58
+ }
59
+ /**
60
+ * Computes subsequent commited headers as they will appear on the blockchain when transactions
61
+ * are submitted & confirmed
62
+ *
63
+ * @param initialStoredHeader
64
+ * @param syncedHeaders
65
+ * @private
66
+ */
67
+ computeCommitedHeaders(initialStoredHeader, syncedHeaders) {
68
+ const computedCommitedHeaders = [initialStoredHeader];
69
+ for (let blockHeader of syncedHeaders) {
70
+ computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length - 1].computeNext(blockHeader));
71
+ }
72
+ return computedCommitedHeaders;
73
+ }
74
+ /**
75
+ * A common logic for submitting blockheaders in a transaction
76
+ *
77
+ * @param signer
78
+ * @param headers headers to sync to the btc relay
79
+ * @param storedHeader current latest stored block header for a given fork
80
+ * @param tipWork work of the current tip in a given fork
81
+ * @param forkId forkId to submit to, forkId=0 means main chain, forkId=-1 means short fork
82
+ * @param feeRate feeRate for the transaction
83
+ * @param totalForkHeaders Total number of headers in a fork
84
+ * @private
85
+ */
86
+ async _saveHeaders(signer, headers, storedHeader, tipWork, forkId, feeRate, totalForkHeaders) {
87
+ const blockHeaderObj = headers.map(serializeBlockHeader);
88
+ let tx;
89
+ switch (forkId) {
90
+ case -1:
91
+ tx = await this.SaveShortForkHeaders(signer, blockHeaderObj, storedHeader, feeRate);
92
+ break;
93
+ case 0:
94
+ tx = await this.SaveMainHeaders(signer, blockHeaderObj, storedHeader, feeRate);
95
+ break;
96
+ default:
97
+ tx = await this.SaveLongForkHeaders(signer, forkId, blockHeaderObj, storedHeader, feeRate, totalForkHeaders);
98
+ break;
99
+ }
100
+ const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
101
+ const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length - 1];
102
+ if (forkId !== 0 && base_1.StatePredictorUtils.gtBuffer(lastStoredHeader.getBlockHash(), tipWork)) {
103
+ //Fork's work is higher than main chain's work, this fork will become a main chain
104
+ forkId = 0;
105
+ }
106
+ return {
107
+ forkId: forkId,
108
+ lastStoredHeader,
109
+ tx,
110
+ computedCommitedHeaders
111
+ };
112
+ }
113
+ async findStoredBlockheaderInTraces(txTrace, commitHash) {
114
+ if (txTrace.to.toLowerCase() === (await this.contract.getAddress()).toLowerCase()) {
115
+ let dataBuffer;
116
+ if (txTrace.type === "CREATE") {
117
+ dataBuffer = Buffer.from(txTrace.input.substring(txTrace.input.length - 384, txTrace.input.length - 64), "hex");
118
+ }
119
+ else {
120
+ const result = this.parseCalldata(txTrace.input);
121
+ if (result != null) {
122
+ if (result.name === "submitMainBlockheaders" || result.name === "submitShortForkBlockheaders") {
123
+ const functionCall = result;
124
+ dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[0]).substring(2), "hex");
125
+ }
126
+ else if (result.name === "submitForkBlockheaders") {
127
+ const functionCall = result;
128
+ dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[1]).substring(2), "hex");
129
+ }
130
+ }
131
+ }
132
+ if (dataBuffer != null) {
133
+ let storedHeader = EVMBtcStoredHeader_1.EVMBtcStoredHeader.deserialize(dataBuffer.subarray(0, 160));
134
+ if (storedHeader.getCommitHash() === commitHash)
135
+ return storedHeader;
136
+ for (let i = 160; i < dataBuffer.length; i += 48) {
137
+ const blockHeader = EVMBtcHeader_1.EVMBtcHeader.deserialize(dataBuffer.subarray(i, i + 48));
138
+ storedHeader = storedHeader.computeNext(blockHeader);
139
+ if (storedHeader.getCommitHash() === commitHash)
140
+ return storedHeader;
141
+ }
142
+ }
143
+ }
144
+ if (txTrace.calls != null) {
145
+ for (let call of txTrace.calls) {
146
+ const result = await this.findStoredBlockheaderInTraces(call, commitHash);
147
+ if (result != null)
148
+ return result;
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ getBlock(commitHash, blockHash) {
154
+ return this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], [
155
+ commitHash,
156
+ blockHash == null ? null : "0x" + Buffer.from([...blockHash]).reverse().toString("hex")
157
+ ], async (event) => {
158
+ const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
159
+ const storedBlockheader = await this.findStoredBlockheaderInTraces(txTrace, event.args.commitHash);
160
+ if (storedBlockheader != null)
161
+ return [storedBlockheader, event.args.commitHash];
162
+ });
163
+ }
164
+ async getBlockHeight() {
165
+ return Number(await this.contract.getBlockheight());
166
+ }
167
+ /**
168
+ * Returns data about current main chain tip stored in the btc relay
169
+ */
170
+ async getTipData() {
171
+ const commitHash = await this.contract.getTipCommitHash();
172
+ if (commitHash == null || BigInt(commitHash) === BigInt(0))
173
+ return null;
174
+ const result = await this.getBlock(commitHash);
175
+ if (result == null)
176
+ return null;
177
+ const storedBlockHeader = result[0];
178
+ return {
179
+ blockheight: storedBlockHeader.getBlockheight(),
180
+ commitHash: commitHash,
181
+ blockhash: storedBlockHeader.getBlockHash().toString("hex"),
182
+ chainWork: storedBlockHeader.getChainWork()
183
+ };
184
+ }
185
+ /**
186
+ * Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
187
+ * btc relay contract is not synced up to the desired blockheight
188
+ *
189
+ * @param blockData
190
+ * @param requiredBlockheight
191
+ */
192
+ async retrieveLogAndBlockheight(blockData, requiredBlockheight) {
193
+ //TODO: we can fetch the blockheight and events in parallel
194
+ const blockHeight = await this.getBlockHeight();
195
+ if (requiredBlockheight != null && blockHeight < requiredBlockheight) {
196
+ return null;
197
+ }
198
+ const result = await this.getBlock(null, Buffer.from(blockData.blockhash, "hex"));
199
+ if (result == null)
200
+ return null;
201
+ const [storedBlockHeader, commitHash] = result;
202
+ //Check if block is part of the main chain
203
+ const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
204
+ if (chainCommitment !== commitHash)
205
+ return null;
206
+ logger.debug("retrieveLogAndBlockheight(): block found," +
207
+ " commit hash: " + commitHash + " blockhash: " + blockData.blockhash + " current btc relay height: " + blockHeight);
208
+ return { header: storedBlockHeader, height: blockHeight };
209
+ }
210
+ /**
211
+ * Retrieves blockheader data by blockheader's commit hash,
212
+ *
213
+ * @param commitmentHashStr
214
+ * @param blockData
215
+ */
216
+ async retrieveLogByCommitHash(commitmentHashStr, blockData) {
217
+ const result = await this.getBlock(commitmentHashStr, Buffer.from(blockData.blockhash, "hex"));
218
+ if (result == null)
219
+ return null;
220
+ const [storedBlockHeader, commitHash] = result;
221
+ //Check if block is part of the main chain
222
+ const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
223
+ if (chainCommitment !== commitHash)
224
+ return null;
225
+ logger.debug("retrieveLogByCommitHash(): block found," +
226
+ " commit hash: " + commitmentHashStr + " blockhash: " + blockData.blockhash + " height: " + storedBlockHeader.blockHeight);
227
+ return storedBlockHeader;
228
+ }
229
+ /**
230
+ * Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
231
+ */
232
+ async retrieveLatestKnownBlockLog() {
233
+ const data = await this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], null, async (event) => {
234
+ const blockHashHex = Buffer.from(event.args.blockHash.substring(2), "hex").reverse().toString("hex");
235
+ const commitHash = event.args.commitHash;
236
+ const isInBtcMainChain = await this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false);
237
+ if (!isInBtcMainChain)
238
+ return null;
239
+ const blockHeader = await this.bitcoinRpc.getBlockHeader(blockHashHex);
240
+ if (commitHash !== await this.contract.getCommitHash(blockHeader.getHeight()))
241
+ return null;
242
+ const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
243
+ const storedHeader = await this.findStoredBlockheaderInTraces(txTrace, commitHash);
244
+ if (storedHeader == null)
245
+ return null;
246
+ return {
247
+ resultStoredHeader: storedHeader,
248
+ resultBitcoinHeader: blockHeader,
249
+ commitHash: commitHash
250
+ };
251
+ });
252
+ if (data != null)
253
+ logger.debug("retrieveLatestKnownBlockLog(): block found," +
254
+ " commit hash: " + data.commitHash + " blockhash: " + data.resultBitcoinHeader.getHash() +
255
+ " height: " + data.resultStoredHeader.getBlockheight());
256
+ return data;
257
+ }
258
+ /**
259
+ * Saves blockheaders as a bitcoin main chain to the btc relay
260
+ *
261
+ * @param signer
262
+ * @param mainHeaders
263
+ * @param storedHeader
264
+ * @param feeRate
265
+ */
266
+ async saveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
267
+ feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
268
+ logger.debug("saveMainHeaders(): submitting main blockheaders, count: " + mainHeaders.length);
269
+ return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate, 0);
270
+ }
271
+ /**
272
+ * Creates a new long fork and submits the headers to it
273
+ *
274
+ * @param signer
275
+ * @param forkHeaders
276
+ * @param storedHeader
277
+ * @param tipWork
278
+ * @param feeRate
279
+ */
280
+ async saveNewForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
281
+ let forkId = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
282
+ feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
283
+ logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
284
+ " count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
285
+ return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, forkHeaders.length);
286
+ }
287
+ /**
288
+ * Continues submitting blockheaders to a given fork
289
+ *
290
+ * @param signer
291
+ * @param forkHeaders
292
+ * @param storedHeader
293
+ * @param forkId
294
+ * @param tipWork
295
+ * @param feeRate
296
+ */
297
+ async saveForkHeaders(signer, forkHeaders, storedHeader, forkId, tipWork, feeRate) {
298
+ feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
299
+ logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
300
+ " count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
301
+ return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, 100);
302
+ }
303
+ /**
304
+ * Submits short fork with given blockheaders
305
+ *
306
+ * @param signer
307
+ * @param forkHeaders
308
+ * @param storedHeader
309
+ * @param tipWork
310
+ * @param feeRate
311
+ */
312
+ async saveShortForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
313
+ feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
314
+ logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
315
+ " count: " + forkHeaders.length);
316
+ return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate, 0);
317
+ }
318
+ /**
319
+ * Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
320
+ *
321
+ * @param requiredBlockheight
322
+ * @param feeRate
323
+ */
324
+ async estimateSynchronizeFee(requiredBlockheight, feeRate) {
325
+ feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
326
+ const tipData = await this.getTipData();
327
+ const currBlockheight = tipData.blockheight;
328
+ const blockheightDelta = requiredBlockheight - currBlockheight;
329
+ if (blockheightDelta <= 0)
330
+ return 0n;
331
+ const synchronizationFee = (BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate))
332
+ + EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_BASE_MAIN * Math.ceil(blockheightDelta / this.maxHeadersPerTx), feeRate);
333
+ logger.debug("estimateSynchronizeFee(): required blockheight: " + requiredBlockheight +
334
+ " blockheight delta: " + blockheightDelta + " fee: " + synchronizationFee.toString(10));
335
+ return synchronizationFee;
336
+ }
337
+ /**
338
+ * Returns fee required (in native token) to synchronize a single block to btc relay
339
+ *
340
+ * @param feeRate
341
+ */
342
+ async getFeePerBlock(feeRate) {
343
+ feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
344
+ return EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER, feeRate);
345
+ }
346
+ /**
347
+ * Gets fee rate required for submitting blockheaders to the main chain
348
+ */
349
+ getMainFeeRate(signer) {
350
+ return this.Chain.Fees.getFeeRate();
351
+ }
352
+ /**
353
+ * Gets fee rate required for submitting blockheaders to the specific fork
354
+ */
355
+ getForkFeeRate(signer, forkId) {
356
+ return this.Chain.Fees.getFeeRate();
357
+ }
358
+ saveInitialHeader(signer, header, epochStart, pastBlocksTimestamps, feeRate) {
359
+ throw new Error("Not supported, EVM contract is initialized with constructor!");
360
+ }
361
+ /**
362
+ * Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
363
+ * requiredConfirmations
364
+ * If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
365
+ * current chain tip & adds them to the txs array
366
+ *
367
+ * @param signer
368
+ * @param btcRelay
369
+ * @param btcTxs
370
+ * @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
371
+ * txns are added here
372
+ * @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
373
+ * to the required blockheight
374
+ * @param feeRate Fee rate to use for synchronization transactions
375
+ * @private
376
+ */
377
+ static async getCommitedHeadersAndSynchronize(signer, btcRelay, btcTxs, txs, synchronizer, feeRate) {
378
+ const leavesTxs = [];
379
+ const blockheaders = {};
380
+ for (let btcTx of btcTxs) {
381
+ const requiredBlockheight = btcTx.blockheight + btcTx.requiredConfirmations - 1;
382
+ const result = await (0, Utils_1.tryWithRetries)(() => btcRelay.retrieveLogAndBlockheight({
383
+ blockhash: btcTx.blockhash
384
+ }, requiredBlockheight));
385
+ if (result != null) {
386
+ blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
387
+ }
388
+ else {
389
+ leavesTxs.push(btcTx);
390
+ }
391
+ }
392
+ if (leavesTxs.length === 0)
393
+ return blockheaders;
394
+ //Need to synchronize
395
+ if (synchronizer == null)
396
+ return null;
397
+ //TODO: We don't have to synchronize to tip, only to our required blockheight
398
+ const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
399
+ logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
400
+ "synchronizing ourselves in " + resp.txs.length + " txs");
401
+ logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
402
+ txs.push(...resp.txs);
403
+ for (let key in resp.computedHeaderMap) {
404
+ const header = resp.computedHeaderMap[key];
405
+ blockheaders[header.getBlockHash().toString("hex")] = header;
406
+ }
407
+ //Check that blockhashes of all the rest txs are included
408
+ for (let btcTx of leavesTxs) {
409
+ if (blockheaders[btcTx.blockhash] == null)
410
+ return null;
411
+ }
412
+ //Retrieve computed headers
413
+ return blockheaders;
414
+ }
415
+ }
416
+ exports.EVMBtcRelay = EVMBtcRelay;
417
+ EVMBtcRelay.GasCosts = {
418
+ GAS_PER_BLOCKHEADER: 30000,
419
+ GAS_BASE_MAIN: 15000 + 21000,
420
+ GAS_PER_BLOCKHEADER_FORK: 65000,
421
+ GAS_PER_BLOCKHEADER_FORKED: 10000,
422
+ GAS_BASE_FORK: 25000 + 21000
423
+ };