@bananapus/nana-sdk-core 3.5.1 → 4.0.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 (292) hide show
  1. package/bin/wallet-check.mjs +489 -0
  2. package/dist/cjs/actions/suckerPairs.d.ts +5 -3
  3. package/dist/cjs/actions/suckerPairs.d.ts.map +1 -1
  4. package/dist/cjs/actions/suckerPairs.js +6 -26
  5. package/dist/cjs/actions/suckerPairs.js.map +1 -1
  6. package/dist/cjs/pause.d.ts +2 -0
  7. package/dist/cjs/pause.d.ts.map +1 -1
  8. package/dist/cjs/pause.js +17 -0
  9. package/dist/cjs/pause.js.map +1 -1
  10. package/dist/cjs/review/confirm.d.ts +52 -0
  11. package/dist/cjs/review/confirm.d.ts.map +1 -0
  12. package/dist/cjs/review/confirm.js +88 -0
  13. package/dist/cjs/review/confirm.js.map +1 -0
  14. package/dist/cjs/review/contractWrite.d.ts +5 -3
  15. package/dist/cjs/review/contractWrite.d.ts.map +1 -1
  16. package/dist/cjs/review/contractWrite.js +8 -7
  17. package/dist/cjs/review/contractWrite.js.map +1 -1
  18. package/dist/cjs/review/decode.d.ts +19 -1
  19. package/dist/cjs/review/decode.d.ts.map +1 -1
  20. package/dist/cjs/review/decode.js +30 -11
  21. package/dist/cjs/review/decode.js.map +1 -1
  22. package/dist/cjs/review/decodeCall.d.ts +21 -0
  23. package/dist/cjs/review/decodeCall.d.ts.map +1 -0
  24. package/dist/cjs/review/decodeCall.js +183 -0
  25. package/dist/cjs/review/decodeCall.js.map +1 -0
  26. package/dist/cjs/review/heldWrite.d.ts.map +1 -1
  27. package/dist/cjs/review/heldWrite.js +5 -4
  28. package/dist/cjs/review/heldWrite.js.map +1 -1
  29. package/dist/cjs/review/index.d.ts +11 -0
  30. package/dist/cjs/review/index.d.ts.map +1 -1
  31. package/dist/cjs/review/index.js +24 -0
  32. package/dist/cjs/review/index.js.map +1 -1
  33. package/dist/cjs/review/intents.d.ts +10 -0
  34. package/dist/cjs/review/intents.d.ts.map +1 -1
  35. package/dist/cjs/review/intents.js +13 -0
  36. package/dist/cjs/review/intents.js.map +1 -1
  37. package/dist/cjs/review/lineError.d.ts +34 -0
  38. package/dist/cjs/review/lineError.d.ts.map +1 -0
  39. package/dist/cjs/review/lineError.js +42 -0
  40. package/dist/cjs/review/lineError.js.map +1 -0
  41. package/dist/cjs/review/messages.d.ts +15 -3
  42. package/dist/cjs/review/messages.d.ts.map +1 -1
  43. package/dist/cjs/review/messages.js +16 -4
  44. package/dist/cjs/review/messages.js.map +1 -1
  45. package/dist/cjs/review/model.d.ts +149 -0
  46. package/dist/cjs/review/model.d.ts.map +1 -0
  47. package/dist/cjs/review/model.js +750 -0
  48. package/dist/cjs/review/model.js.map +1 -0
  49. package/dist/cjs/review/plan.d.ts +63 -0
  50. package/dist/cjs/review/plan.d.ts.map +1 -0
  51. package/dist/cjs/review/plan.js +149 -0
  52. package/dist/cjs/review/plan.js.map +1 -0
  53. package/dist/cjs/review/react/index.d.ts +1 -0
  54. package/dist/cjs/review/react/index.d.ts.map +1 -1
  55. package/dist/cjs/review/react/index.js +1 -0
  56. package/dist/cjs/review/react/index.js.map +1 -1
  57. package/dist/cjs/review/react/useReviewedRun.d.ts +96 -0
  58. package/dist/cjs/review/react/useReviewedRun.d.ts.map +1 -0
  59. package/dist/cjs/review/react/useReviewedRun.js +261 -0
  60. package/dist/cjs/review/react/useReviewedRun.js.map +1 -0
  61. package/dist/cjs/review/receipt.d.ts +4 -1
  62. package/dist/cjs/review/receipt.d.ts.map +1 -1
  63. package/dist/cjs/review/receipt.js +7 -3
  64. package/dist/cjs/review/receipt.js.map +1 -1
  65. package/dist/cjs/review/relayr.d.ts +13 -4
  66. package/dist/cjs/review/relayr.d.ts.map +1 -1
  67. package/dist/cjs/review/relayr.js +51 -8
  68. package/dist/cjs/review/relayr.js.map +1 -1
  69. package/dist/cjs/review/relayrSend.d.ts +135 -0
  70. package/dist/cjs/review/relayrSend.d.ts.map +1 -0
  71. package/dist/cjs/review/relayrSend.js +943 -0
  72. package/dist/cjs/review/relayrSend.js.map +1 -0
  73. package/dist/cjs/review/reviewQueue.d.ts +10 -9
  74. package/dist/cjs/review/reviewQueue.d.ts.map +1 -1
  75. package/dist/cjs/review/reviewQueue.js +19 -29
  76. package/dist/cjs/review/reviewQueue.js.map +1 -1
  77. package/dist/cjs/review/reviewRequests.d.ts +86 -0
  78. package/dist/cjs/review/reviewRequests.d.ts.map +1 -0
  79. package/dist/cjs/review/reviewRequests.js +266 -0
  80. package/dist/cjs/review/reviewRequests.js.map +1 -0
  81. package/dist/cjs/review/safeRelayr.d.ts +3 -1
  82. package/dist/cjs/review/safeRelayr.d.ts.map +1 -1
  83. package/dist/cjs/review/safeRelayr.js +5 -1
  84. package/dist/cjs/review/safeRelayr.js.map +1 -1
  85. package/dist/cjs/review/safeSend.d.ts +69 -0
  86. package/dist/cjs/review/safeSend.d.ts.map +1 -0
  87. package/dist/cjs/review/safeSend.js +308 -0
  88. package/dist/cjs/review/safeSend.js.map +1 -0
  89. package/dist/cjs/review/send.d.ts +241 -0
  90. package/dist/cjs/review/send.d.ts.map +1 -0
  91. package/dist/cjs/review/send.js +1017 -0
  92. package/dist/cjs/review/send.js.map +1 -0
  93. package/dist/cjs/review/sequence.d.ts +77 -0
  94. package/dist/cjs/review/sequence.d.ts.map +1 -0
  95. package/dist/cjs/review/sequence.js +232 -0
  96. package/dist/cjs/review/sequence.js.map +1 -0
  97. package/dist/cjs/review/sign.d.ts +109 -0
  98. package/dist/cjs/review/sign.d.ts.map +1 -0
  99. package/dist/cjs/review/sign.js +374 -0
  100. package/dist/cjs/review/sign.js.map +1 -0
  101. package/dist/cjs/review/simulation.d.ts +41 -2
  102. package/dist/cjs/review/simulation.d.ts.map +1 -1
  103. package/dist/cjs/review/simulation.js +99 -29
  104. package/dist/cjs/review/simulation.js.map +1 -1
  105. package/dist/cjs/review/transaction-message.d.ts +13 -0
  106. package/dist/cjs/review/transaction-message.d.ts.map +1 -1
  107. package/dist/cjs/review/transaction-message.js +25 -0
  108. package/dist/cjs/review/transaction-message.js.map +1 -1
  109. package/dist/cjs/review/transactionReview.d.ts +27 -19
  110. package/dist/cjs/review/transactionReview.d.ts.map +1 -1
  111. package/dist/cjs/review/transactionReview.js +22 -63
  112. package/dist/cjs/review/transactionReview.js.map +1 -1
  113. package/dist/cjs/review/wallet.d.ts +82 -0
  114. package/dist/cjs/review/wallet.d.ts.map +1 -0
  115. package/dist/cjs/review/wallet.js +157 -0
  116. package/dist/cjs/review/wallet.js.map +1 -0
  117. package/dist/cjs/review/writeHold.d.ts +100 -12
  118. package/dist/cjs/review/writeHold.d.ts.map +1 -1
  119. package/dist/cjs/review/writeHold.js +61 -13
  120. package/dist/cjs/review/writeHold.js.map +1 -1
  121. package/dist/cjs/review/writeRecovery.d.ts +6 -1
  122. package/dist/cjs/review/writeRecovery.d.ts.map +1 -1
  123. package/dist/cjs/review/writeRecovery.js +108 -40
  124. package/dist/cjs/review/writeRecovery.js.map +1 -1
  125. package/dist/cjs/safeService.d.ts +27 -4
  126. package/dist/cjs/safeService.d.ts.map +1 -1
  127. package/dist/cjs/safeService.js +120 -3
  128. package/dist/cjs/safeService.js.map +1 -1
  129. package/dist/cjs/testing.d.ts +24 -0
  130. package/dist/cjs/testing.d.ts.map +1 -1
  131. package/dist/cjs/testing.js +26 -1
  132. package/dist/cjs/testing.js.map +1 -1
  133. package/dist/cjs/v6/defifa.d.ts.map +1 -1
  134. package/dist/cjs/v6/defifa.js +24 -23
  135. package/dist/cjs/v6/defifa.js.map +1 -1
  136. package/dist/cjs/v6/executionErrors.d.ts.map +1 -1
  137. package/dist/cjs/v6/executionErrors.js +3 -2
  138. package/dist/cjs/v6/executionErrors.js.map +1 -1
  139. package/dist/cjs/v6/pay.d.ts +25 -7
  140. package/dist/cjs/v6/pay.d.ts.map +1 -1
  141. package/dist/cjs/v6/pay.js +168 -40
  142. package/dist/cjs/v6/pay.js.map +1 -1
  143. package/dist/cjs/v6/suckers.d.ts +30 -0
  144. package/dist/cjs/v6/suckers.d.ts.map +1 -1
  145. package/dist/cjs/v6/suckers.js +69 -5
  146. package/dist/cjs/v6/suckers.js.map +1 -1
  147. package/dist/esm/actions/suckerPairs.d.ts +5 -3
  148. package/dist/esm/actions/suckerPairs.d.ts.map +1 -1
  149. package/dist/esm/actions/suckerPairs.js +7 -27
  150. package/dist/esm/actions/suckerPairs.js.map +1 -1
  151. package/dist/esm/pause.d.ts +2 -0
  152. package/dist/esm/pause.d.ts.map +1 -1
  153. package/dist/esm/pause.js +16 -0
  154. package/dist/esm/pause.js.map +1 -1
  155. package/dist/esm/review/confirm.d.ts +52 -0
  156. package/dist/esm/review/confirm.d.ts.map +1 -0
  157. package/dist/esm/review/confirm.js +83 -0
  158. package/dist/esm/review/confirm.js.map +1 -0
  159. package/dist/esm/review/contractWrite.d.ts +5 -3
  160. package/dist/esm/review/contractWrite.d.ts.map +1 -1
  161. package/dist/esm/review/contractWrite.js +8 -7
  162. package/dist/esm/review/contractWrite.js.map +1 -1
  163. package/dist/esm/review/decode.d.ts +19 -1
  164. package/dist/esm/review/decode.d.ts.map +1 -1
  165. package/dist/esm/review/decode.js +29 -14
  166. package/dist/esm/review/decode.js.map +1 -1
  167. package/dist/esm/review/decodeCall.d.ts +21 -0
  168. package/dist/esm/review/decodeCall.d.ts.map +1 -0
  169. package/dist/esm/review/decodeCall.js +180 -0
  170. package/dist/esm/review/decodeCall.js.map +1 -0
  171. package/dist/esm/review/heldWrite.d.ts.map +1 -1
  172. package/dist/esm/review/heldWrite.js +5 -4
  173. package/dist/esm/review/heldWrite.js.map +1 -1
  174. package/dist/esm/review/index.d.ts +11 -0
  175. package/dist/esm/review/index.d.ts.map +1 -1
  176. package/dist/esm/review/index.js +11 -0
  177. package/dist/esm/review/index.js.map +1 -1
  178. package/dist/esm/review/intents.d.ts +10 -0
  179. package/dist/esm/review/intents.d.ts.map +1 -1
  180. package/dist/esm/review/intents.js +12 -0
  181. package/dist/esm/review/intents.js.map +1 -1
  182. package/dist/esm/review/lineError.d.ts +34 -0
  183. package/dist/esm/review/lineError.d.ts.map +1 -0
  184. package/dist/esm/review/lineError.js +36 -0
  185. package/dist/esm/review/lineError.js.map +1 -0
  186. package/dist/esm/review/messages.d.ts +15 -3
  187. package/dist/esm/review/messages.d.ts.map +1 -1
  188. package/dist/esm/review/messages.js +15 -3
  189. package/dist/esm/review/messages.js.map +1 -1
  190. package/dist/esm/review/model.d.ts +149 -0
  191. package/dist/esm/review/model.d.ts.map +1 -0
  192. package/dist/esm/review/model.js +746 -0
  193. package/dist/esm/review/model.js.map +1 -0
  194. package/dist/esm/review/plan.d.ts +63 -0
  195. package/dist/esm/review/plan.d.ts.map +1 -0
  196. package/dist/esm/review/plan.js +141 -0
  197. package/dist/esm/review/plan.js.map +1 -0
  198. package/dist/esm/review/react/index.d.ts +1 -0
  199. package/dist/esm/review/react/index.d.ts.map +1 -1
  200. package/dist/esm/review/react/index.js +1 -0
  201. package/dist/esm/review/react/index.js.map +1 -1
  202. package/dist/esm/review/react/useReviewedRun.d.ts +96 -0
  203. package/dist/esm/review/react/useReviewedRun.d.ts.map +1 -0
  204. package/dist/esm/review/react/useReviewedRun.js +257 -0
  205. package/dist/esm/review/react/useReviewedRun.js.map +1 -0
  206. package/dist/esm/review/receipt.d.ts +4 -1
  207. package/dist/esm/review/receipt.d.ts.map +1 -1
  208. package/dist/esm/review/receipt.js +6 -2
  209. package/dist/esm/review/receipt.js.map +1 -1
  210. package/dist/esm/review/relayr.d.ts +13 -4
  211. package/dist/esm/review/relayr.d.ts.map +1 -1
  212. package/dist/esm/review/relayr.js +50 -8
  213. package/dist/esm/review/relayr.js.map +1 -1
  214. package/dist/esm/review/relayrSend.d.ts +135 -0
  215. package/dist/esm/review/relayrSend.d.ts.map +1 -0
  216. package/dist/esm/review/relayrSend.js +934 -0
  217. package/dist/esm/review/relayrSend.js.map +1 -0
  218. package/dist/esm/review/reviewQueue.d.ts +10 -9
  219. package/dist/esm/review/reviewQueue.d.ts.map +1 -1
  220. package/dist/esm/review/reviewQueue.js +17 -26
  221. package/dist/esm/review/reviewQueue.js.map +1 -1
  222. package/dist/esm/review/reviewRequests.d.ts +86 -0
  223. package/dist/esm/review/reviewRequests.d.ts.map +1 -0
  224. package/dist/esm/review/reviewRequests.js +263 -0
  225. package/dist/esm/review/reviewRequests.js.map +1 -0
  226. package/dist/esm/review/safeRelayr.d.ts +3 -1
  227. package/dist/esm/review/safeRelayr.d.ts.map +1 -1
  228. package/dist/esm/review/safeRelayr.js +5 -1
  229. package/dist/esm/review/safeRelayr.js.map +1 -1
  230. package/dist/esm/review/safeSend.d.ts +69 -0
  231. package/dist/esm/review/safeSend.d.ts.map +1 -0
  232. package/dist/esm/review/safeSend.js +303 -0
  233. package/dist/esm/review/safeSend.js.map +1 -0
  234. package/dist/esm/review/send.d.ts +241 -0
  235. package/dist/esm/review/send.d.ts.map +1 -0
  236. package/dist/esm/review/send.js +1011 -0
  237. package/dist/esm/review/send.js.map +1 -0
  238. package/dist/esm/review/sequence.d.ts +77 -0
  239. package/dist/esm/review/sequence.d.ts.map +1 -0
  240. package/dist/esm/review/sequence.js +223 -0
  241. package/dist/esm/review/sequence.js.map +1 -0
  242. package/dist/esm/review/sign.d.ts +109 -0
  243. package/dist/esm/review/sign.d.ts.map +1 -0
  244. package/dist/esm/review/sign.js +368 -0
  245. package/dist/esm/review/sign.js.map +1 -0
  246. package/dist/esm/review/simulation.d.ts +41 -2
  247. package/dist/esm/review/simulation.d.ts.map +1 -1
  248. package/dist/esm/review/simulation.js +95 -29
  249. package/dist/esm/review/simulation.js.map +1 -1
  250. package/dist/esm/review/transaction-message.d.ts +13 -0
  251. package/dist/esm/review/transaction-message.d.ts.map +1 -1
  252. package/dist/esm/review/transaction-message.js +23 -0
  253. package/dist/esm/review/transaction-message.js.map +1 -1
  254. package/dist/esm/review/transactionReview.d.ts +27 -19
  255. package/dist/esm/review/transactionReview.d.ts.map +1 -1
  256. package/dist/esm/review/transactionReview.js +19 -56
  257. package/dist/esm/review/transactionReview.js.map +1 -1
  258. package/dist/esm/review/wallet.d.ts +82 -0
  259. package/dist/esm/review/wallet.d.ts.map +1 -0
  260. package/dist/esm/review/wallet.js +150 -0
  261. package/dist/esm/review/wallet.js.map +1 -0
  262. package/dist/esm/review/writeHold.d.ts +100 -12
  263. package/dist/esm/review/writeHold.d.ts.map +1 -1
  264. package/dist/esm/review/writeHold.js +59 -13
  265. package/dist/esm/review/writeHold.js.map +1 -1
  266. package/dist/esm/review/writeRecovery.d.ts +6 -1
  267. package/dist/esm/review/writeRecovery.d.ts.map +1 -1
  268. package/dist/esm/review/writeRecovery.js +111 -43
  269. package/dist/esm/review/writeRecovery.js.map +1 -1
  270. package/dist/esm/safeService.d.ts +27 -4
  271. package/dist/esm/safeService.d.ts.map +1 -1
  272. package/dist/esm/safeService.js +119 -4
  273. package/dist/esm/safeService.js.map +1 -1
  274. package/dist/esm/testing.d.ts +24 -0
  275. package/dist/esm/testing.d.ts.map +1 -1
  276. package/dist/esm/testing.js +23 -1
  277. package/dist/esm/testing.js.map +1 -1
  278. package/dist/esm/v6/defifa.d.ts.map +1 -1
  279. package/dist/esm/v6/defifa.js +24 -23
  280. package/dist/esm/v6/defifa.js.map +1 -1
  281. package/dist/esm/v6/executionErrors.d.ts.map +1 -1
  282. package/dist/esm/v6/executionErrors.js +3 -2
  283. package/dist/esm/v6/executionErrors.js.map +1 -1
  284. package/dist/esm/v6/pay.d.ts +25 -7
  285. package/dist/esm/v6/pay.d.ts.map +1 -1
  286. package/dist/esm/v6/pay.js +166 -39
  287. package/dist/esm/v6/pay.js.map +1 -1
  288. package/dist/esm/v6/suckers.d.ts +30 -0
  289. package/dist/esm/v6/suckers.d.ts.map +1 -1
  290. package/dist/esm/v6/suckers.js +67 -5
  291. package/dist/esm/v6/suckers.js.map +1 -1
  292. package/package.json +11 -2
@@ -0,0 +1,1017 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SAFE_BATCH_UNFOLLOWABLE_LINE = exports.SAFE_BATCH_UNSUPPORTED_LINE = void 0;
4
+ exports.reviewPlan = reviewPlan;
5
+ exports.sendReviewed = sendReviewed;
6
+ exports.resumeReviewed = resumeReviewed;
7
+ const lineError_js_1 = require("./lineError.js");
8
+ const viem_1 = require("viem");
9
+ const confirm_js_1 = require("./confirm.js");
10
+ const contractWrite_js_1 = require("./contractWrite.js");
11
+ const constants_js_1 = require("../constants.js");
12
+ const gas_js_1 = require("./gas.js");
13
+ const safeService_js_1 = require("../safeService.js");
14
+ const messages_js_1 = require("./messages.js");
15
+ const model_js_1 = require("./model.js");
16
+ const plan_js_1 = require("./plan.js");
17
+ const receipt_js_1 = require("./receipt.js");
18
+ const relayr_js_1 = require("./relayr.js");
19
+ const relayrSend_js_1 = require("./relayrSend.js");
20
+ const sequence_js_1 = require("./sequence.js");
21
+ const sign_js_1 = require("./sign.js");
22
+ const simulation_js_1 = require("./simulation.js");
23
+ const wallet_js_1 = require("./wallet.js");
24
+ const writeHold_js_1 = require("./writeHold.js");
25
+ const writeRecovery_js_1 = require("./writeRecovery.js");
26
+ const CHANGED = "Transaction data changed after review. Nothing was sent; review it again.";
27
+ const MISMATCHED = "This call's data does not match its arguments. Nothing was sent.";
28
+ const BAD_GAS = "This call's gas limit is not valid. Nothing was sent.";
29
+ const BAD_ADOPT = "This call's adoption rule is not valid. Nothing was sent.";
30
+ const ACCOUNT_CHANGED = messages_js_1.REVIEWED_ACCOUNT_CHANGED_LINE;
31
+ const NOT_SAVED = "This action's progress could not be saved. Nothing was sent; review it again.";
32
+ exports.SAFE_BATCH_UNSUPPORTED_LINE = "This Safe connection can't propose these calls as one transaction. Nothing was sent.";
33
+ exports.SAFE_BATCH_UNFOLLOWABLE_LINE = "Your Safe took this batch, but it can't be followed from here. Check your Safe before sending again.";
34
+ const SIMULATION_UNAVAILABLE = "Simulation unavailable. This could not be checked before you confirm; it is checked again before your wallet opens.";
35
+ /**
36
+ * `call` as it encodes now, each field read once, so a getter cannot show one
37
+ * value and send another. Refuses `data` that is not its arguments' encoding.
38
+ */
39
+ function exact(call) {
40
+ const { chainId, to, data, value, gas, abi, functionName, args } = call;
41
+ const { label, contractName } = call;
42
+ const ignoring = call.adoptIgnoringArgs;
43
+ if (gas !== undefined && (typeof gas !== "bigint" || gas <= 0n)) {
44
+ throw new lineError_js_1.ReviewLineError(BAD_GAS);
45
+ }
46
+ if (ignoring !== undefined && !Array.isArray(ignoring)) {
47
+ throw new lineError_js_1.ReviewLineError(BAD_ADOPT);
48
+ }
49
+ const adoptIgnoringArgs = ignoring && Object.freeze([...ignoring]);
50
+ if (adoptIgnoringArgs &&
51
+ (!abi ||
52
+ !functionName ||
53
+ new Set(adoptIgnoringArgs).size !== adoptIgnoringArgs.length ||
54
+ adoptIgnoringArgs.some((index) => !Number.isInteger(index) || index < 0 || index >= (args?.length ?? 0)))) {
55
+ throw new lineError_js_1.ReviewLineError(BAD_ADOPT);
56
+ }
57
+ if (abi && functionName) {
58
+ const encoded = (0, viem_1.encodeFunctionData)({
59
+ abi,
60
+ functionName,
61
+ args: args ?? [],
62
+ });
63
+ if (encoded.toLowerCase() !== data.toLowerCase()) {
64
+ throw new lineError_js_1.ReviewLineError(MISMATCHED);
65
+ }
66
+ }
67
+ return {
68
+ sent: Object.freeze({
69
+ chainId,
70
+ to,
71
+ data,
72
+ value: value ?? 0n,
73
+ ...(gas === undefined ? {} : { gas }),
74
+ }),
75
+ shown: {
76
+ abi,
77
+ functionName,
78
+ args,
79
+ label,
80
+ contractName,
81
+ ...(adoptIgnoringArgs ? { adoptIgnoringArgs } : {}),
82
+ },
83
+ };
84
+ }
85
+ /** What a Safe app may adopt a queued proposal by, as reviewed. */
86
+ function adopting({ abi, adoptIgnoringArgs, }) {
87
+ return abi && adoptIgnoringArgs
88
+ ? { abi, ignoring: adoptIgnoringArgs }
89
+ : undefined;
90
+ }
91
+ function sameCall(a, b) {
92
+ return (a.chainId === b.chainId &&
93
+ a.to.toLowerCase() === b.to.toLowerCase() &&
94
+ a.data.toLowerCase() === b.data.toLowerCase() &&
95
+ a.value === b.value &&
96
+ a.gas === b.gas);
97
+ }
98
+ /** The chain's name in a line the person reads, never only its id when core knows it. */
99
+ function chainNameOf(client, chainId) {
100
+ return (client.chain?.name ??
101
+ constants_js_1.JB_CHAINS[chainId]?.name ??
102
+ `chain ${chainId}`);
103
+ }
104
+ /** A call as the review shows it, from `from` (none: Relayr's own account runs it). */
105
+ function shownCall({ sent: { gas, ...sent }, shown }, from, safe) {
106
+ return {
107
+ ...sent,
108
+ ...(from ? { from } : {}),
109
+ // A Safe app signs safeTxGas 0, never the app's gas.
110
+ ...(safe ? { safeTxGas: 0n } : gas === undefined ? {} : { gas }),
111
+ ...(shown.abi ? { abi: shown.abi } : {}),
112
+ ...(shown.functionName ? { functionName: shown.functionName } : {}),
113
+ ...(shown.args ? { args: shown.args } : {}),
114
+ ...(shown.label
115
+ ? { label: shown.label }
116
+ : from
117
+ ? {}
118
+ : { label: "Sent by Relayr" }),
119
+ ...(shown.contractName ? { contractName: shown.contractName } : {}),
120
+ ...(shown.adoptIgnoringArgs
121
+ ? { adoptIgnoringArgs: shown.adoptIgnoringArgs }
122
+ : {}),
123
+ };
124
+ }
125
+ function labelOf(call, index) {
126
+ return call.label ?? call.functionName ?? `Step ${index + 1}`;
127
+ }
128
+ /**
129
+ * The latest block, or `minimum` while the node has not reached it: never
130
+ * state from before a prerequisite took effect, never stale state after it.
131
+ */
132
+ async function notBefore(client, minimum) {
133
+ const head = await client.getBlockNumber();
134
+ return head > minimum ? head : minimum;
135
+ }
136
+ /**
137
+ * The review-time simulation of every call, per chain: a sequence through
138
+ * {@link simulateCallSequence}, a lone call through
139
+ * {@link simulateStateChangingTransaction}. A lone call that needs a step on
140
+ * another chain is left to the simulation before its send. It never rejects:
141
+ * a revert is `failed`, anything else `unavailable`.
142
+ */
143
+ async function reviewSimulation(fromOf, calls, sent, blocks, clientFor,
144
+ /** Steps whose value someone else pays: the sender's balance is topped up by it. */
145
+ funded) {
146
+ const chains = [...new Set(sent.map((call) => call.chainId))];
147
+ const perChain = chains.map(async (chainId) => {
148
+ const steps = sent.flatMap((call, index) => call.chainId === chainId ? [index] : []);
149
+ const paid = steps.reduce((sum, index) => sum + (funded?.(index) ? (sent[index].value ?? 0n) : 0n), 0n);
150
+ let chainName = `chain ${chainId}`;
151
+ try {
152
+ const client = clientFor(chainId);
153
+ chainName = chainNameOf(client, chainId);
154
+ const from = fromOf(steps[0]);
155
+ const balance = paid
156
+ ? { balance: (await client.getBalance({ address: from })) + paid }
157
+ : {};
158
+ if (steps.length > 1) {
159
+ // A sequence runs from one sender; mixed senders are checked before each send.
160
+ if (steps.some((index) => fromOf(index) !== from))
161
+ return { status: "unavailable", message: SIMULATION_UNAVAILABLE };
162
+ await (0, simulation_js_1.simulateCallSequence)(client, {
163
+ from,
164
+ chainName,
165
+ calls: steps.map((index) => ({
166
+ to: sent[index].to,
167
+ data: sent[index].data,
168
+ value: sent[index].value,
169
+ gas: sent[index].gas,
170
+ label: labelOf(calls[index], index),
171
+ dependsOnPrior: calls[index].dependsOnPrior,
172
+ funded: funded?.(index) === true,
173
+ })),
174
+ ...balance,
175
+ });
176
+ }
177
+ else if (!(calls[steps[0]].dependsOnPrior && steps[0] > 0)) {
178
+ const minimum = blocks[steps[0]];
179
+ await (0, simulation_js_1.simulateStateChangingTransaction)(client, {
180
+ from,
181
+ to: sent[steps[0]].to,
182
+ data: sent[steps[0]].data,
183
+ value: sent[steps[0]].value,
184
+ gas: sent[steps[0]].gas,
185
+ ...balance,
186
+ ...(minimum === undefined
187
+ ? {}
188
+ : { blockNumber: await notBefore(client, minimum) }),
189
+ });
190
+ }
191
+ return { status: "passed" };
192
+ }
193
+ catch (error) {
194
+ if (!(0, simulation_js_1.isRevert)(error))
195
+ return { status: "unavailable", message: SIMULATION_UNAVAILABLE };
196
+ const reason = (0, simulation_js_1.revertReason)(error);
197
+ return {
198
+ status: "failed",
199
+ // A sequence's failure is the SDK's line naming its call.
200
+ message: error instanceof lineError_js_1.ReviewLineError
201
+ ? error.message
202
+ : `${labelOf(calls[steps[0]], steps[0])} cannot run on ${chainName}: ${reason ?? simulation_js_1.REVERT_UNKNOWN_LINE}`,
203
+ explained: reason !== null,
204
+ };
205
+ }
206
+ });
207
+ // A revert on one chain is reported at once, whatever the others still wait on.
208
+ const failed = new Promise((resolve) => {
209
+ for (const result of perChain) {
210
+ void result.then((settled) => {
211
+ if (settled.status === "failed")
212
+ resolve(settled);
213
+ });
214
+ }
215
+ });
216
+ const all = Promise.all(perChain).then((results) => results.find((result) => result.status === "unavailable") ?? {
217
+ status: "passed",
218
+ });
219
+ return Promise.race([failed, all]);
220
+ }
221
+ function simulationWarning(simulation) {
222
+ if (simulation.status === "failed") {
223
+ return { code: "simulation-failed", text: simulation.message };
224
+ }
225
+ if (simulation.status === "unavailable") {
226
+ return { code: "simulation-unavailable", text: SIMULATION_UNAVAILABLE };
227
+ }
228
+ return undefined;
229
+ }
230
+ /** One prompt over `model`, with the review-time simulation of `run` as its warning. */
231
+ async function promptSimulated(model, fromOf, calls, run, blocks, clientFor, signal, funded) {
232
+ const simulation = reviewSimulation(fromOf, calls, run, blocks, clientFor, funded);
233
+ const enriched = Promise.all([
234
+ (0, model_js_1.enrichReviewModel)(model, clientFor).catch(() => model),
235
+ simulation,
236
+ ]).then(([read, simulated]) => {
237
+ const warning = simulationWarning(simulated);
238
+ return warning ? { ...read, warnings: [...read.warnings, warning] } : read;
239
+ });
240
+ await (0, confirm_js_1.promptReview)(model, simulation, enriched, signal);
241
+ }
242
+ /**
243
+ * One review of calls an app then sends one by one, across chains and
244
+ * paths: core's prompt over exactly `action.calls`, simulated as their
245
+ * senders, and nothing else. The plan it returns lets {@link sendReviewed},
246
+ * `signReviewed`, `proposeReviewedSafeTransaction`, `payRelayrReviewed` and
247
+ * `runRelayrReviewed` skip their prompt for a call identical to one of its
248
+ * calls (account, chain, sender, target, data, value and gas: a call's
249
+ * exact gas limit, or a forward request's signed gas, is set before the
250
+ * review), each spent once, for {@link REVIEWED_PLAN_TTL_MS} after confirm.
251
+ * Anything else is refused.
252
+ */
253
+ async function reviewPlan(action, deps) {
254
+ const { account } = action;
255
+ const calls = [...action.calls];
256
+ if (!calls.length)
257
+ throw new lineError_js_1.ReviewLineError("There is no transaction to review.");
258
+ const froms = calls.map(({ from }) => from ?? account);
259
+ const snapshots = calls.map(exact);
260
+ const roundOf = calls.map(({ relayrRound }) => relayrRound);
261
+ const fees = new Map();
262
+ for (const round of new Set(roundOf)) {
263
+ if (round === undefined)
264
+ continue;
265
+ const members = snapshots.flatMap(({ sent }, index) => roundOf[index] === round
266
+ ? [{ ...sent, raw: froms[index] === "relayr" }]
267
+ : []);
268
+ if (members.some(({ raw, gas }) => !raw && gas === undefined))
269
+ throw new lineError_js_1.ReviewLineError("A relayed call needs the gas limit its request signs.");
270
+ const chainId = action.relayrRounds?.[round]?.paymentChainId ?? members[0].chainId;
271
+ if (!(0, relayr_js_1.relayrPaymentChains)(members.map((member) => member.chainId)).includes(chainId))
272
+ throw new lineError_js_1.ReviewLineError("Choose a Relayr funding chain in the same network family as these destinations.");
273
+ fees.set(round, {
274
+ chainId,
275
+ cap: await (0, relayrSend_js_1.relayrFeeCap)(members.map(({ chainId, to, data, value, gas, raw }) => ({
276
+ chainId,
277
+ target: to,
278
+ data,
279
+ value,
280
+ gas: gas ?? 0n,
281
+ raw,
282
+ })), account, chainId, deps.clientFor),
283
+ });
284
+ }
285
+ const feeRows = [...fees.values()].map(({ chainId, cap }, index) => ({
286
+ label: fees.size > 1 ? `Network fee ${index + 1}` : "Network fee",
287
+ value: `Up to ${(0, viem_1.formatEther)(cap)} ETH on ${chainNameOf(deps.clientFor(chainId), chainId)}`,
288
+ strong: true,
289
+ }));
290
+ const context = [...(action.context ?? []), ...feeRows];
291
+ const model = (0, model_js_1.buildReviewModel)({
292
+ calls: snapshots.map((snapshot, index) => shownCall(snapshot, froms[index] === "relayr" ? undefined : froms[index], false)),
293
+ ...(action.title ? { title: action.title } : {}),
294
+ ...(action.description ? { description: action.description } : {}),
295
+ ...(action.confirmLabel ? { confirmLabel: action.confirmLabel } : {}),
296
+ ...(context.length ? { context } : {}),
297
+ });
298
+ const relayr = (0, plan_js_1.relayrStandInSender)();
299
+ await promptSimulated(model, (index) => (froms[index] === "relayr" ? relayr : froms[index]), calls, snapshots.map(({ sent }) => sent), calls.map((call) => call.simulationBlockNumber), deps.clientFor, action.signal,
300
+ // Relayr pays a relayed call's value: its signer needs none of its own.
301
+ (index) => roundOf[index] !== undefined && froms[index] !== "relayr");
302
+ return (0, plan_js_1.mintPlan)(account, snapshots.map(({ sent: { chainId, to, data, value, gas } }, index) => ({
303
+ chainId,
304
+ from: froms[index],
305
+ to,
306
+ data,
307
+ value,
308
+ // Relayr's own call: its gas bounds only the fee.
309
+ ...(gas === undefined || froms[index] === "relayr" ? {} : { gas }),
310
+ ...(roundOf[index] === undefined ? {} : { relayrRound: roundOf[index] }),
311
+ })), fees);
312
+ }
313
+ /**
314
+ * Sends one reviewed action: one call, or a sequence (approve then pay, say),
315
+ * reviewed once and sent without asking again.
316
+ *
317
+ * Core builds one model over every call and asks the registered review
318
+ * handler once ({@link registerReviewHandler}); nothing is sent unless the
319
+ * handler returns the confirmation core minted for that model. The
320
+ * review-time simulation runs while the review is open, and confirm never
321
+ * waits on it. Then, for each call in order: `reverify`, a check that the call
322
+ * still encodes to exactly the reviewed bytes, a simulation (at the previous
323
+ * step's receipt block when `dependsOnPrior`), and the send of those bytes
324
+ * through the adapter under the call's write hold, by
325
+ * `submitReviewedContractWrite`. The connected account must stay
326
+ * `action.account`, and the wallet on the call's chain. Each step waits for
327
+ * its receipt; a revert stops the sequence. A Safe-connected wallet's first
328
+ * step becomes a proposal, and the sequence stops there.
329
+ *
330
+ * The action's `signatures` show in the same model and prompt, and are
331
+ * signed after confirm and before the first send, as `signReviewed` signs.
332
+ *
333
+ * Only an explicit wallet rejection (EIP-1193 4001) releases a step's hold;
334
+ * any other failure once the wallet opened keeps it held.
335
+ *
336
+ * A self-reviewing adapter (Signa) is never prompted: `onPreview` gets the
337
+ * model, and the adapter gets the raw calls to review on its own page. An
338
+ * action with `signatures` is refused for it with EIP-1193 4200.
339
+ */
340
+ async function sendReviewed(action, deps) {
341
+ action.guard?.();
342
+ await (0, wallet_js_1.walletReady)(deps.adapter);
343
+ return send(action, deps);
344
+ }
345
+ /**
346
+ * Finish a sequence {@link sendReviewed} saved under `record.scope`: the step
347
+ * whose wallet call opened is decided by its write hold first (still held is
348
+ * refused; a settled success counts as completed, its receipt's hash
349
+ * recorded), then every remaining step, and only those, is reviewed once
350
+ * more as one fresh model and sent as `sendReviewed` sends. `reverify` gets
351
+ * each step's original index in the action. Only `record.account` can resume
352
+ * it. The result's `hashes` are every completed step's, `receipts` this
353
+ * run's.
354
+ */
355
+ async function resumeReviewed(record, action, deps) {
356
+ action.guard?.();
357
+ await (0, wallet_js_1.walletReady)(deps.adapter);
358
+ const storage = deps.storage ?? (0, writeRecovery_js_1.createHeldWriteStorage)();
359
+ const { raw, record: saved } = (0, sequence_js_1.readReviewedSequence)(record.scope, record.account, storage);
360
+ if (!saved || saved.id !== record.id)
361
+ throw new lineError_js_1.ReviewLineError(sequence_js_1.REVIEWED_SEQUENCE_MOVED_LINE);
362
+ let expected = raw;
363
+ const connected = deps.adapter.account();
364
+ if (!connected)
365
+ throw new lineError_js_1.ReviewLineError("Connect a wallet first.");
366
+ if (connected.toLowerCase() !== saved.account.toLowerCase())
367
+ throw new lineError_js_1.ReviewLineError(ACCOUNT_CHANGED);
368
+ if (saved.sending) {
369
+ const { step, reservation } = saved.sending;
370
+ const call = saved.calls[step];
371
+ const handle = (0, writeHold_js_1.openStepHold)(deps.hold, saved.account, deps.adapter.isSafe(), call);
372
+ const state = await handle.state(deps.clientFor(call.chainId));
373
+ if (state.status !== "clear")
374
+ throw new writeHold_js_1.WriteHeldError(state);
375
+ // Only the step's own reservation's outcome decides it: a later one of
376
+ // the same call or intent hides it.
377
+ if (state.reservation !== reservation)
378
+ throw new lineError_js_1.ReviewLineError(sequence_js_1.REVIEWED_SEQUENCE_UNCERTAIN_LINE);
379
+ // Clear with no settlement: a settlement that could not be saved, which
380
+ // may have run, unless the user released it by its exit ("I checked my
381
+ // wallet") or nothing was sent: then the step is reviewed and sent again.
382
+ if (!state.settled && !handle.released(reservation))
383
+ throw new lineError_js_1.ReviewLineError(sequence_js_1.REVIEWED_SEQUENCE_UNCERTAIN_LINE);
384
+ delete saved.sending;
385
+ delete saved.proposed;
386
+ const { settled } = state;
387
+ if (settled?.result === "success") {
388
+ // This sequence's own step: it is recorded, never offered again.
389
+ await handle.acknowledge(settled);
390
+ saved.completed.push(settled.hash);
391
+ }
392
+ /** Tells the site of the step that went through while away, once saved and when its receipt reads. */
393
+ const told = async () => {
394
+ if (settled?.result !== "success" || !action.onConfirmed)
395
+ return;
396
+ try {
397
+ const receipt = await deps
398
+ .clientFor(call.chainId)
399
+ .getTransactionReceipt({ hash: settled.hash });
400
+ if (receipt.status === "success")
401
+ await action.onConfirmed(call, step, receipt);
402
+ }
403
+ catch {
404
+ /* The step went through whatever the read or the site's callback does. */
405
+ }
406
+ };
407
+ if (saved.completed.length === saved.calls.length) {
408
+ await (0, sequence_js_1.removeReviewedSequence)(saved, storage, expected);
409
+ await told();
410
+ const total = saved.calls.length;
411
+ deps.onProgress?.({ step: total - 1, total, phase: "done" });
412
+ return { status: "done", hashes: saved.completed, receipts: [] };
413
+ }
414
+ expected = await (0, sequence_js_1.saveReviewedSequence)(saved, storage, expected);
415
+ await told();
416
+ }
417
+ return send({
418
+ ...action,
419
+ account: saved.account,
420
+ calls: saved.calls.slice(saved.completed.length),
421
+ scope: saved.scope,
422
+ }, deps, { record: saved, storage, raw: expected });
423
+ }
424
+ async function send(action, deps, resuming) {
425
+ const { adapter, clientFor, hold, onProgress } = deps;
426
+ action.guard?.();
427
+ const { account } = action;
428
+ const calls = [...action.calls];
429
+ if (!calls.length)
430
+ throw new lineError_js_1.ReviewLineError("There is no transaction to review.");
431
+ const snapshots = calls.map(exact);
432
+ const sent = snapshots.map(({ sent }) => sent);
433
+ /** Each step's simulation block, read once; a resumed run's prerequisites are long confirmed. */
434
+ const blocks = calls.map((call) => resuming ? undefined : call.simulationBlockNumber);
435
+ /** A site's release callback never stops core's own release. */
436
+ const tellUnsent = async (call, step) => {
437
+ try {
438
+ await action.onUnsent?.(call, step);
439
+ }
440
+ catch {
441
+ /* The step is released whatever the site's callback does. */
442
+ }
443
+ };
444
+ const connected = adapter.account();
445
+ if (!connected)
446
+ throw new lineError_js_1.ReviewLineError("Connect a wallet first.");
447
+ if (connected.toLowerCase() !== account.toLowerCase()) {
448
+ throw new lineError_js_1.ReviewLineError(ACCOUNT_CHANGED);
449
+ }
450
+ // A resumed sequence signs none: its signatures went with its first run.
451
+ const prepared = (resuming ? [] : [...(action.signatures ?? [])]).map((signature) => (0, sign_js_1.prepareSignature)(signature, account));
452
+ // Signa has no reviewed-signature path yet.
453
+ if (prepared.length && adapter.kind === "self-reviewing") {
454
+ throw (0, wallet_js_1.unsupportedWalletRequest)();
455
+ }
456
+ const request = (safe) => ({
457
+ calls: snapshots.map((snapshot) => shownCall(snapshot, account, safe)),
458
+ ...(prepared.length
459
+ ? { signatures: prepared.map(({ review }) => review) }
460
+ : {}),
461
+ ...(action.title ? { title: action.title } : {}),
462
+ ...(action.description ? { description: action.description } : {}),
463
+ ...(action.confirmLabel ? { confirmLabel: action.confirmLabel } : {}),
464
+ ...(action.context ? { context: action.context } : {}),
465
+ });
466
+ if (adapter.kind === "self-reviewing") {
467
+ if (!adapter.submitToReviewer) {
468
+ throw new lineError_js_1.ReviewLineError("This wallet cannot review this transaction.");
469
+ }
470
+ deps.onPreview?.((0, model_js_1.buildReviewModel)(request(false)));
471
+ /** The steps whose beforeWrite began, and whether the wallet was handed the calls. */
472
+ let began = 0;
473
+ let submitting = false;
474
+ let hash;
475
+ try {
476
+ for (const [step, call] of calls.entries()) {
477
+ began += 1;
478
+ await action.beforeWallet?.(call, step);
479
+ }
480
+ // The site's hooks may have awaited: the gates once more, right before the wallet.
481
+ action.guard?.();
482
+ action.assertWriteState?.();
483
+ if (adapter.account()?.toLowerCase() !== account.toLowerCase())
484
+ throw new lineError_js_1.ReviewLineError(ACCOUNT_CHANGED);
485
+ submitting = true;
486
+ hash = await adapter.submitToReviewer({
487
+ account,
488
+ calls: sent.map((call) => ({ ...call })),
489
+ });
490
+ }
491
+ catch (error) {
492
+ // Only the site's own refusal or an explicit rejection proves nothing was sent.
493
+ if (!submitting || (0, contractWrite_js_1.isDefiniteWalletRejection)(error))
494
+ for (const [step, call] of calls.slice(0, began).entries())
495
+ await tellUnsent(call, step);
496
+ throw error;
497
+ }
498
+ // Every step is told; a site's failure to record it never reads as unsent.
499
+ const failures = [];
500
+ for (const [step, call] of calls.entries())
501
+ try {
502
+ await action.onSubmitted?.(call, step, hash, {
503
+ safe: false,
504
+ adopted: false,
505
+ });
506
+ }
507
+ catch (error) {
508
+ failures.push(error);
509
+ }
510
+ if (failures.length)
511
+ throw new contractWrite_js_1.SubmittedContractWriteError(hash, failures[0]);
512
+ return { status: "done", hashes: [hash], receipts: [] };
513
+ }
514
+ const { scope } = action;
515
+ const storage = scope === undefined
516
+ ? undefined
517
+ : (resuming?.storage ?? deps.storage ?? (0, writeRecovery_js_1.createHeldWriteStorage)());
518
+ /** The journal's saved value as this run last read or wrote it. */
519
+ let expected = resuming?.raw ?? null;
520
+ if (storage && !resuming) {
521
+ const saved = (0, sequence_js_1.readReviewedSequence)(scope, account, storage);
522
+ // A fresh run refuses while an earlier run of the same action is unfinished.
523
+ if (saved.record)
524
+ throw new lineError_js_1.ReviewLineError(sequence_js_1.REVIEWED_SEQUENCE_UNFINISHED_LINE);
525
+ (0, sequence_js_1.assertSavableCalls)(calls);
526
+ expected = saved.raw;
527
+ }
528
+ /** Where the remaining steps start in the whole action. */
529
+ const offset = resuming?.record.completed.length ?? 0;
530
+ const safe = adapter.isSafe();
531
+ /** Through a Safe app, the one MultiSend call every step goes as. */
532
+ const batched =
533
+ // A resumed sequence is journaled step by step: never folded into one proposal.
534
+ action.batch && safe && calls.length > 1 && !resuming
535
+ ? (0, writeHold_js_1.safeBatchCall)(account, calls.map((call, step) => ({ ...call, ...sent[step] })))
536
+ : undefined;
537
+ if (batched &&
538
+ (!adapter.sendCalls ||
539
+ sent.some((call) => call.chainId !== batched.chainId)))
540
+ throw new lineError_js_1.ReviewLineError(exports.SAFE_BATCH_UNSUPPORTED_LINE);
541
+ const handles = (batched ? [batched] : calls).map((call, step) => (0, writeHold_js_1.openStepHold)(hold, account, safe, {
542
+ intent: call.intent,
543
+ legacy: call.legacy,
544
+ ...(batched ?? sent[step]),
545
+ }));
546
+ // A step its hold already holds is refused before the review opens.
547
+ for (const handle of handles) {
548
+ const state = handle.peek();
549
+ if (state.status !== "clear")
550
+ throw new writeHold_js_1.WriteHeldError(state);
551
+ }
552
+ /**
553
+ * Each step's latest reservation as the review opened. Another prompt of
554
+ * the same action that reserved meanwhile, whether or not it has settled,
555
+ * makes this review stale.
556
+ */
557
+ const generation = (handle) => handle.peek().reservation ?? null;
558
+ const reviewedGenerations = handles.map(generation);
559
+ const model = (0, model_js_1.buildReviewModel)(request(safe));
560
+ /** What each step runs as: a Safe app's call is proposed with safeTxGas 0, not the app's gas. */
561
+ const run = safe
562
+ ? sent.map(({ gas: _gas, ...call }) => call)
563
+ : sent;
564
+ if (action.plan) {
565
+ // A plan binds bytes, never an adoption rule; a Safe app's unbatched
566
+ // sequence stops at its first proposal, so its other calls would be spent unsent.
567
+ if (snapshots.some(({ shown }) => shown.adoptIgnoringArgs) ||
568
+ (safe && !batched && calls.length > 1))
569
+ throw new lineError_js_1.ReviewLineError(plan_js_1.REVIEWED_PLAN_REFUSED_LINE);
570
+ (0, plan_js_1.spendPlan)(action.plan, account, [
571
+ ...sent.map(({ chainId, to, data, value, gas }) => ({
572
+ chainId,
573
+ from: account,
574
+ to,
575
+ data,
576
+ value,
577
+ ...(gas === undefined ? {} : { gas }),
578
+ })),
579
+ ...prepared.flatMap(sign_js_1.plannedCallsOf),
580
+ ]);
581
+ }
582
+ else
583
+ await promptSimulated(model, () => account, calls, run, blocks, clientFor, action.signal);
584
+ let signed = {};
585
+ if (prepared.length) {
586
+ // The action's own gates, last before the wallet opens for each signature.
587
+ const stillReviewed = () => {
588
+ action.guard?.();
589
+ action.assertWriteState?.();
590
+ calls.forEach((call, step) => {
591
+ let now;
592
+ try {
593
+ now = exact(call).sent;
594
+ }
595
+ catch {
596
+ throw new lineError_js_1.ReviewLineError(CHANGED);
597
+ }
598
+ if (!sameCall(now, sent[step]))
599
+ throw new lineError_js_1.ReviewLineError(CHANGED);
600
+ });
601
+ handles.forEach((handle, step) => {
602
+ if (generation(handle) !== reviewedGenerations[step]) {
603
+ throw new writeHold_js_1.WriteHoldStaleReviewError();
604
+ }
605
+ });
606
+ };
607
+ signed = {
608
+ signatures: await (0, sign_js_1.signPrepared)(prepared, account, adapter, stillReviewed),
609
+ };
610
+ }
611
+ if (batched) {
612
+ const client = clientFor(batched.chainId);
613
+ const unchanged = () => calls.forEach((call, step) => {
614
+ let now;
615
+ try {
616
+ now = exact(call).sent;
617
+ }
618
+ catch {
619
+ throw new lineError_js_1.ReviewLineError(CHANGED);
620
+ }
621
+ if (!sameCall(now, sent[step]))
622
+ throw new lineError_js_1.ReviewLineError(CHANGED);
623
+ });
624
+ const progress = (phase, hash) => onProgress?.({
625
+ step: 0,
626
+ total: calls.length,
627
+ phase,
628
+ ...(hash ? { hash } : {}),
629
+ });
630
+ /** The steps whose `beforeWallet` began. */
631
+ let began = 0;
632
+ const hash = await handles[0].send(client, (gate) => {
633
+ if (generation(handles[0]) !== reviewedGenerations[0]) {
634
+ throw new writeHold_js_1.WriteHoldStaleReviewError();
635
+ }
636
+ const released = async () => {
637
+ gate.onRejected();
638
+ for (const [step, call] of calls.slice(0, began).entries())
639
+ await tellUnsent(call, step);
640
+ };
641
+ return (0, contractWrite_js_1.submitReviewedContractWrite)({
642
+ request: batched,
643
+ expectedAccount: account,
644
+ review: () => Promise.resolve(),
645
+ switchChain: async (chainId) => {
646
+ if (adapter.chainId() !== chainId)
647
+ await adapter.switchChain(chainId);
648
+ },
649
+ currentAccount: () => adapter.account(),
650
+ reverify: async () => {
651
+ for (const [step, call] of calls.entries())
652
+ await action.reverify?.(call, step, undefined);
653
+ unchanged();
654
+ },
655
+ simulate: async (sending) => {
656
+ await (0, simulation_js_1.simulateCallSequence)(client, {
657
+ from: account,
658
+ chainName: chainNameOf(client, batched.chainId),
659
+ calls: run.map((call, step) => ({
660
+ ...call,
661
+ label: labelOf(calls[step], step),
662
+ dependsOnPrior: calls[step].dependsOnPrior,
663
+ })),
664
+ });
665
+ return sending;
666
+ },
667
+ beforeWrite: async () => {
668
+ await gate.beforeSend();
669
+ try {
670
+ for (const [step, call] of calls.entries()) {
671
+ began = step + 1;
672
+ await action.beforeWallet?.(call, step);
673
+ }
674
+ }
675
+ catch (error) {
676
+ await released();
677
+ throw error;
678
+ }
679
+ },
680
+ beforeSend: () => {
681
+ action.assertWriteState?.();
682
+ unchanged();
683
+ if (!adapter.isSafe())
684
+ throw new lineError_js_1.ReviewLineError("Wallet connection changed. Review the transaction again.");
685
+ if (adapter.chainId() !== batched.chainId)
686
+ throw new lineError_js_1.ReviewLineError("Your wallet changed networks. Review the transaction again.");
687
+ if (adapter.account()?.toLowerCase() !== account.toLowerCase())
688
+ throw new lineError_js_1.ReviewLineError(ACCOUNT_CHANGED);
689
+ },
690
+ onBeforeWriteAborted: released,
691
+ onWriteRejected: released,
692
+ onWriteSubmitted: async (submitted) => {
693
+ gate.afterSubmit(submitted);
694
+ // Every step went: each is told, whatever an earlier callback does.
695
+ const failures = [];
696
+ for (const [step, call] of calls.entries())
697
+ try {
698
+ await action.onSubmitted?.(call, step, submitted, {
699
+ safe: true,
700
+ adopted: false,
701
+ });
702
+ }
703
+ catch (error) {
704
+ failures.push(error);
705
+ }
706
+ if (failures.length)
707
+ throw failures[0];
708
+ },
709
+ write: async () => {
710
+ const id = await adapter.sendCalls(batched.chainId, sent.map(({ to, data, value }) => ({ to, data, value })));
711
+ // A Safe app answers with the proposal's safeTxHash; anything else can't be followed.
712
+ if (typeof id !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(id))
713
+ throw new lineError_js_1.ReviewLineError(exports.SAFE_BATCH_UNFOLLOWABLE_LINE);
714
+ return id;
715
+ },
716
+ onPhase: (phase) => progress(phase),
717
+ guard: action.guard,
718
+ });
719
+ });
720
+ progress("proposed", hash);
721
+ return { status: "proposed", step: 0, safeTxHash: hash, ...signed };
722
+ }
723
+ // Saved before the first wallet call, once the review is confirmed.
724
+ let journal;
725
+ if (storage) {
726
+ if (resuming)
727
+ journal = { ...resuming.record, modelId: model.id };
728
+ else
729
+ journal = {
730
+ version: 1,
731
+ id: globalThis.crypto.randomUUID(),
732
+ account,
733
+ scope: scope,
734
+ modelId: model.id,
735
+ // What was reviewed, read once above.
736
+ calls: calls.map((call, step) => ({
737
+ ...call,
738
+ ...snapshots[step].shown,
739
+ ...snapshots[step].sent,
740
+ })),
741
+ completed: [],
742
+ };
743
+ // Refused when another window changed it meanwhile.
744
+ expected = await (0, sequence_js_1.saveReviewedSequence)(journal, storage, expected);
745
+ }
746
+ /**
747
+ * Progress after a send is saved as it can be: the step's saved
748
+ * `sending` mark already lets its hold decide it on resume.
749
+ */
750
+ const keep = async () => {
751
+ try {
752
+ if (journal.completed.length === journal.calls.length)
753
+ await (0, sequence_js_1.removeReviewedSequence)(journal, storage, expected);
754
+ else
755
+ expected = await (0, sequence_js_1.saveReviewedSequence)(journal, storage, expected);
756
+ }
757
+ catch {
758
+ /* Another window owns the sequence now; the hold still holds the step. */
759
+ }
760
+ };
761
+ const hashes = [];
762
+ const receipts = [];
763
+ const total = offset + calls.length;
764
+ for (const [step, call] of calls.entries()) {
765
+ const reviewed = sent[step];
766
+ /** This step's index in the whole action. */
767
+ const at = offset + step;
768
+ const progress = (phase, hash) => onProgress?.({ step: at, total, phase, ...(hash ? { hash } : {}) });
769
+ const unchanged = () => {
770
+ let now;
771
+ try {
772
+ now = exact(call);
773
+ }
774
+ catch {
775
+ throw new lineError_js_1.ReviewLineError(CHANGED);
776
+ }
777
+ if (!sameCall(now.sent, reviewed) ||
778
+ String(now.shown.adoptIgnoringArgs) !==
779
+ String(snapshots[step].shown.adoptIgnoringArgs))
780
+ throw new lineError_js_1.ReviewLineError(CHANGED);
781
+ };
782
+ const client = clientFor(reviewed.chainId);
783
+ const prior = receipts[step - 1];
784
+ /** Where the step before this one confirmed: this run's receipt, or a resumed run's last completed step. */
785
+ const confirmedBefore = async () => {
786
+ if (prior)
787
+ return {
788
+ chainId: sent[step - 1].chainId,
789
+ blockNumber: prior.blockNumber,
790
+ blockHash: prior.blockHash,
791
+ };
792
+ const done = resuming?.record;
793
+ if (step > 0 || !done?.completed.length)
794
+ return undefined;
795
+ const { chainId } = done.calls[done.completed.length - 1];
796
+ // One read: a receipt that can't be read now (a repriced original) is no block.
797
+ const receipt = await clientFor(chainId)
798
+ .getTransactionReceipt({
799
+ hash: done.completed[done.completed.length - 1],
800
+ })
801
+ .catch(() => undefined);
802
+ return receipt
803
+ ? {
804
+ chainId,
805
+ blockNumber: receipt.blockNumber,
806
+ blockHash: receipt.blockHash,
807
+ }
808
+ : undefined;
809
+ };
810
+ const priorBlock = call.dependsOnPrior &&
811
+ prior &&
812
+ sent[step - 1].chainId === reviewed.chainId
813
+ ? prior.blockNumber
814
+ : undefined;
815
+ /** The reservation this step's send took. */
816
+ let reserved;
817
+ /** A proposal of exactly this call its Safe already queued, sent in its place. */
818
+ let queued;
819
+ const hash = await handles[step].send(client, (gate) => {
820
+ // Under the hold's own check of the call, before anything reserves.
821
+ if (generation(handles[step]) !== reviewedGenerations[step]) {
822
+ throw new writeHold_js_1.WriteHoldStaleReviewError();
823
+ }
824
+ /** Nothing was sent: the hold lets go, and the step is no longer in flight. */
825
+ const released = async () => {
826
+ gate.onRejected();
827
+ if (journal) {
828
+ delete journal.sending;
829
+ await keep();
830
+ }
831
+ await tellUnsent(call, at);
832
+ };
833
+ return (0, contractWrite_js_1.submitReviewedContractWrite)({
834
+ request: reviewed,
835
+ expectedAccount: account,
836
+ // Confirmed once, above, for every step.
837
+ review: () => Promise.resolve(),
838
+ switchChain: async (chainId) => {
839
+ if (adapter.chainId() !== chainId)
840
+ await adapter.switchChain(chainId);
841
+ },
842
+ currentAccount: () => adapter.account(),
843
+ reverify: async () => {
844
+ if (action.reverify)
845
+ await action.reverify(call, at, await confirmedBefore());
846
+ unchanged();
847
+ },
848
+ simulate: async (sending) => {
849
+ const blockNumber = priorBlock ??
850
+ (blocks[step] === undefined
851
+ ? undefined
852
+ : await notBefore(client, blocks[step]));
853
+ const tx = {
854
+ to: sending.to,
855
+ data: sending.data,
856
+ value: sending.value,
857
+ ...(blockNumber === undefined ? {} : { blockNumber }),
858
+ };
859
+ const { gas } = run[step];
860
+ const [, estimate, found] = await Promise.all([
861
+ (0, simulation_js_1.simulateStateChangingTransaction)(client, {
862
+ from: account,
863
+ ...tx,
864
+ gas,
865
+ }).catch((error) => {
866
+ throw (0, simulation_js_1.simulationFailure)(`${labelOf(call, at)} cannot run on ${chainNameOf(client, sending.chainId)}: ${(0, simulation_js_1.revertDetail)(error)}`, error);
867
+ }),
868
+ // A Safe app signs the sent gas as safeTxGas; 0 makes a failed call revert.
869
+ safe || gas !== undefined
870
+ ? undefined
871
+ : client.estimateGas({ account, ...tx }),
872
+ // A Safe app never proposes again a call its Safe already queued exactly.
873
+ safe && (0, safeService_js_1.hasSafeService)(sending.chainId)
874
+ ? (0, safeService_js_1.findPendingSafeAppProposal)(client, sending.chainId, account, sending, hold.safeService, true, adopting(snapshots[step].shown))
875
+ : null,
876
+ ]);
877
+ queued = found;
878
+ return {
879
+ ...sending,
880
+ gas: safe ? 0n : (gas ?? (0, gas_js_1.gasWithHeadroom)(estimate)),
881
+ };
882
+ },
883
+ beforeWrite: async () => {
884
+ // An adopted proposal is held as the call it runs.
885
+ await gate.beforeSend(queued?.call);
886
+ reserved = handles[step].peek().reservation;
887
+ if (journal)
888
+ try {
889
+ const { reservation } = handles[step].peek();
890
+ if (reservation === undefined)
891
+ throw new lineError_js_1.ReviewLineError(NOT_SAVED);
892
+ journal.sending = { step: at, reservation };
893
+ expected = await (0, sequence_js_1.saveReviewedSequence)(journal, storage, expected);
894
+ }
895
+ catch (error) {
896
+ // Reserved, and the wallet never opened.
897
+ delete journal.sending;
898
+ gate.onRejected();
899
+ throw error;
900
+ }
901
+ try {
902
+ await action.beforeWallet?.(call, at);
903
+ }
904
+ catch (error) {
905
+ await released();
906
+ throw error;
907
+ }
908
+ },
909
+ beforeSend: () => {
910
+ action.assertWriteState?.();
911
+ unchanged();
912
+ if (adapter.isSafe() !== safe) {
913
+ throw new lineError_js_1.ReviewLineError("Wallet connection changed. Review the transaction again.");
914
+ }
915
+ if (adapter.chainId() !== reviewed.chainId) {
916
+ throw new lineError_js_1.ReviewLineError("Your wallet changed networks. Review the transaction again.");
917
+ }
918
+ // Last, after every callback and getter above could have run.
919
+ if (adapter.account()?.toLowerCase() !== account.toLowerCase()) {
920
+ throw new lineError_js_1.ReviewLineError(ACCOUNT_CHANGED);
921
+ }
922
+ },
923
+ onBeforeWriteAborted: released,
924
+ onWriteRejected: released,
925
+ // An adopted proposal's nonce lets the hold prove its execution onchain.
926
+ onWriteSubmitted: async (submitted) => {
927
+ gate.afterSubmit(submitted, queued?.tx);
928
+ await action.onSubmitted?.(call, at, submitted, {
929
+ safe,
930
+ adopted: !!queued,
931
+ });
932
+ },
933
+ write: async ({ chainId, to, data, value, gas }) => queued?.proposalHash ??
934
+ adapter.sendTransaction({ chainId, to, data, value, gas }),
935
+ onPhase: (phase) => progress(phase),
936
+ guard: action.guard,
937
+ });
938
+ });
939
+ hashes.push(hash);
940
+ // A later step held under this step's key (the same call, or the same
941
+ // intent) was reserved by this send, not by another prompt.
942
+ for (const [later, handle] of handles.entries()) {
943
+ const sameKey = call.intent
944
+ ? calls[later].intent === call.intent &&
945
+ sent[later].chainId === reviewed.chainId
946
+ : !calls[later].intent && sameCall(sent[later], reviewed);
947
+ if (later > step && sameKey) {
948
+ reviewedGenerations[later] = generation(handle);
949
+ }
950
+ }
951
+ if (safe) {
952
+ if (journal) {
953
+ journal.proposed = { step: at, safeTxHash: hash };
954
+ await keep();
955
+ }
956
+ progress("proposed", hash);
957
+ return { status: "proposed", step: at, safeTxHash: hash, ...signed };
958
+ }
959
+ progress("receipt", hash);
960
+ const receipt = await (0, receipt_js_1.waitForTrackedReceipt)(client, hash);
961
+ // The hold checks the receipt itself and settles, so the same call (or
962
+ // intent) can be reviewed and sent again without waiting on a view.
963
+ // Only this send's own reservation's settlement speaks for it: a later
964
+ // send of the same call or intent may have settled since.
965
+ const settled = await handles[step]
966
+ .state(client)
967
+ .then((state) => state.status === "clear" &&
968
+ reserved !== undefined &&
969
+ state.reservation === reserved
970
+ ? state.settled
971
+ : undefined)
972
+ .catch(() => undefined);
973
+ if (receipt.status === "success" && settled?.result === "failed") {
974
+ // It ran, and the hold proved it failed: a Safe's call that failed.
975
+ if (journal) {
976
+ delete journal.sending;
977
+ await keep();
978
+ }
979
+ throw new lineError_js_1.ReviewLineError(messages_js_1.WRITE_HOLD_CALL_FAILED_LINE);
980
+ }
981
+ // A Safe's execTransaction is done only once its hold proved the Safe's
982
+ // own success; until then the step stays in flight, held and journaled.
983
+ if (receipt.status === "success" &&
984
+ (0, safeService_js_1.isSafeExecTransaction)(reviewed.data) &&
985
+ settled?.result !== "success") {
986
+ throw new lineError_js_1.ReviewLineError(messages_js_1.WRITE_HOLD_SAFE_UNCONFIRMED_LINE);
987
+ }
988
+ if (receipt.status !== "success") {
989
+ // It ran and reverted: nothing of it to resume.
990
+ if (journal) {
991
+ delete journal.sending;
992
+ await keep();
993
+ }
994
+ throw new lineError_js_1.ReviewedCallRevertedError(at, hash, receipt);
995
+ }
996
+ receipts.push(receipt);
997
+ if (journal) {
998
+ delete journal.sending;
999
+ journal.completed.push(hash);
1000
+ await keep();
1001
+ }
1002
+ try {
1003
+ await action.onConfirmed?.(call, at, receipt);
1004
+ }
1005
+ catch {
1006
+ /* The step went through whatever the site's callback does. */
1007
+ }
1008
+ }
1009
+ onProgress?.({ step: total - 1, total, phase: "done" });
1010
+ return {
1011
+ status: "done",
1012
+ hashes: journal ? [...journal.completed] : hashes,
1013
+ receipts,
1014
+ ...signed,
1015
+ };
1016
+ }
1017
+ //# sourceMappingURL=send.js.map