@across-protocol/sdk 4.3.139 → 4.3.140-alpha.1

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 (169) hide show
  1. package/dist/cjs/src/arch/evm/SpokeUtils.d.ts +3 -3
  2. package/dist/cjs/src/arch/evm/SpokeUtils.js +1 -1
  3. package/dist/cjs/src/arch/evm/SpokeUtils.js.map +1 -1
  4. package/dist/cjs/src/arch/evm/UpgradeUtils.d.ts +2 -0
  5. package/dist/cjs/src/arch/evm/UpgradeUtils.js +11 -0
  6. package/dist/cjs/src/arch/evm/UpgradeUtils.js.map +1 -0
  7. package/dist/cjs/src/arch/evm/index.d.ts +1 -0
  8. package/dist/cjs/src/arch/evm/index.js +1 -0
  9. package/dist/cjs/src/arch/evm/index.js.map +1 -1
  10. package/dist/cjs/src/arch/index.d.ts +1 -0
  11. package/dist/cjs/src/arch/index.js +2 -1
  12. package/dist/cjs/src/arch/index.js.map +1 -1
  13. package/dist/cjs/src/arch/tvm/SpokeUtils.d.ts +12 -0
  14. package/dist/cjs/src/arch/tvm/SpokeUtils.js +101 -0
  15. package/dist/cjs/src/arch/tvm/SpokeUtils.js.map +1 -0
  16. package/dist/cjs/src/arch/tvm/TransactionUtils.d.ts +16 -0
  17. package/dist/cjs/src/arch/tvm/TransactionUtils.js +51 -0
  18. package/dist/cjs/src/arch/tvm/TransactionUtils.js.map +1 -0
  19. package/dist/cjs/src/arch/tvm/index.d.ts +2 -0
  20. package/dist/cjs/src/arch/tvm/index.js +6 -0
  21. package/dist/cjs/src/arch/tvm/index.js.map +1 -0
  22. package/dist/cjs/src/clients/BundleDataClient/BundleDataClient.js +3 -1
  23. package/dist/cjs/src/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  24. package/dist/cjs/src/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +10 -2
  25. package/dist/cjs/src/clients/SpokePoolClient/EVMSpokePoolClient.js +40 -14
  26. package/dist/cjs/src/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
  27. package/dist/cjs/src/gasPriceOracle/oracle.js +1 -0
  28. package/dist/cjs/src/gasPriceOracle/oracle.js.map +1 -1
  29. package/dist/cjs/src/relayFeeCalculator/chain-queries/baseQuery.d.ts +3 -3
  30. package/dist/cjs/src/relayFeeCalculator/chain-queries/baseQuery.js +6 -6
  31. package/dist/cjs/src/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
  32. package/dist/cjs/src/utils/AddressUtils.d.ts +11 -0
  33. package/dist/cjs/src/utils/AddressUtils.js +47 -2
  34. package/dist/cjs/src/utils/AddressUtils.js.map +1 -1
  35. package/dist/cjs/src/utils/BlockExplorerUtils.js +13 -2
  36. package/dist/cjs/src/utils/BlockExplorerUtils.js.map +1 -1
  37. package/dist/cjs/src/utils/Multicall.js +1 -0
  38. package/dist/cjs/src/utils/Multicall.js.map +1 -1
  39. package/dist/cjs/src/utils/NetworkUtils.d.ts +1 -0
  40. package/dist/cjs/src/utils/NetworkUtils.js +8 -3
  41. package/dist/cjs/src/utils/NetworkUtils.js.map +1 -1
  42. package/dist/cjs/src/utils/TypeGuards.d.ts +3 -1
  43. package/dist/cjs/src/utils/TypeGuards.js +4 -0
  44. package/dist/cjs/src/utils/TypeGuards.js.map +1 -1
  45. package/dist/cjs/src/utils/abi/typechain/SP1AutoVerifier.d.ts +42 -0
  46. package/dist/cjs/src/utils/abi/typechain/SP1AutoVerifier.js +3 -0
  47. package/dist/cjs/src/utils/abi/typechain/SP1AutoVerifier.js.map +1 -0
  48. package/dist/cjs/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.d.ts +26 -0
  49. package/dist/cjs/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.js +40 -0
  50. package/dist/cjs/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.js.map +1 -0
  51. package/dist/cjs/src/utils/abi/typechain/factories/index.d.ts +1 -0
  52. package/dist/cjs/src/utils/abi/typechain/factories/index.js +4 -2
  53. package/dist/cjs/src/utils/abi/typechain/factories/index.js.map +1 -1
  54. package/dist/cjs/src/utils/abi/typechain/index.d.ts +2 -0
  55. package/dist/cjs/src/utils/abi/typechain/index.js +4 -2
  56. package/dist/cjs/src/utils/abi/typechain/index.js.map +1 -1
  57. package/dist/esm/src/arch/evm/SpokeUtils.d.ts +3 -3
  58. package/dist/esm/src/arch/evm/SpokeUtils.js +1 -1
  59. package/dist/esm/src/arch/evm/SpokeUtils.js.map +1 -1
  60. package/dist/esm/src/arch/evm/UpgradeUtils.d.ts +9 -0
  61. package/dist/esm/src/arch/evm/UpgradeUtils.js +15 -0
  62. package/dist/esm/src/arch/evm/UpgradeUtils.js.map +1 -0
  63. package/dist/esm/src/arch/evm/index.d.ts +1 -0
  64. package/dist/esm/src/arch/evm/index.js +1 -0
  65. package/dist/esm/src/arch/evm/index.js.map +1 -1
  66. package/dist/esm/src/arch/index.d.ts +1 -0
  67. package/dist/esm/src/arch/index.js +2 -0
  68. package/dist/esm/src/arch/index.js.map +1 -1
  69. package/dist/esm/src/arch/tvm/SpokeUtils.d.ts +48 -0
  70. package/dist/esm/src/arch/tvm/SpokeUtils.js +138 -0
  71. package/dist/esm/src/arch/tvm/SpokeUtils.js.map +1 -0
  72. package/dist/esm/src/arch/tvm/TransactionUtils.d.ts +41 -0
  73. package/dist/esm/src/arch/tvm/TransactionUtils.js +78 -0
  74. package/dist/esm/src/arch/tvm/TransactionUtils.js.map +1 -0
  75. package/dist/esm/src/arch/tvm/index.d.ts +2 -0
  76. package/dist/esm/src/arch/tvm/index.js +3 -0
  77. package/dist/esm/src/arch/tvm/index.js.map +1 -0
  78. package/dist/esm/src/clients/BundleDataClient/BundleDataClient.js +4 -2
  79. package/dist/esm/src/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  80. package/dist/esm/src/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +21 -2
  81. package/dist/esm/src/clients/SpokePoolClient/EVMSpokePoolClient.js +52 -15
  82. package/dist/esm/src/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
  83. package/dist/esm/src/gasPriceOracle/oracle.js +1 -0
  84. package/dist/esm/src/gasPriceOracle/oracle.js.map +1 -1
  85. package/dist/esm/src/relayFeeCalculator/chain-queries/baseQuery.d.ts +3 -3
  86. package/dist/esm/src/relayFeeCalculator/chain-queries/baseQuery.js +6 -6
  87. package/dist/esm/src/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
  88. package/dist/esm/src/utils/AddressUtils.d.ts +11 -0
  89. package/dist/esm/src/utils/AddressUtils.js +53 -2
  90. package/dist/esm/src/utils/AddressUtils.js.map +1 -1
  91. package/dist/esm/src/utils/BlockExplorerUtils.js +15 -3
  92. package/dist/esm/src/utils/BlockExplorerUtils.js.map +1 -1
  93. package/dist/esm/src/utils/Multicall.js +1 -0
  94. package/dist/esm/src/utils/Multicall.js.map +1 -1
  95. package/dist/esm/src/utils/NetworkUtils.d.ts +6 -0
  96. package/dist/esm/src/utils/NetworkUtils.js +12 -3
  97. package/dist/esm/src/utils/NetworkUtils.js.map +1 -1
  98. package/dist/esm/src/utils/TypeGuards.d.ts +3 -1
  99. package/dist/esm/src/utils/TypeGuards.js +3 -0
  100. package/dist/esm/src/utils/TypeGuards.js.map +1 -1
  101. package/dist/esm/src/utils/abi/typechain/SP1AutoVerifier.d.ts +42 -0
  102. package/dist/esm/src/utils/abi/typechain/SP1AutoVerifier.js +2 -0
  103. package/dist/esm/src/utils/abi/typechain/SP1AutoVerifier.js.map +1 -0
  104. package/dist/esm/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.d.ts +26 -0
  105. package/dist/esm/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.js +39 -0
  106. package/dist/esm/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.js.map +1 -0
  107. package/dist/esm/src/utils/abi/typechain/factories/index.d.ts +1 -0
  108. package/dist/esm/src/utils/abi/typechain/factories/index.js +1 -0
  109. package/dist/esm/src/utils/abi/typechain/factories/index.js.map +1 -1
  110. package/dist/esm/src/utils/abi/typechain/index.d.ts +2 -0
  111. package/dist/esm/src/utils/abi/typechain/index.js +1 -0
  112. package/dist/esm/src/utils/abi/typechain/index.js.map +1 -1
  113. package/dist/types/src/arch/evm/SpokeUtils.d.ts +3 -3
  114. package/dist/types/src/arch/evm/SpokeUtils.d.ts.map +1 -1
  115. package/dist/types/src/arch/evm/UpgradeUtils.d.ts +10 -0
  116. package/dist/types/src/arch/evm/UpgradeUtils.d.ts.map +1 -0
  117. package/dist/types/src/arch/evm/index.d.ts +1 -0
  118. package/dist/types/src/arch/evm/index.d.ts.map +1 -1
  119. package/dist/types/src/arch/index.d.ts +1 -0
  120. package/dist/types/src/arch/index.d.ts.map +1 -1
  121. package/dist/types/src/arch/tvm/SpokeUtils.d.ts +49 -0
  122. package/dist/types/src/arch/tvm/SpokeUtils.d.ts.map +1 -0
  123. package/dist/types/src/arch/tvm/TransactionUtils.d.ts +42 -0
  124. package/dist/types/src/arch/tvm/TransactionUtils.d.ts.map +1 -0
  125. package/dist/types/src/arch/tvm/index.d.ts +3 -0
  126. package/dist/types/src/arch/tvm/index.d.ts.map +1 -0
  127. package/dist/types/src/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  128. package/dist/types/src/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +21 -2
  129. package/dist/types/src/clients/SpokePoolClient/EVMSpokePoolClient.d.ts.map +1 -1
  130. package/dist/types/src/relayFeeCalculator/chain-queries/baseQuery.d.ts +3 -3
  131. package/dist/types/src/relayFeeCalculator/chain-queries/baseQuery.d.ts.map +1 -1
  132. package/dist/types/src/utils/AddressUtils.d.ts +11 -0
  133. package/dist/types/src/utils/AddressUtils.d.ts.map +1 -1
  134. package/dist/types/src/utils/BlockExplorerUtils.d.ts.map +1 -1
  135. package/dist/types/src/utils/Multicall.d.ts.map +1 -1
  136. package/dist/types/src/utils/NetworkUtils.d.ts +6 -0
  137. package/dist/types/src/utils/NetworkUtils.d.ts.map +1 -1
  138. package/dist/types/src/utils/TypeGuards.d.ts +3 -1
  139. package/dist/types/src/utils/TypeGuards.d.ts.map +1 -1
  140. package/dist/types/src/utils/abi/typechain/SP1AutoVerifier.d.ts +43 -0
  141. package/dist/types/src/utils/abi/typechain/SP1AutoVerifier.d.ts.map +1 -0
  142. package/dist/types/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.d.ts +27 -0
  143. package/dist/types/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.d.ts.map +1 -0
  144. package/dist/types/src/utils/abi/typechain/factories/index.d.ts +1 -0
  145. package/dist/types/src/utils/abi/typechain/factories/index.d.ts.map +1 -1
  146. package/dist/types/src/utils/abi/typechain/index.d.ts +2 -0
  147. package/dist/types/src/utils/abi/typechain/index.d.ts.map +1 -1
  148. package/package.json +4 -3
  149. package/src/arch/evm/SpokeUtils.ts +3 -4
  150. package/src/arch/evm/UpgradeUtils.ts +18 -0
  151. package/src/arch/evm/index.ts +1 -0
  152. package/src/arch/index.ts +1 -0
  153. package/src/arch/tvm/SpokeUtils.ts +225 -0
  154. package/src/arch/tvm/TransactionUtils.ts +126 -0
  155. package/src/arch/tvm/index.ts +2 -0
  156. package/src/clients/BundleDataClient/BundleDataClient.ts +4 -1
  157. package/src/clients/SpokePoolClient/EVMSpokePoolClient.ts +69 -21
  158. package/src/gasPriceOracle/oracle.ts +1 -0
  159. package/src/relayFeeCalculator/chain-queries/baseQuery.ts +24 -8
  160. package/src/utils/AddressUtils.ts +62 -2
  161. package/src/utils/BlockExplorerUtils.ts +14 -3
  162. package/src/utils/Multicall.ts +1 -0
  163. package/src/utils/NetworkUtils.ts +13 -3
  164. package/src/utils/TypeGuards.ts +6 -1
  165. package/src/utils/abi/contracts/SP1AutoVerifier.json +25 -0
  166. package/src/utils/abi/typechain/SP1AutoVerifier.ts +112 -0
  167. package/src/utils/abi/typechain/factories/SP1AutoVerifier__factory.ts +49 -0
  168. package/src/utils/abi/typechain/factories/index.ts +1 -0
  169. package/src/utils/abi/typechain/index.ts +2 -0
@@ -0,0 +1,225 @@
1
+ import assert from "assert";
2
+ import { Contract, providers } from "ethers";
3
+ import { CHAIN_IDs } from "../../constants";
4
+ import { FillStatus, FillWithBlock, RelayData } from "../../interfaces";
5
+ import { get1967Upgrades } from "../evm/UpgradeUtils";
6
+ import { relayFillStatus as evmRelayFillStatus } from "../evm/SpokeUtils";
7
+ import {
8
+ BigNumber,
9
+ getRelayDataHash,
10
+ isDefined,
11
+ isUnsafeDepositId,
12
+ paginatedEventQuery,
13
+ spreadEventWithBlockNumber,
14
+ unpackFillEvent,
15
+ } from "../../utils";
16
+
17
+ type BlockTag = providers.BlockTag;
18
+
19
+ // Re-export functions that work unchanged on TRON's JSON-RPC.
20
+ export { populateV3Relay, getTimestampForBlock, fillStatusArray } from "../evm/SpokeUtils";
21
+
22
+ // Local implementations for functions requiring historical eth_call,
23
+ // which TRON does not support (only "latest" blockTag is accepted).
24
+
25
+ /**
26
+ * @notice Retrieve the on-chain time at a specific block number.
27
+ * TRON does not support historical eth_call so block timestamps are used
28
+ * instead of SpokePool.getCurrentTime(). In production these are equivalent.
29
+ */
30
+ export async function getTimeAt(spokePool: Contract, blockNumber: number): Promise<number> {
31
+ const block = await spokePool.provider.getBlock(blockNumber);
32
+ return block.timestamp;
33
+ }
34
+
35
+ // Fallback fill deadline buffer (6 hours) used when a contract upgrade is
36
+ // detected in the query range. An upgrade implies fillDeadlineBuffer may
37
+ // have changed and we cannot read historical state on TRON, so we use a
38
+ // conservative upper bound.
39
+ const FALLBACK_FILL_DEADLINE_BUFFER = 21600; // 6 hours in seconds
40
+
41
+ /**
42
+ * @notice Return the maximum fill deadline buffer across a block range.
43
+ * TRON does not support historical eth_call, so we read the current value.
44
+ * If a contract upgrade (EIP-1967 Upgraded event) occurred within the range,
45
+ * the value may have changed at the upgrade boundary. In that case, return
46
+ * the greater of the current value and a conservative 6-hour fallback.
47
+ */
48
+ export async function getMaxFillDeadlineInRange(
49
+ spokePool: Contract,
50
+ startBlock: number,
51
+ endBlock: number
52
+ ): Promise<number> {
53
+ const [fillDeadlineBuffer, upgrades] = await Promise.all([
54
+ spokePool.fillDeadlineBuffer(),
55
+ get1967Upgrades(spokePool, startBlock, endBlock),
56
+ ]);
57
+
58
+ const currentBuffer = Number(fillDeadlineBuffer);
59
+
60
+ if (upgrades.length > 0) {
61
+ return Math.max(currentBuffer, FALLBACK_FILL_DEADLINE_BUFFER);
62
+ }
63
+
64
+ return currentBuffer;
65
+ }
66
+
67
+ /**
68
+ * @notice Not supported on TVM — callers should use findDepositBlock instead.
69
+ */
70
+ export function getDepositIdAtBlock(_contract: Contract, _blockTag: number): Promise<BigNumber> {
71
+ throw new Error("getDepositIdAtBlock: not supported on TVM");
72
+ }
73
+
74
+ /**
75
+ * @notice Find the block at which a deposit was created.
76
+ * TRON does not support historical eth_call, so event queries replace the
77
+ * EVM binary-search over numberOfDeposits().
78
+ */
79
+ export async function findDepositBlock(
80
+ spokePool: Contract,
81
+ depositId: BigNumber,
82
+ lowBlock: number,
83
+ highBlock?: number
84
+ ): Promise<number | undefined> {
85
+ if (isUnsafeDepositId(depositId)) {
86
+ throw new Error(`Cannot search for depositId ${depositId}`);
87
+ }
88
+
89
+ highBlock ??= await spokePool.provider.getBlockNumber();
90
+ assert(highBlock > lowBlock, `Block numbers out of range (${lowBlock} >= ${highBlock})`);
91
+
92
+ const events = await paginatedEventQuery(
93
+ spokePool,
94
+ spokePool.filters.FundsDeposited(null, null, null, null, null, depositId),
95
+ { from: lowBlock, to: highBlock }
96
+ );
97
+
98
+ if (events.length === 0) {
99
+ return undefined;
100
+ }
101
+
102
+ return events[0].blockNumber;
103
+ }
104
+
105
+ /**
106
+ * @notice Determine the fill status of a relay at a given block.
107
+ * For "latest" queries, delegates to the EVM implementation (TRON's
108
+ * eth_call works normally for "latest"). For historical queries the
109
+ * status is reconstructed from on-chain events because TRON does not
110
+ * support eth_call with historical blockTags.
111
+ */
112
+ export async function relayFillStatus(
113
+ spokePool: Contract,
114
+ relayData: RelayData,
115
+ blockTag: BlockTag = "latest",
116
+ destinationChainId?: number
117
+ ): Promise<FillStatus> {
118
+ if (blockTag === "latest") {
119
+ return evmRelayFillStatus(spokePool, relayData, blockTag, destinationChainId);
120
+ }
121
+
122
+ destinationChainId ??= await spokePool.chainId();
123
+ assert(isDefined(destinationChainId));
124
+
125
+ const hash = getRelayDataHash(relayData, destinationChainId);
126
+
127
+ // Historical blockTag: check the current state first as an optimisation.
128
+ // Fill status can only increase (Unfilled -> RequestedSlowFill -> Filled),
129
+ // so if the deposit is still Unfilled now it was Unfilled at every prior block.
130
+ const latestStatus = Number(await spokePool.fillStatuses(hash));
131
+ if (latestStatus === FillStatus.Unfilled) {
132
+ return FillStatus.Unfilled;
133
+ }
134
+
135
+ // Reconstruct from events up to the requested block.
136
+ const fromBlock = 0;
137
+ const toBlock = Number(blockTag);
138
+
139
+ const fillEvents = await paginatedEventQuery(
140
+ spokePool,
141
+ spokePool.filters.FilledRelay(null, null, null, null, null, relayData.originChainId, relayData.depositId),
142
+ { from: fromBlock, to: toBlock }
143
+ );
144
+
145
+ if (fillEvents.length > 0) {
146
+ return FillStatus.Filled;
147
+ }
148
+
149
+ // No fill before blockTag — check for slow fill requests.
150
+ if (latestStatus >= FillStatus.RequestedSlowFill) {
151
+ const slowFillEvents = await paginatedEventQuery(
152
+ spokePool,
153
+ spokePool.filters.RequestedSlowFill(null, null, null, null, relayData.originChainId, relayData.depositId),
154
+ { from: fromBlock, to: toBlock }
155
+ );
156
+
157
+ if (slowFillEvents.length > 0) {
158
+ return FillStatus.RequestedSlowFill;
159
+ }
160
+ }
161
+
162
+ return FillStatus.Unfilled;
163
+ }
164
+
165
+ /**
166
+ * @notice Find the block at which a fill was completed.
167
+ * TRON does not support historical eth_call, so event queries replace the
168
+ * EVM binary-search over fillStatuses().
169
+ */
170
+ export async function findFillBlock(
171
+ spokePool: Contract,
172
+ relayData: RelayData,
173
+ lowBlockNumber: number,
174
+ highBlockNumber?: number
175
+ ): Promise<number | undefined> {
176
+ const { provider } = spokePool;
177
+ highBlockNumber ??= await provider.getBlockNumber();
178
+ assert(highBlockNumber > lowBlockNumber, `Block numbers out of range (${lowBlockNumber} >= ${highBlockNumber})`);
179
+
180
+ const events = await paginatedEventQuery(
181
+ spokePool,
182
+ spokePool.filters.FilledRelay(null, null, null, null, null, relayData.originChainId, relayData.depositId),
183
+ { from: lowBlockNumber, to: highBlockNumber }
184
+ );
185
+
186
+ if (events.length === 0) {
187
+ return undefined;
188
+ }
189
+
190
+ return events[0].blockNumber;
191
+ }
192
+
193
+ /**
194
+ * @notice Find the fill event for a deposit.
195
+ * Queries fill events directly rather than binary-searching then re-querying.
196
+ */
197
+ export async function findFillEvent(
198
+ spokePool: Contract,
199
+ relayData: RelayData,
200
+ lowBlockNumber: number,
201
+ highBlockNumber?: number
202
+ ): Promise<FillWithBlock | undefined> {
203
+ const { provider } = spokePool;
204
+ highBlockNumber ??= await provider.getBlockNumber();
205
+
206
+ const events = await paginatedEventQuery(
207
+ spokePool,
208
+ spokePool.filters.FilledRelay(null, null, null, null, null, relayData.originChainId, relayData.depositId),
209
+ { from: lowBlockNumber, to: highBlockNumber }
210
+ );
211
+
212
+ if (events.length === 0) {
213
+ return undefined;
214
+ }
215
+
216
+ const event = events[0];
217
+ // In production the chainId returned from the provider matches 1:1 with the actual chainId. Querying the provider
218
+ // object saves an RPC query because the chainId is cached by StaticJsonRpcProvider instances. In hre, the SpokePool
219
+ // may be configured with a different chainId than what is returned by the provider.
220
+ const destinationChainId = Object.values(CHAIN_IDs).includes(relayData.originChainId)
221
+ ? (await spokePool.provider.getNetwork()).chainId
222
+ : Number(await spokePool.chainId());
223
+
224
+ return unpackFillEvent(spreadEventWithBlockNumber(event), destinationChainId);
225
+ }
@@ -0,0 +1,126 @@
1
+ import { TronWeb } from "tronweb";
2
+ import { PopulatedTransaction } from "ethers";
3
+ import { TvmAddress } from "../../utils";
4
+
5
+ export interface TronTransactionResult {
6
+ txid: string;
7
+ result: boolean;
8
+ }
9
+
10
+ /** Result of an off-chain contract call via `triggerConstantContract` (no broadcast). */
11
+ export interface TronSimulationResult {
12
+ success: boolean;
13
+ message?: string;
14
+ constantResult?: unknown;
15
+ energyUsed?: number;
16
+ energyRequired?: number;
17
+ energyPenalty?: number;
18
+ }
19
+
20
+ /**
21
+ * Submit a populated EVM transaction to TRON via TronWeb.
22
+ *
23
+ * The EVM `populateV3Relay()` already produces correct ABI-encoded calldata.
24
+ * This function extracts `to` and `data` from the PopulatedTransaction,
25
+ * converts the target address to TRON Base58 format, and uses TronWeb's
26
+ * `triggerSmartContract` → `sign` → `sendRawTransaction` pipeline.
27
+ *
28
+ * @param tronWeb An authenticated TronWeb instance (with private key set).
29
+ * @param populatedTx The populated transaction containing `to` and `data`.
30
+ * @param feeLimit The maximum TRX to burn for energy consumption, in SUN (1 TRX = 1,000,000 SUN).
31
+ * @returns The transaction ID and result status.
32
+ */
33
+ export async function submitTransaction(
34
+ tronWeb: TronWeb,
35
+ populatedTx: PopulatedTransaction,
36
+ feeLimit: number,
37
+ callValue: number = 0
38
+ ): Promise<TronTransactionResult> {
39
+ const { to, data } = populatedTx;
40
+ if (!to || !data) {
41
+ throw new Error("submitTransaction: populatedTx must have both 'to' and 'data' fields");
42
+ }
43
+
44
+ const tronAddress = TvmAddress.from(to).toNative();
45
+ const ownerAddress = tronWeb.defaultAddress?.base58;
46
+ if (!ownerAddress) {
47
+ throw new Error("submitTransaction: TronWeb instance must have a default address configured");
48
+ }
49
+
50
+ // Use triggerSmartContract with the `input` option to pass pre-encoded calldata.
51
+ // The function selector is empty — the full calldata (selector + params) is in `input`.
52
+ const input = data.startsWith("0x") ? data.slice(2) : data;
53
+ const txWrapper = await tronWeb.transactionBuilder.triggerSmartContract(
54
+ tronAddress,
55
+ // Use empty function selector — the `input` option provides the full calldata.
56
+ "",
57
+ { feeLimit, input, callValue },
58
+ [],
59
+ ownerAddress
60
+ );
61
+
62
+ if (!txWrapper?.result?.result) {
63
+ const message = txWrapper?.result?.message ?? "Unknown error";
64
+ throw new Error(`submitTransaction: triggerSmartContract failed: ${message}`);
65
+ }
66
+
67
+ const signedTx = await tronWeb.trx.sign(txWrapper.transaction);
68
+ const broadcast = await tronWeb.trx.sendRawTransaction(signedTx);
69
+
70
+ return {
71
+ txid: broadcast.txid ?? signedTx.txID,
72
+ result: broadcast.result ?? false,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Simulate a populated EVM transaction against TRON via TronWeb (constant call / `eth_call`-style).
78
+ *
79
+ * Same calldata path as {@link submitTransaction}: `to` and `data` from the populated tx,
80
+ * EVM `to` converted to TRON Base58, empty function selector with `{ input: data }`.
81
+ * Does not sign or broadcast.
82
+ *
83
+ * @param tronWeb TronWeb instance with a default address (used as `caller`).
84
+ * @param populatedTx Must contain `to` and `data`.
85
+ * @param feeLimit Maximum TRX for energy, in SUN (mirrors `submitTransaction`).
86
+ */
87
+ export async function simulateTransaction(
88
+ tronWeb: TronWeb,
89
+ populatedTx: PopulatedTransaction,
90
+ feeLimit: number,
91
+ callValue: number = 0
92
+ ): Promise<TronSimulationResult> {
93
+ const { to, data } = populatedTx;
94
+ if (!to || !data) {
95
+ throw new Error("simulateTransaction: populatedTx must have both 'to' and 'data' fields");
96
+ }
97
+
98
+ const tronAddress = TvmAddress.from(to).toNative();
99
+ const ownerAddress = tronWeb.defaultAddress?.base58;
100
+ if (!ownerAddress) {
101
+ throw new Error("simulateTransaction: TronWeb instance must have a default address configured");
102
+ }
103
+
104
+ // `triggerConstantContract` is used to Invoke the readonly function (modified by the view or pure modifier) of a contract for contract data query;
105
+ // or to Invoke the non-readonly function of a contract for predicting whether the transaction can be successfully executed
106
+ // and estimating the energy consumption; or to estimate the energy consumption of contract deployment
107
+ const input = data.startsWith("0x") ? data.slice(2) : data;
108
+ const txWrapper = await tronWeb.transactionBuilder.triggerConstantContract(
109
+ tronAddress,
110
+ "",
111
+ { feeLimit, input, callValue },
112
+ [],
113
+ ownerAddress
114
+ );
115
+
116
+ const success = txWrapper?.result?.result === true;
117
+
118
+ return {
119
+ success,
120
+ message: txWrapper?.result?.message,
121
+ constantResult: txWrapper?.constant_result,
122
+ energyUsed: txWrapper?.energy_used,
123
+ energyRequired: txWrapper?.energy_required,
124
+ energyPenalty: txWrapper?.energy_penalty,
125
+ };
126
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./SpokeUtils";
2
+ export * from "./TransactionUtils";
@@ -20,6 +20,7 @@ import {
20
20
  import { SpokePoolClient } from "..";
21
21
  import { findFillEvent as findEvmFillEvent } from "../../arch/evm";
22
22
  import { findFillEvent as findSvmFillEvent } from "../../arch/svm";
23
+ import { findFillEvent as findTvmFillEvent } from "../../arch/tvm";
23
24
  import {
24
25
  BigNumber,
25
26
  bnZero,
@@ -40,6 +41,7 @@ import {
40
41
  toBytes32,
41
42
  convertRelayDataParamsToBytes32,
42
43
  convertFillParamsToBytes32,
44
+ chainIsTvm,
43
45
  } from "../../utils";
44
46
  import winston from "winston";
45
47
  import {
@@ -1308,7 +1310,8 @@ export class BundleDataClient {
1308
1310
  spokePoolClient.logger
1309
1311
  );
1310
1312
  } else if (isEVMSpokePoolClient(spokePoolClient)) {
1311
- return await findEvmFillEvent(
1313
+ const findFillEventHandler = chainIsTvm(spokePoolClient.chainId) ? findTvmFillEvent : findEvmFillEvent;
1314
+ return await findFillEventHandler(
1312
1315
  spokePoolClient.spokePool,
1313
1316
  deposit,
1314
1317
  spokePoolClient.deploymentBlock,
@@ -7,6 +7,12 @@ import {
7
7
  relayFillStatus,
8
8
  getTimestampForBlock as _getTimestampForBlock,
9
9
  } from "../../arch/evm";
10
+ import {
11
+ relayFillStatus as relayFillStatusTvm,
12
+ getMaxFillDeadlineInRange as getMaxFillDeadlineTvm,
13
+ getTimeAt as _getTimeAtTvm,
14
+ findDepositBlock as findDepositBlockTvm,
15
+ } from "../../arch/tvm";
10
16
  import { DepositWithBlock, FillStatus, Log, RelayData } from "../../interfaces";
11
17
  import {
12
18
  BigNumber,
@@ -17,6 +23,7 @@ import {
17
23
  toBN,
18
24
  EvmAddress,
19
25
  unpackDepositEvent,
26
+ chainIsTvm,
20
27
  } from "../../utils";
21
28
  import {
22
29
  EventSearchConfig,
@@ -36,6 +43,8 @@ import { EVM_SPOKE_POOL_CLIENT_TYPE } from "./types";
36
43
  */
37
44
  export class EVMSpokePoolClient extends SpokePoolClient {
38
45
  readonly type = EVM_SPOKE_POOL_CLIENT_TYPE;
46
+ readonly tvm: boolean;
47
+
39
48
  constructor(
40
49
  logger: winston.Logger,
41
50
  public readonly spokePool: Contract,
@@ -46,10 +55,12 @@ export class EVMSpokePoolClient extends SpokePoolClient {
46
55
  ) {
47
56
  super(logger, hubPoolClient, chainId, deploymentBlock, eventSearchConfig);
48
57
  this.spokePoolAddress = EvmAddress.from(spokePool.address);
58
+ this.tvm = chainIsTvm(this.chainId);
49
59
  }
50
60
 
51
61
  public override relayFillStatus(relayData: RelayData, atHeight?: number): Promise<FillStatus> {
52
- return relayFillStatus(this.spokePool, relayData, atHeight, this.chainId);
62
+ const fillStatusHandler = this.tvm ? relayFillStatusTvm : relayFillStatus;
63
+ return fillStatusHandler(this.spokePool, relayData, atHeight, this.chainId);
53
64
  }
54
65
 
55
66
  public override fillStatusArray(relayData: RelayData[], atHeight?: number): Promise<(FillStatus | undefined)[]> {
@@ -57,7 +68,8 @@ export class EVMSpokePoolClient extends SpokePoolClient {
57
68
  }
58
69
 
59
70
  public override getMaxFillDeadlineInRange(startBlock: number, endBlock: number): Promise<number> {
60
- return getMaxFillDeadline(this.spokePool, startBlock, endBlock);
71
+ const maxFillDeadlineInRangeHandler = this.tvm ? getMaxFillDeadlineTvm : getMaxFillDeadline;
72
+ return maxFillDeadlineInRangeHandler(this.spokePool, startBlock, endBlock);
61
73
  }
62
74
 
63
75
  private _availableEventsOnSpoke(eventNames: string[] = knownEventNames): { [eventName: string]: EventFilter } {
@@ -72,6 +84,42 @@ export class EVMSpokePoolClient extends SpokePoolClient {
72
84
  return Object.keys(this._availableEventsOnSpoke(knownEventNames));
73
85
  }
74
86
 
87
+ /**
88
+ * Retrieve the on-chain time at a specific block.
89
+ * EVM reads SpokePool.getCurrentTime() via multicall with a historical blockTag.
90
+ * @param blockNumber The block number to query.
91
+ * @returns The on-chain time as a number.
92
+ */
93
+ protected async _getCurrentTime(blockNumber: number): Promise<number> {
94
+ if (this.tvm) {
95
+ const block = await this.spokePool.provider.getBlock(blockNumber);
96
+ const currentTime = block.timestamp;
97
+ if (currentTime < this.currentTime) {
98
+ throw new Error(`EVMSpokePoolClient::_getCurrentTimeTvm: currentTime: ${currentTime} < ${this.currentTime}`);
99
+ }
100
+ return currentTime;
101
+ }
102
+ const { spokePool } = this;
103
+ const multicallFunctions = ["getCurrentTime"];
104
+ const multicallOutput = await spokePool.callStatic.multicall(
105
+ multicallFunctions.map((f) => spokePool.interface.encodeFunctionData(f)),
106
+ { blockTag: blockNumber }
107
+ );
108
+
109
+ const [currentTime] = multicallFunctions.map(
110
+ (fn, idx) => spokePool.interface.decodeFunctionResult(fn, multicallOutput[idx])[0]
111
+ );
112
+
113
+ if (!BigNumber.isBigNumber(currentTime) || currentTime.lt(this.currentTime)) {
114
+ const errMsg = BigNumber.isBigNumber(currentTime)
115
+ ? `currentTime: ${currentTime} < ${toBN(this.currentTime)}`
116
+ : `currentTime is not a BigNumber: ${JSON.stringify(currentTime)}`;
117
+ throw new Error(`EVMSpokePoolClient::update: ${errMsg}`);
118
+ }
119
+
120
+ return currentTime.toNumber();
121
+ }
122
+
75
123
  protected override async _update(eventsToQuery: string[]): Promise<SpokePoolUpdate> {
76
124
  const searchConfig = await this.updateSearchConfig(this.spokePool.provider);
77
125
  if (isUpdateFailureReason(searchConfig)) {
@@ -107,25 +155,14 @@ export class EVMSpokePoolClient extends SpokePoolClient {
107
155
  });
108
156
 
109
157
  const timerStart = Date.now();
110
- const multicallFunctions = ["getCurrentTime"];
111
- const [multicallOutput, ...events] = await Promise.all([
112
- spokePool.callStatic.multicall(
113
- multicallFunctions.map((f) => spokePool.interface.encodeFunctionData(f)),
114
- { blockTag: searchConfig.to }
115
- ),
158
+ const [currentTime, ...events] = await Promise.all([
159
+ this._getCurrentTime(searchConfig.to),
116
160
  ...eventSearchConfigs.map((config) => paginatedEventQuery(this.spokePool, config.filter, config.searchConfig)),
117
161
  ]);
118
162
  this.log("debug", `Time to query new events from RPC for ${this.chainId}: ${Date.now() - timerStart} ms`);
119
163
 
120
- const [currentTime] = multicallFunctions.map(
121
- (fn, idx) => spokePool.interface.decodeFunctionResult(fn, multicallOutput[idx])[0]
122
- );
123
-
124
- if (!BigNumber.isBigNumber(currentTime) || currentTime.lt(this.currentTime)) {
125
- const errMsg = BigNumber.isBigNumber(currentTime)
126
- ? `currentTime: ${currentTime} < ${toBN(this.currentTime)}`
127
- : `currentTime is not a BigNumber: ${JSON.stringify(currentTime)}`;
128
- throw new Error(`EVMSpokePoolClient::update: ${errMsg}`);
164
+ if (currentTime < this.currentTime) {
165
+ throw new Error(`EVMSpokePoolClient::update: currentTime: ${currentTime} < ${this.currentTime}`);
129
166
  }
130
167
 
131
168
  // Sort all events to ensure they are stored in a consistent order.
@@ -138,14 +175,15 @@ export class EVMSpokePoolClient extends SpokePoolClient {
138
175
 
139
176
  return {
140
177
  success: true,
141
- currentTime: currentTime.toNumber(), // uint32
178
+ currentTime,
142
179
  searchEndBlock: searchConfig.to,
143
180
  events: eventsWithBlockNumber,
144
181
  };
145
182
  }
146
183
 
147
184
  public override getTimeAt(blockNumber: number): Promise<number> {
148
- return _getTimeAt(this.spokePool, blockNumber);
185
+ const getTimeAtHandler = this.tvm ? _getTimeAtTvm : _getTimeAt;
186
+ return getTimeAtHandler(this.spokePool, blockNumber);
149
187
  }
150
188
 
151
189
  public override async findDeposit(depositId: BigNumber): Promise<DepositSearchResult> {
@@ -190,12 +228,22 @@ export class EVMSpokePoolClient extends SpokePoolClient {
190
228
  return _getTimestampForBlock(this.spokePool.provider, blockNumber);
191
229
  }
192
230
 
193
- private async queryDepositEvents(
231
+ /**
232
+ * Find the block at which a deposit was created.
233
+ * EVM uses a binary-search over historical numberOfDeposits().
234
+ * TVM overrides this with an event-based lookup.
235
+ */
236
+ protected _findDepositBlock(depositId: BigNumber, lowBlock: number, highBlock?: number): Promise<number | undefined> {
237
+ const findDepositBlockHandler = this.tvm ? findDepositBlockTvm : findDepositBlock;
238
+ return findDepositBlockHandler(this.spokePool, depositId, lowBlock, highBlock);
239
+ }
240
+
241
+ protected async queryDepositEvents(
194
242
  depositId: BigNumber
195
243
  ): Promise<{ event: Log; elapsedMs: number } | { reason: string }> {
196
244
  const tStart = Date.now();
197
245
  const upperBound = this.latestHeightSearched || undefined;
198
- const from = await findDepositBlock(this.spokePool, depositId, this.deploymentBlock, upperBound);
246
+ const from = await this._findDepositBlock(depositId, this.deploymentBlock, upperBound);
199
247
  const chain = getNetworkName(this.chainId);
200
248
 
201
249
  if (!from) {
@@ -113,6 +113,7 @@ function getGasPriceEthers(provider: providers.Provider, opts: GasPriceEstimateO
113
113
  [CHAIN_IDs.POLYGON]: polygon.gasStation,
114
114
  [CHAIN_IDs.SCROLL]: ethereum.legacy,
115
115
  [CHAIN_IDs.TEMPO]: ethereum.eip1559,
116
+ [CHAIN_IDs.TRON]: ethereum.legacy,
116
117
  [CHAIN_IDs.ZK_SYNC]: ethereum.legacy,
117
118
  // Testnet
118
119
  [CHAIN_IDs.POLYGON_AMOY]: polygon.gasStation,
@@ -9,6 +9,7 @@ import { populateV3Relay } from "../../arch/evm";
9
9
  import {
10
10
  BigNumberish,
11
11
  EvmAddress,
12
+ TvmAddress,
12
13
  TransactionCostEstimate,
13
14
  BigNumber,
14
15
  toBNWei,
@@ -85,9 +86,15 @@ export class QueryBase implements QueryInterface {
85
86
  const { gasPrice = this.fixedGasPrice, gasUnits, opStackL1GasCostMultiplier } = options;
86
87
 
87
88
  const { recipient, outputToken, exclusiveRelayer } = relayData;
88
- assert(recipient.isEVM(), `getGasCosts: recipient not an EVM address (${recipient})`);
89
- assert(outputToken.isEVM(), `getGasCosts: outputToken not an EVM address (${outputToken})`);
90
- assert(exclusiveRelayer.isEVM(), `getGasCosts: exclusiveRelayer not an EVM address (${exclusiveRelayer})`);
89
+ assert(recipient.isEVM() || recipient.isTVM(), `getGasCosts: recipient not an EVM-like address (${recipient})`);
90
+ assert(
91
+ outputToken.isEVM() || outputToken.isTVM(),
92
+ `getGasCosts: outputToken not an EVM-like address (${outputToken})`
93
+ );
94
+ assert(
95
+ exclusiveRelayer.isEVM() || exclusiveRelayer.isTVM(),
96
+ `getGasCosts: exclusiveRelayer not an EVM-like address (${exclusiveRelayer})`
97
+ );
91
98
 
92
99
  const tx = await this.getUnsignedTxFromDeposit({ ...relayData, recipient, outputToken, exclusiveRelayer }, relayer);
93
100
  const {
@@ -119,8 +126,8 @@ export class QueryBase implements QueryInterface {
119
126
  getUnsignedTxFromDeposit(
120
127
  relayData: Omit<RelayData, "recipient" | "outputToken"> & {
121
128
  destinationChainId: number;
122
- recipient: EvmAddress;
123
- outputToken: EvmAddress;
129
+ recipient: EvmAddress | TvmAddress;
130
+ outputToken: EvmAddress | TvmAddress;
124
131
  },
125
132
  relayer = getDefaultRelayer(relayData.destinationChainId)
126
133
  ): Promise<PopulatedTransaction> {
@@ -138,9 +145,18 @@ export class QueryBase implements QueryInterface {
138
145
  relayer = getDefaultRelayer(relayData.destinationChainId)
139
146
  ): Promise<BigNumber> {
140
147
  const { recipient, outputToken, exclusiveRelayer } = relayData;
141
- assert(recipient.isEVM(), `getNativeGasCost: recipient not an EVM address (${recipient})`);
142
- assert(outputToken.isEVM(), `getNativeGasCost: outputToken not an EVM address (${outputToken})`);
143
- assert(exclusiveRelayer.isEVM(), `getNativeGasCost: exclusiveRelayer not an EVM address (${exclusiveRelayer})`);
148
+ assert(
149
+ recipient.isEVM() || recipient.isTVM(),
150
+ `getNativeGasCost: recipient not an EVM-like address (${recipient})`
151
+ );
152
+ assert(
153
+ outputToken.isEVM() || outputToken.isTVM(),
154
+ `getNativeGasCost: outputToken not an EVM-like address (${outputToken})`
155
+ );
156
+ assert(
157
+ exclusiveRelayer.isEVM() || exclusiveRelayer.isTVM(),
158
+ `getNativeGasCost: exclusiveRelayer not an EVM-like address (${exclusiveRelayer})`
159
+ );
144
160
 
145
161
  const unsignedTx = await this.getUnsignedTxFromDeposit(
146
162
  { ...relayData, recipient, outputToken, exclusiveRelayer },