@across-protocol/sdk 4.1.43 → 4.1.45-beta.0

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 (184) hide show
  1. package/dist/cjs/addressAggregator/adapters/abstract.d.ts +15 -0
  2. package/dist/cjs/addressAggregator/adapters/abstract.js +83 -0
  3. package/dist/cjs/addressAggregator/adapters/abstract.js.map +1 -0
  4. package/dist/cjs/addressAggregator/adapters/bybit.d.ts +5 -9
  5. package/dist/cjs/addressAggregator/adapters/bybit.js +11 -12
  6. package/dist/cjs/addressAggregator/adapters/bybit.js.map +1 -1
  7. package/dist/cjs/addressAggregator/adapters/env.d.ts +5 -7
  8. package/dist/cjs/addressAggregator/adapters/env.js +12 -11
  9. package/dist/cjs/addressAggregator/adapters/env.js.map +1 -1
  10. package/dist/cjs/addressAggregator/adapters/file.d.ts +5 -7
  11. package/dist/cjs/addressAggregator/adapters/file.js +13 -12
  12. package/dist/cjs/addressAggregator/adapters/file.js.map +1 -1
  13. package/dist/cjs/addressAggregator/adapters/risklabs.d.ts +5 -9
  14. package/dist/cjs/addressAggregator/adapters/risklabs.js +18 -14
  15. package/dist/cjs/addressAggregator/adapters/risklabs.js.map +1 -1
  16. package/dist/cjs/addressAggregator/index.js +2 -2
  17. package/dist/cjs/addressAggregator/index.js.map +1 -1
  18. package/dist/cjs/addressAggregator/types.d.ts +9 -1
  19. package/dist/cjs/addressAggregator/types.js.map +1 -1
  20. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +13 -6
  21. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  22. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +9 -0
  23. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  24. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +7 -6
  25. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +46 -35
  26. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  27. package/dist/cjs/clients/HubPoolClient.d.ts +2 -0
  28. package/dist/cjs/clients/HubPoolClient.js +27 -10
  29. package/dist/cjs/clients/HubPoolClient.js.map +1 -1
  30. package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
  31. package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js +16 -3
  32. package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
  33. package/dist/cjs/clients/mocks/MockHubPoolClient.d.ts +2 -0
  34. package/dist/cjs/clients/mocks/MockHubPoolClient.js +18 -0
  35. package/dist/cjs/clients/mocks/MockHubPoolClient.js.map +1 -1
  36. package/dist/cjs/constants.d.ts +1 -0
  37. package/dist/cjs/constants.js +13 -1
  38. package/dist/cjs/constants.js.map +1 -1
  39. package/dist/cjs/svm/eventsClient.js +5 -12
  40. package/dist/cjs/svm/eventsClient.js.map +1 -1
  41. package/dist/cjs/svm/index.d.ts +2 -0
  42. package/dist/cjs/svm/index.js +3 -0
  43. package/dist/cjs/svm/index.js.map +1 -1
  44. package/dist/cjs/svm/utils/events.d.ts +6 -1
  45. package/dist/cjs/svm/utils/events.js +13 -4
  46. package/dist/cjs/svm/utils/events.js.map +1 -1
  47. package/dist/cjs/svm/utils/index.d.ts +1 -0
  48. package/dist/cjs/svm/utils/index.js +5 -0
  49. package/dist/cjs/svm/utils/index.js.map +1 -0
  50. package/dist/cjs/utils/DepositUtils.d.ts +1 -0
  51. package/dist/cjs/utils/DepositUtils.js +5 -1
  52. package/dist/cjs/utils/DepositUtils.js.map +1 -1
  53. package/dist/cjs/utils/TokenUtils.d.ts +1 -0
  54. package/dist/cjs/utils/TokenUtils.js +5 -1
  55. package/dist/cjs/utils/TokenUtils.js.map +1 -1
  56. package/dist/cjs/utils/common.d.ts +2 -0
  57. package/dist/cjs/utils/common.js +3 -1
  58. package/dist/cjs/utils/common.js.map +1 -1
  59. package/dist/esm/addressAggregator/adapters/abstract.d.ts +15 -0
  60. package/dist/esm/addressAggregator/adapters/abstract.js +80 -0
  61. package/dist/esm/addressAggregator/adapters/abstract.js.map +1 -0
  62. package/dist/esm/addressAggregator/adapters/bybit.d.ts +5 -9
  63. package/dist/esm/addressAggregator/adapters/bybit.js +12 -13
  64. package/dist/esm/addressAggregator/adapters/bybit.js.map +1 -1
  65. package/dist/esm/addressAggregator/adapters/env.d.ts +5 -7
  66. package/dist/esm/addressAggregator/adapters/env.js +12 -11
  67. package/dist/esm/addressAggregator/adapters/env.js.map +1 -1
  68. package/dist/esm/addressAggregator/adapters/file.d.ts +5 -7
  69. package/dist/esm/addressAggregator/adapters/file.js +14 -13
  70. package/dist/esm/addressAggregator/adapters/file.js.map +1 -1
  71. package/dist/esm/addressAggregator/adapters/risklabs.d.ts +5 -9
  72. package/dist/esm/addressAggregator/adapters/risklabs.js +19 -15
  73. package/dist/esm/addressAggregator/adapters/risklabs.js.map +1 -1
  74. package/dist/esm/addressAggregator/index.js +2 -2
  75. package/dist/esm/addressAggregator/index.js.map +1 -1
  76. package/dist/esm/addressAggregator/types.d.ts +9 -1
  77. package/dist/esm/addressAggregator/types.js.map +1 -1
  78. package/dist/esm/clients/BundleDataClient/BundleDataClient.js +14 -7
  79. package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  80. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +15 -0
  81. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  82. package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +28 -6
  83. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +82 -49
  84. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  85. package/dist/esm/clients/HubPoolClient.d.ts +2 -0
  86. package/dist/esm/clients/HubPoolClient.js +31 -13
  87. package/dist/esm/clients/HubPoolClient.js.map +1 -1
  88. package/dist/esm/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
  89. package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js +24 -11
  90. package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
  91. package/dist/esm/clients/mocks/MockHubPoolClient.d.ts +2 -0
  92. package/dist/esm/clients/mocks/MockHubPoolClient.js +18 -0
  93. package/dist/esm/clients/mocks/MockHubPoolClient.js.map +1 -1
  94. package/dist/esm/constants.d.ts +1 -0
  95. package/dist/esm/constants.js +12 -0
  96. package/dist/esm/constants.js.map +1 -1
  97. package/dist/esm/svm/eventsClient.js +6 -13
  98. package/dist/esm/svm/eventsClient.js.map +1 -1
  99. package/dist/esm/svm/index.d.ts +2 -0
  100. package/dist/esm/svm/index.js +3 -0
  101. package/dist/esm/svm/index.js.map +1 -1
  102. package/dist/esm/svm/utils/events.d.ts +9 -1
  103. package/dist/esm/svm/utils/events.js +15 -3
  104. package/dist/esm/svm/utils/events.js.map +1 -1
  105. package/dist/esm/svm/utils/index.d.ts +1 -0
  106. package/dist/esm/svm/utils/index.js +2 -0
  107. package/dist/esm/svm/utils/index.js.map +1 -0
  108. package/dist/esm/utils/DepositUtils.d.ts +1 -0
  109. package/dist/esm/utils/DepositUtils.js +5 -1
  110. package/dist/esm/utils/DepositUtils.js.map +1 -1
  111. package/dist/esm/utils/TokenUtils.d.ts +1 -0
  112. package/dist/esm/utils/TokenUtils.js +3 -0
  113. package/dist/esm/utils/TokenUtils.js.map +1 -1
  114. package/dist/esm/utils/common.d.ts +2 -0
  115. package/dist/esm/utils/common.js +2 -0
  116. package/dist/esm/utils/common.js.map +1 -1
  117. package/dist/types/addressAggregator/adapters/abstract.d.ts +16 -0
  118. package/dist/types/addressAggregator/adapters/abstract.d.ts.map +1 -0
  119. package/dist/types/addressAggregator/adapters/bybit.d.ts +5 -9
  120. package/dist/types/addressAggregator/adapters/bybit.d.ts.map +1 -1
  121. package/dist/types/addressAggregator/adapters/env.d.ts +5 -7
  122. package/dist/types/addressAggregator/adapters/env.d.ts.map +1 -1
  123. package/dist/types/addressAggregator/adapters/file.d.ts +5 -7
  124. package/dist/types/addressAggregator/adapters/file.d.ts.map +1 -1
  125. package/dist/types/addressAggregator/adapters/risklabs.d.ts +5 -9
  126. package/dist/types/addressAggregator/adapters/risklabs.d.ts.map +1 -1
  127. package/dist/types/addressAggregator/types.d.ts +9 -1
  128. package/dist/types/addressAggregator/types.d.ts.map +1 -1
  129. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  130. package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
  131. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +28 -6
  132. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
  133. package/dist/types/clients/HubPoolClient.d.ts +2 -0
  134. package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
  135. package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
  136. package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts.map +1 -1
  137. package/dist/types/clients/mocks/MockHubPoolClient.d.ts +2 -0
  138. package/dist/types/clients/mocks/MockHubPoolClient.d.ts.map +1 -1
  139. package/dist/types/constants.d.ts +1 -0
  140. package/dist/types/constants.d.ts.map +1 -1
  141. package/dist/types/svm/eventsClient.d.ts.map +1 -1
  142. package/dist/types/svm/index.d.ts +2 -0
  143. package/dist/types/svm/index.d.ts.map +1 -1
  144. package/dist/types/svm/utils/events.d.ts +9 -1
  145. package/dist/types/svm/utils/events.d.ts.map +1 -1
  146. package/dist/types/svm/utils/index.d.ts +2 -0
  147. package/dist/types/svm/utils/index.d.ts.map +1 -0
  148. package/dist/types/utils/DepositUtils.d.ts +1 -0
  149. package/dist/types/utils/DepositUtils.d.ts.map +1 -1
  150. package/dist/types/utils/TokenUtils.d.ts +1 -0
  151. package/dist/types/utils/TokenUtils.d.ts.map +1 -1
  152. package/dist/types/utils/common.d.ts +2 -0
  153. package/dist/types/utils/common.d.ts.map +1 -1
  154. package/package.json +1 -1
  155. package/src/addressAggregator/adapters/abstract.ts +74 -0
  156. package/src/addressAggregator/adapters/bybit.ts +10 -11
  157. package/src/addressAggregator/adapters/env.ts +10 -10
  158. package/src/addressAggregator/adapters/file.ts +11 -12
  159. package/src/addressAggregator/adapters/risklabs.ts +14 -13
  160. package/src/addressAggregator/index.ts +2 -2
  161. package/src/addressAggregator/types.ts +10 -1
  162. package/src/clients/BundleDataClient/BundleDataClient.ts +28 -18
  163. package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +29 -0
  164. package/src/clients/BundleDataClient/utils/FillUtils.ts +128 -71
  165. package/src/clients/HubPoolClient.ts +38 -14
  166. package/src/clients/SpokePoolClient/SpokePoolClient.ts +40 -14
  167. package/src/clients/mocks/MockHubPoolClient.ts +16 -0
  168. package/src/constants.ts +13 -0
  169. package/src/svm/eventsClient.ts +6 -12
  170. package/src/svm/index.ts +2 -0
  171. package/src/svm/utils/events.ts +16 -4
  172. package/src/svm/utils/index.ts +1 -0
  173. package/src/utils/DepositUtils.ts +6 -1
  174. package/src/utils/TokenUtils.ts +6 -0
  175. package/src/utils/common.ts +2 -0
  176. package/dist/cjs/addressAggregator/adapters/util.d.ts +0 -4
  177. package/dist/cjs/addressAggregator/adapters/util.js +0 -66
  178. package/dist/cjs/addressAggregator/adapters/util.js.map +0 -1
  179. package/dist/esm/addressAggregator/adapters/util.d.ts +0 -4
  180. package/dist/esm/addressAggregator/adapters/util.js +0 -60
  181. package/dist/esm/addressAggregator/adapters/util.js.map +0 -1
  182. package/dist/types/addressAggregator/adapters/util.d.ts +0 -5
  183. package/dist/types/addressAggregator/adapters/util.d.ts.map +0 -1
  184. package/src/addressAggregator/adapters/util.ts +0 -47
@@ -1,124 +1,119 @@
1
1
  import _ from "lodash";
2
+ import assert from "assert";
2
3
  import { providers } from "ethers";
3
- import { Deposit, DepositWithBlock, Fill, FillWithBlock } from "../../../interfaces";
4
- import { getBlockRangeForChain, isSlowFill, isValidEvmAddress, isDefined, chainIsEvm } from "../../../utils";
4
+ import { DepositWithBlock, Fill, FillWithBlock } from "../../../interfaces";
5
+ import { isSlowFill, isValidEvmAddress, isDefined, chainIsEvm } from "../../../utils";
5
6
  import { HubPoolClient } from "../../HubPoolClient";
6
7
 
8
+ /**
9
+ * @notice FillRepaymentInformation is a fill with additional properties required to determine where it can
10
+ * be repaid.
11
+ */
12
+ type FillRepaymentInformation = Fill & { quoteBlockNumber: number; fromLiteChain: boolean };
13
+
14
+ /**
15
+ * @notice Return repayment chain and repayment token for a fill, but does not verify if the returned
16
+ * repayment information is valid for the desired repayment address.
17
+ * @dev The passed in fill should be verified first via verifyFillRepayment().
18
+ * @param fill The fill to get the repayment information for.
19
+ * @return The chain to send the refund to and the token to use for the refund.
20
+ */
7
21
  export function getRefundInformationFromFill(
8
- fill: Fill,
9
- hubPoolClient: HubPoolClient,
10
- blockRangesForChains: number[][],
11
- chainIdListForBundleEvaluationBlockNumbers: number[],
12
- fromLiteChain: boolean
22
+ relayData: FillRepaymentInformation,
23
+ hubPoolClient: HubPoolClient
13
24
  ): {
14
25
  chainToSendRefundTo: number;
15
26
  repaymentToken: string;
16
27
  } {
17
- // Handle slow relay where repaymentChainId = 0. Slow relays always pay recipient on destination chain.
18
- // So, save the slow fill under the destination chain, and save the fast fill under its repayment chain.
19
- let chainToSendRefundTo = isSlowFill(fill) ? fill.destinationChainId : fill.repaymentChainId;
20
- // If the fill is for a deposit originating from the lite chain, the repayment chain is the origin chain
21
- // regardless of whether it is a slow or fast fill (we ignore slow fills but this is for posterity).
22
- // @note fill.repaymentChainId should already be set to originChainId but reset it to be safe.
23
- if (fromLiteChain) {
24
- chainToSendRefundTo = fill.originChainId;
28
+ const chainToSendRefundTo = _getRepaymentChainId(relayData, hubPoolClient);
29
+ if (chainToSendRefundTo === relayData.originChainId) {
30
+ return {
31
+ chainToSendRefundTo,
32
+ repaymentToken: relayData.inputToken,
33
+ };
25
34
  }
26
35
 
27
- // Save fill data and associate with repayment chain and L2 token refund should be denominated in.
28
- const endBlockForMainnet = getBlockRangeForChain(
29
- blockRangesForChains,
30
- hubPoolClient.chainId,
31
- chainIdListForBundleEvaluationBlockNumbers
32
- )[1];
33
-
36
+ // Now figure out the equivalent L2 token for the repayment token. If the inputToken doesn't have a
37
+ // PoolRebalanceRoute, then the repayment chain would have been the originChainId after the getRepaymentChainId()
38
+ // call and we would have returned already, so the following call should always succeed.
34
39
  const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
35
- fill.inputToken,
36
- fill.originChainId,
37
- endBlockForMainnet
40
+ relayData.inputToken,
41
+ relayData.originChainId,
42
+ relayData.quoteBlockNumber
38
43
  );
39
44
 
40
45
  const repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(
41
46
  l1TokenCounterpart,
42
47
  chainToSendRefundTo,
43
- endBlockForMainnet
48
+ relayData.quoteBlockNumber
44
49
  );
45
50
  return {
46
51
  chainToSendRefundTo,
47
52
  repaymentToken,
48
53
  };
49
54
  }
50
-
51
- export function getRepaymentChainId(fill: Fill, matchedDeposit: Deposit): number {
52
- // Lite chain deposits force repayment on origin chain.
53
- return matchedDeposit.fromLiteChain ? matchedDeposit.originChainId : fill.repaymentChainId;
54
- }
55
-
56
- export function forceDestinationRepayment(
57
- repaymentChainId: number,
58
- matchedDeposit: Deposit & { quoteBlockNumber: number },
59
- hubPoolClient: HubPoolClient
60
- ): boolean {
61
- if (!matchedDeposit.fromLiteChain) {
62
- try {
63
- const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
64
- matchedDeposit.inputToken,
65
- matchedDeposit.originChainId,
66
- matchedDeposit.quoteBlockNumber
67
- );
68
- hubPoolClient.getL2TokenForL1TokenAtBlock(l1TokenCounterpart, repaymentChainId, matchedDeposit.quoteBlockNumber);
69
- // Repayment token could be found, this is a valid repayment chain.
70
- return false;
71
- } catch {
72
- // Repayment token doesn't exist on repayment chain via PoolRebalanceRoutes, impossible to repay filler there.
73
- return true;
74
- }
75
- } else {
76
- return false;
77
- }
78
- }
79
-
80
- // Verify that a fill sent to an EVM chain has a 20 byte address. If the fill does not, then attempt
81
- // to repay the `msg.sender` of the relay transaction. Otherwise, return undefined.
55
+ /**
56
+ * @notice Verifies that the fill can be repaid. If the repayment address is not
57
+ * valid for the requested repayment chain, then this function will attempt to change the fill's repayment chain
58
+ * to the destination chain and its repayment address to the msg.sender and if this is possible,
59
+ * return the fill. Otherwise, return undefined.
60
+ * @param _fill Fill with a requested repayment chain and address
61
+ * @return Fill with the applied repayment chain (depends on the validity of the requested repayment address)
62
+ * and applied repayment address, or undefined if the applied repayment address is not valid for the
63
+ * applied repayment chain.
64
+ */
82
65
  export async function verifyFillRepayment(
83
66
  _fill: FillWithBlock,
84
67
  destinationChainProvider: providers.Provider,
85
68
  matchedDeposit: DepositWithBlock,
86
69
  hubPoolClient: HubPoolClient
87
70
  ): Promise<FillWithBlock | undefined> {
88
- const fill = _.cloneDeep(_fill);
71
+ const fill = {
72
+ ..._.cloneDeep(_fill),
73
+ fromLiteChain: matchedDeposit.fromLiteChain,
74
+ quoteBlockNumber: matchedDeposit.quoteBlockNumber,
75
+ };
89
76
 
90
77
  // Slow fills don't result in repayments so they're always valid.
91
78
  if (isSlowFill(fill)) {
92
79
  return fill;
93
80
  }
94
81
 
95
- let repaymentChainId = getRepaymentChainId(fill, matchedDeposit);
82
+ let repaymentChainId = _getRepaymentChainId(fill, hubPoolClient);
96
83
 
97
- // If repayment chain doesn't have a Pool Rebalance Route for the input token, then change the repayment
98
- // chain to the destination chain.
99
- if (forceDestinationRepayment(repaymentChainId, matchedDeposit, hubPoolClient)) {
100
- repaymentChainId = fill.destinationChainId;
101
- }
102
-
103
- if (!isValidEvmAddress(fill.relayer)) {
84
+ // Repayments will always go to the fill.relayer address so check if its a valid EVM address. If its not, attempt
85
+ // to change it to the msg.sender of the FilledRelay.
86
+ if (_repaymentAddressNeedsToBeOverwritten(fill)) {
104
87
  // TODO: Handle case where fill was sent on non-EVM chain, in which case the following call would fail
105
88
  // or return something unexpected. We'd want to return undefined here.
89
+
90
+ // @todo: If chainIsEvm:
106
91
  const fillTransaction = await destinationChainProvider.getTransaction(fill.transactionHash);
107
92
  const destinationRelayer = fillTransaction?.from;
108
93
  // Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
109
94
  if (!isDefined(destinationRelayer) || !isValidEvmAddress(destinationRelayer)) {
110
95
  return undefined;
111
96
  }
112
- if (!matchedDeposit.fromLiteChain) {
97
+ // If we can switch the repayment chain to the destination chain, then do so. We should only switch if the
98
+ // destination chain has a valid repayment token that is equivalent to the deposited input token. This would
99
+ // also be the same mapping as the repayment token on the repayment chain.
100
+ if (
101
+ !matchedDeposit.fromLiteChain &&
102
+ hubPoolClient.areTokensEquivalent(fill.inputToken, fill.originChainId, fill.outputToken, fill.destinationChainId)
103
+ ) {
113
104
  repaymentChainId = fill.destinationChainId;
114
- } else {
115
- // We can't switch repayment chain for a lite chain deposit so just check whether the repayment chain,
116
- // which should be the origin chain, is an EVM chain.
105
+ }
106
+ // If we can't switch the chain, then we need to verify that the msg.sender is a valid address on the repayment chain.
107
+ // Because we already checked that the `destinationRelayer` was a valid EVM address above, we only need to check
108
+ // that the repayment chain is an EVM chain.
109
+ else {
117
110
  if (!chainIsEvm(repaymentChainId)) {
118
111
  return undefined;
119
112
  }
120
113
  }
121
114
  fill.relayer = destinationRelayer;
115
+
116
+ // @todo: If chainIsSvm:
122
117
  }
123
118
 
124
119
  // Repayment address is now valid and repayment chain is either origin chain for lite chain or the destination
@@ -126,3 +121,65 @@ export async function verifyFillRepayment(
126
121
  fill.repaymentChainId = repaymentChainId;
127
122
  return fill;
128
123
  }
124
+
125
+ function _getRepaymentChainId(relayData: FillRepaymentInformation, hubPoolClient: HubPoolClient): number {
126
+ if (relayData.fromLiteChain) {
127
+ assert(!isSlowFill(relayData), "getRepaymentChainId: fromLiteChain and slow fill are mutually exclusive");
128
+ return relayData.originChainId;
129
+ }
130
+
131
+ // Handle slow relay where FilledRelay.repaymentChainId = 0, as hardcoded in the SpokePool contract.
132
+ // Slow relays always pay recipient on destination chain.
133
+ if (isSlowFill(relayData)) {
134
+ return relayData.destinationChainId;
135
+ }
136
+
137
+ // Repayment chain is valid if the input token and repayment chain are mapped to the same PoolRebalanceRoute.
138
+ const repaymentTokenIsValid = _repaymentChainTokenIsValid(relayData, hubPoolClient);
139
+ if (repaymentTokenIsValid) {
140
+ return relayData.repaymentChainId;
141
+ }
142
+
143
+ // If repayment chain is not valid, default to origin chain since we always know the input token can be refunded.
144
+ return relayData.originChainId;
145
+ }
146
+
147
+ function _repaymentChainTokenIsValid(relayData: FillRepaymentInformation, hubPoolClient: HubPoolClient): boolean {
148
+ if (
149
+ !hubPoolClient.l2TokenHasPoolRebalanceRoute(
150
+ relayData.inputToken,
151
+ relayData.originChainId,
152
+ relayData.quoteBlockNumber
153
+ )
154
+ ) {
155
+ return false;
156
+ }
157
+ const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
158
+ relayData.inputToken,
159
+ relayData.originChainId,
160
+ relayData.quoteBlockNumber
161
+ );
162
+ if (
163
+ !hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
164
+ l1TokenCounterpart,
165
+ relayData.repaymentChainId,
166
+ relayData.quoteBlockNumber
167
+ )
168
+ ) {
169
+ return false;
170
+ }
171
+ return true;
172
+ }
173
+
174
+ function _repaymentAddressNeedsToBeOverwritten(fill: Fill): boolean {
175
+ // Slow fills don't result in repayments so they're always valid.
176
+ if (isSlowFill(fill)) {
177
+ return false;
178
+ }
179
+
180
+ // @todo add Solana logic here:
181
+ // - i.e. If chainIsSvm && !isValidSvmAddress(fill.relayer) then return false
182
+ // If chainIsEvm && !isValidEvmAddress(fill.relayer) then return false
183
+ // If chainIsEvm && isValidEvmAddress(fill.relayer) then return true
184
+ return !isValidEvmAddress(fill.relayer);
185
+ }
@@ -265,6 +265,28 @@ export class HubPoolClient extends BaseAbstractClient {
265
265
  return this.l1TokensToDestinationTokens?.[l1Token]?.[destinationChainId] != undefined;
266
266
  }
267
267
 
268
+ l2TokenEnabledForL1TokenAtBlock(l1Token: string, destinationChainId: number, hubBlockNumber: number): boolean {
269
+ // Find the last mapping published before the target block.
270
+ const l2Token: DestinationTokenWithBlock | undefined = sortEventsDescending(
271
+ this.l1TokensToDestinationTokensWithBlock?.[l1Token]?.[destinationChainId] ?? []
272
+ ).find((mapping: DestinationTokenWithBlock) => mapping.blockNumber <= hubBlockNumber);
273
+ return l2Token !== undefined;
274
+ }
275
+
276
+ l2TokenHasPoolRebalanceRoute(l2Token: string, l2ChainId: number, hubPoolBlock = this.latestBlockSearched): boolean {
277
+ return Object.values(this.l1TokensToDestinationTokensWithBlock).some((destinationTokenMapping) => {
278
+ return Object.entries(destinationTokenMapping).some(([_l2ChainId, setPoolRebalanceRouteEvents]) => {
279
+ return setPoolRebalanceRouteEvents.some((e) => {
280
+ return (
281
+ e.blockNumber <= hubPoolBlock &&
282
+ compareAddressesSimple(e.l2Token, l2Token) &&
283
+ Number(_l2ChainId) === l2ChainId
284
+ );
285
+ });
286
+ });
287
+ });
288
+ }
289
+
268
290
  /**
269
291
  * @dev If tokenAddress + chain do not exist in TOKEN_SYMBOLS_MAP then this will throw.
270
292
  * @param tokenAddress Token address on `chain`
@@ -529,21 +551,23 @@ export class HubPoolClient extends BaseAbstractClient {
529
551
  chainIdB: number,
530
552
  hubPoolBlock = this.latestBlockSearched
531
553
  ): boolean {
532
- try {
533
- // Resolve both SpokePool tokens back to their respective HubPool tokens and verify that they match.
534
- const l1TokenA = this.getL1TokenForL2TokenAtBlock(tokenA, chainIdA, hubPoolBlock);
535
- const l1TokenB = this.getL1TokenForL2TokenAtBlock(tokenB, chainIdB, hubPoolBlock);
536
- if (l1TokenA !== l1TokenB) {
537
- return false;
538
- }
539
-
540
- // Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
541
- const _tokenA = this.getL2TokenForL1TokenAtBlock(l1TokenA, chainIdA, hubPoolBlock);
542
- const _tokenB = this.getL2TokenForL1TokenAtBlock(l1TokenB, chainIdB, hubPoolBlock);
543
- return tokenA === _tokenA && tokenB === _tokenB;
544
- } catch {
545
- return false; // One or both input tokens were not recognised.
554
+ if (
555
+ !this.l2TokenHasPoolRebalanceRoute(tokenA, chainIdA, hubPoolBlock) ||
556
+ !this.l2TokenHasPoolRebalanceRoute(tokenB, chainIdB, hubPoolBlock)
557
+ ) {
558
+ return false;
559
+ }
560
+ // Resolve both SpokePool tokens back to their respective HubPool tokens and verify that they match.
561
+ const l1TokenA = this.getL1TokenForL2TokenAtBlock(tokenA, chainIdA, hubPoolBlock);
562
+ const l1TokenB = this.getL1TokenForL2TokenAtBlock(tokenB, chainIdB, hubPoolBlock);
563
+ if (l1TokenA !== l1TokenB) {
564
+ return false;
546
565
  }
566
+
567
+ // Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
568
+ const _tokenA = this.getL2TokenForL1TokenAtBlock(l1TokenA, chainIdA, hubPoolBlock);
569
+ const _tokenB = this.getL2TokenForL1TokenAtBlock(l1TokenB, chainIdB, hubPoolBlock);
570
+ return tokenA === _tokenA && tokenB === _tokenB;
547
571
  }
548
572
 
549
573
  getSpokeActivationBlockForChain(chainId: number): number {
@@ -17,6 +17,7 @@ import {
17
17
  isZeroAddress,
18
18
  toAddress,
19
19
  validateFillForDeposit,
20
+ chainIsEvm,
20
21
  } from "../../utils";
21
22
  import {
22
23
  duplicateEvent,
@@ -41,7 +42,7 @@ import {
41
42
  } from "../../interfaces";
42
43
  import { BaseAbstractClient, UpdateFailureReason } from "../BaseAbstractClient";
43
44
  import { AcrossConfigStoreClient } from "../AcrossConfigStoreClient";
44
- import { getRepaymentChainId, forceDestinationRepayment } from "../BundleDataClient";
45
+ import { getRefundInformationFromFill } from "../BundleDataClient";
45
46
  import { HubPoolClient } from "../HubPoolClient";
46
47
 
47
48
  export type SpokePoolUpdateSuccess = {
@@ -365,22 +366,25 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
365
366
  fill: FillWithBlock
366
367
  ) => {
367
368
  if (validateFillForDeposit(fill, deposit).valid) {
368
- const repaymentChainId = getRepaymentChainId(fill, deposit);
369
- // In order to keep this function sync, we can't call verifyFillRepayment so we'll log any fills that
370
- // we'll have to overwrite repayment information for. This includes fills for lite chains where the
371
- // repayment address is invalid, and fills for non-lite chains where the repayment address is valid or
372
- // the repayment chain is invalid. We don't check that the origin chain is a valid EVM chain for
373
- // lite chain deposits yet because only EVM chains are supported on Across...for now. This means
374
- // this logic will have to be revisited when we add SVM to log properly.
369
+ const fillRepaymentData = {
370
+ ...fill,
371
+ fromLiteChain: deposit.fromLiteChain,
372
+ quoteBlockNumber: this.hubPoolClient!.latestBlockSearched,
373
+ };
374
+ const { chainToSendRefundTo: repaymentChainId } = getRefundInformationFromFill(
375
+ fillRepaymentData,
376
+ this.hubPoolClient!
377
+ );
378
+ // In order to keep this function sync, we can't call verifyFillRepayment so we'll log any fills where
379
+ // the filler-specified repayment chain and repayment address is not a valid repayment upon
380
+ // first glance. In other words, the repayment address is not a valid EVM address or the repayment chain
381
+ // is not a valid EVM chain. In the case where the repayment address is not a valid EVM address, the dataworker
382
+ // might be able to overwrite the repayment address to the msg.sender on the fill txn, but to keep this
383
+ // functioon synchronous, we can't make that decision now. So this function might log some false positives.
375
384
  if (
376
385
  this.hubPoolClient &&
377
386
  !isSlowFill(fill) &&
378
- (!isValidEvmAddress(fill.relayer) ||
379
- forceDestinationRepayment(
380
- repaymentChainId,
381
- { ...deposit, quoteBlockNumber: this.hubPoolClient!.latestBlockSearched },
382
- this.hubPoolClient
383
- ))
387
+ (!chainIsEvm(repaymentChainId) || !isValidEvmAddress(fill.relayer))
384
388
  ) {
385
389
  groupedFills.unrepayableFills.push(fill);
386
390
  }
@@ -450,6 +454,25 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
450
454
  return `${event.depositId.toString()}-${event.originChainId}`;
451
455
  }
452
456
 
457
+ protected canResolveZeroAddressOutputToken(deposit: DepositWithBlock): boolean {
458
+ if (
459
+ !this.hubPoolClient?.l2TokenHasPoolRebalanceRoute(
460
+ deposit.inputToken,
461
+ deposit.originChainId,
462
+ deposit.quoteBlockNumber
463
+ )
464
+ ) {
465
+ return false;
466
+ } else {
467
+ const l1Token = this.hubPoolClient?.getL1TokenForDeposit(deposit);
468
+ return this.hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
469
+ l1Token,
470
+ deposit.destinationChainId,
471
+ deposit.quoteBlockNumber
472
+ );
473
+ }
474
+ }
475
+
453
476
  /**
454
477
  * A wrapper over the `_update` method that handles errors and logs. This method additionally calls into the
455
478
  * HubPoolClient to update the state of this client with data from the HubPool contract.
@@ -709,6 +732,9 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
709
732
  * @returns The destination token.
710
733
  */
711
734
  protected getDestinationTokenForDeposit(deposit: DepositWithBlock): string {
735
+ if (!this.canResolveZeroAddressOutputToken(deposit)) {
736
+ return ZERO_ADDRESS;
737
+ }
712
738
  // If there is no rate model client return address(0).
713
739
  return this.hubPoolClient?.getL2TokenForDeposit(deposit) ?? ZERO_ADDRESS;
714
740
  }
@@ -100,6 +100,22 @@ export class MockHubPoolClient extends HubPoolClient {
100
100
  this.spokePoolTokens[l1Token][chainId] = l2Token;
101
101
  }
102
102
 
103
+ l2TokenEnabledForL1TokenAtBlock(l1Token: string, destinationChainId: number, hubBlockNumber: number): boolean {
104
+ if (this.spokePoolTokens[l1Token]?.[destinationChainId]) {
105
+ return true;
106
+ } else {
107
+ return super.l2TokenEnabledForL1TokenAtBlock(l1Token, destinationChainId, hubBlockNumber);
108
+ }
109
+ }
110
+ l2TokenHasPoolRebalanceRoute(l2Token: string, chainId: number, hubPoolBlock: number): boolean {
111
+ const l1Token = Object.keys(this.spokePoolTokens).find(
112
+ (l1Token) => this.spokePoolTokens[l1Token]?.[chainId] === l2Token
113
+ );
114
+ if (!l1Token) {
115
+ return super.l2TokenHasPoolRebalanceRoute(l2Token, chainId, hubPoolBlock);
116
+ } else return true;
117
+ }
118
+
103
119
  deleteTokenMapping(l1Token: string, chainId: number) {
104
120
  delete this.spokePoolTokens[l1Token]?.[chainId];
105
121
  }
package/src/constants.ts CHANGED
@@ -66,6 +66,19 @@ export const BRIDGED_USDC_SYMBOLS = [
66
66
  TOKEN_SYMBOLS_MAP.USDzC.symbol,
67
67
  ];
68
68
 
69
+ export const STABLE_COIN_SYMBOLS = [
70
+ ...BRIDGED_USDC_SYMBOLS,
71
+ TOKEN_SYMBOLS_MAP.USDB.symbol,
72
+ TOKEN_SYMBOLS_MAP.USDC.symbol,
73
+ TOKEN_SYMBOLS_MAP.USDT.symbol,
74
+ TOKEN_SYMBOLS_MAP.DAI.symbol,
75
+ TOKEN_SYMBOLS_MAP["TATARA-USDC"].symbol,
76
+ TOKEN_SYMBOLS_MAP["TATARA-USDT"].symbol,
77
+ TOKEN_SYMBOLS_MAP["TATARA-USDS"].symbol,
78
+ TOKEN_SYMBOLS_MAP.GHO.symbol,
79
+ TOKEN_SYMBOLS_MAP.WGHO.symbol,
80
+ ];
81
+
69
82
  export const CUSTOM_GAS_TOKENS = {
70
83
  [CHAIN_IDs.POLYGON]: "MATIC",
71
84
  [CHAIN_IDs.POLYGON_AMOY]: "MATIC",
@@ -1,6 +1,5 @@
1
1
  import { getDeployedAddress, SvmSpokeIdl } from "@across-protocol/contracts";
2
2
  import { getSolanaChainId } from "@across-protocol/contracts/dist/src/svm/web3-v1";
3
- import { BorshEventCoder, utils } from "@coral-xyz/anchor";
4
3
  import web3, {
5
4
  Address,
6
5
  Commitment,
@@ -9,8 +8,9 @@ import web3, {
9
8
  RpcTransport,
10
9
  Signature,
11
10
  } from "@solana/kit";
11
+ import { bs58 } from "../utils";
12
12
  import { EventData, EventName, EventWithData } from "./types";
13
- import { getEventName, parseEventData } from "./utils/events";
13
+ import { decodeEvent } from "./utils/events";
14
14
  import { isDevnet } from "./utils/helpers";
15
15
 
16
16
  // Utility type to extract the return type for the JSON encoding overload. We only care about the overload where the
@@ -185,17 +185,11 @@ export class SvmSpokeEventsClient {
185
185
  this.svmSpokeAddress === ixProgramId &&
186
186
  this.svmSpokeEventAuthority === singleIxAccount
187
187
  ) {
188
- const ixData = utils.bytes.bs58.decode(ix.data);
188
+ const ixData = bs58.decode(ix.data);
189
189
  // Skip the first 8 bytes (assumed header) and encode the rest.
190
- const eventData = utils.bytes.base64.encode(Buffer.from(new Uint8Array(ixData).slice(8)));
191
- const event = new BorshEventCoder(SvmSpokeIdl).decode(eventData);
192
- if (!event?.name) throw new Error("Event name is undefined");
193
- const name = getEventName(event.name);
194
- events.push({
195
- program: this.svmSpokeAddress,
196
- data: parseEventData(event?.data),
197
- name,
198
- });
190
+ const eventData = Buffer.from(ixData.slice(8)).toString("base64");
191
+ const { name, data } = decodeEvent(SvmSpokeIdl, eventData);
192
+ events.push({ program: this.svmSpokeAddress, name, data });
199
193
  }
200
194
  }
201
195
  }
package/src/svm/index.ts CHANGED
@@ -1 +1,3 @@
1
1
  export * from "./eventsClient";
2
+ export * from "./types";
3
+ export * as utils from "./utils";
@@ -1,6 +1,6 @@
1
- import { BN } from "@coral-xyz/anchor";
2
- import web3 from "@solana/kit";
3
- import { EventName, SVMEventNames } from "../types";
1
+ import { BN, BorshEventCoder, Idl } from "@coral-xyz/anchor";
2
+ import { address } from "@solana/kit";
3
+ import { EventName, EventData, SVMEventNames } from "../types";
4
4
 
5
5
  /**
6
6
  * Parses event data from a transaction.
@@ -15,7 +15,7 @@ export function parseEventData(eventData: any): any {
15
15
 
16
16
  if (typeof eventData === "object") {
17
17
  if (eventData.constructor.name === "PublicKey") {
18
- return web3.address(eventData.toString());
18
+ return address(eventData.toString());
19
19
  }
20
20
  if (BN.isBN(eventData)) {
21
21
  return BigInt(eventData.toString());
@@ -30,6 +30,18 @@ export function parseEventData(eventData: any): any {
30
30
  return eventData;
31
31
  }
32
32
 
33
+ /**
34
+ * Decodes a raw event according to a supplied IDL.
35
+ */
36
+ export function decodeEvent(idl: Idl, rawEvent: string): { data: EventData; name: EventName } {
37
+ const event = new BorshEventCoder(idl).decode(rawEvent);
38
+ if (!event) throw new Error(`Malformed rawEvent for IDL ${idl.address}: ${rawEvent}`);
39
+ return {
40
+ name: getEventName(event.name),
41
+ data: parseEventData(event.data),
42
+ };
43
+ }
44
+
33
45
  /**
34
46
  * Converts a snake_case string to camelCase.
35
47
  */
@@ -0,0 +1 @@
1
+ export * from "./events";
@@ -2,7 +2,7 @@ import assert from "assert";
2
2
  import { SpokePoolClient } from "../clients";
3
3
  import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE, UNDEFINED_MESSAGE_HASH, ZERO_BYTES } from "../constants";
4
4
  import { CachingMechanismInterface, Deposit, DepositWithBlock, Fill, RelayData, SlowFillRequest } from "../interfaces";
5
- import { getMessageHash, isUnsafeDepositId } from "./SpokeUtils";
5
+ import { getMessageHash, isUnsafeDepositId, isZeroAddress } from "./SpokeUtils";
6
6
  import { getNetworkName } from "./NetworkUtils";
7
7
  import { bnZero } from "./BigNumberUtils";
8
8
  import { getDepositInCache, getDepositKey, setDepositInCache } from "./CachingUtils";
@@ -208,6 +208,11 @@ export function isZeroValueDeposit(deposit: Pick<Deposit, "inputAmount" | "messa
208
208
  return deposit.inputAmount.eq(0) && isMessageEmpty(deposit.message);
209
209
  }
210
210
 
211
+ export function invalidOutputToken(deposit: Pick<DepositWithBlock, "outputToken">): boolean {
212
+ // If the output token is zero address, then it is invalid.
213
+ return isZeroAddress(deposit.outputToken);
214
+ }
215
+
211
216
  export function isZeroValueFillOrSlowFillRequest(
212
217
  e: Pick<Fill | SlowFillRequest, "inputAmount" | "messageHash">
213
218
  ): boolean {
@@ -109,6 +109,12 @@ export function isBridgedUsdc(tokenSymbol: string): boolean {
109
109
  );
110
110
  }
111
111
 
112
+ export function isStablecoin(tokenSymbol: string): boolean {
113
+ return constants.STABLE_COIN_SYMBOLS.some(
114
+ (stablecoinSymbol) => stablecoinSymbol.toLowerCase() === tokenSymbol.toLowerCase()
115
+ );
116
+ }
117
+
112
118
  export function getTokenInfo(l2TokenAddress: string, chainId: number): L1Token {
113
119
  // @dev This might give false positives if tokens on different networks have the same address. I'm not sure how
114
120
  // to get around this...
@@ -1,4 +1,5 @@
1
1
  import Decimal from "decimal.js";
2
+ import bs58 from "bs58";
2
3
  import { ethers } from "ethers";
3
4
  import { BigNumber, BigNumberish, BN, formatUnits, parseUnits, toBN } from "./BigNumberUtils";
4
5
  import { ConvertDecimals } from "./FormattingUtils";
@@ -8,6 +9,7 @@ export const AddressZero = ethers.constants.AddressZero;
8
9
  export const MAX_BIG_INT = BigNumber.from(Number.MAX_SAFE_INTEGER.toString());
9
10
 
10
11
  export const { keccak256 } = ethers.utils;
12
+ export { bs58 };
11
13
 
12
14
  /**
13
15
  * toBNWei.
@@ -1,4 +0,0 @@
1
- import { Logger } from "../../utils";
2
- export { Logger } from "../../utils";
3
- export declare function logError(name: string, error: unknown, logger?: Logger): Promise<string[]>;
4
- export declare function fetch(name: string, url: string, timeout?: number, retries?: number): Promise<unknown>;