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