@laceio/sdk 0.1.0 → 0.1.1-canary.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 (408) hide show
  1. package/dist/{Ed25519PublicKey-lvu1s54U.cjs → Address-AMKTo-dA.cjs} +17150 -61
  2. package/dist/{Ed25519PublicKey-CjnuAYRR.js → Address-_c_-9hLC.js} +16955 -264
  3. package/dist/{Bip32Account-r3KAhCgU.js → Bip32Account-CaW1zC9Y.js} +4 -5
  4. package/dist/{Bip32Account-Cz3h0ZUa.cjs → Bip32Account-DOaGmKpv.cjs} +4 -5
  5. package/dist/Cardano-B-gyqBBD.js +311 -0
  6. package/dist/Cardano-BQ_af0Y3.cjs +322 -0
  7. package/dist/Ed25519PublicKey-B3QcR0J2.cjs +88 -0
  8. package/dist/Ed25519PublicKey-DIE19xHg.js +83 -0
  9. package/dist/InMemoryKeyAgent-Bc_xZY-v.cjs +652 -0
  10. package/dist/InMemoryKeyAgent-D8T3vtXy.js +631 -0
  11. package/dist/{PaymentAddress-DhbtuYb7.cjs → PaymentAddress-B4n1KZMx.cjs} +1 -2
  12. package/dist/{PaymentAddress-xSmGUQ-8.js → PaymentAddress-Cml3EA1n.js} +1 -2
  13. package/dist/Percent-Cfuw5YyL.cjs +11 -0
  14. package/dist/Percent-DpvyJ_93.js +5 -0
  15. package/dist/SodiumBip32Ed25519-BaD-kjyx.cjs +380 -0
  16. package/dist/SodiumBip32Ed25519-DCikAMsl.js +363 -0
  17. package/dist/{TransactionBuilder-CeJTNzm-.cjs → TransactionBuilder-BakTJvsJ.cjs} +59 -89
  18. package/dist/{TransactionBuilder-DGNDM4kt.js → TransactionBuilder-D2ErwVWv.js} +5 -71
  19. package/dist/_md-6zV2Cme0.cjs +684 -0
  20. package/dist/_md-gbUqlhqd.js +399 -0
  21. package/dist/_web-crypto-random-bytes-BvzwTYPG.js +197 -0
  22. package/dist/acc-module-NL_8mEHH.cjs +27654 -0
  23. package/dist/acc-module-ur8CkBd1.js +27624 -0
  24. package/dist/account-call-B7cF8ly4.cjs +293 -0
  25. package/dist/account-call-BF4rBcNw.js +3 -0
  26. package/dist/account-call-CoCq3V55.js +252 -0
  27. package/dist/account-call-DFcD257E.cjs +4 -0
  28. package/dist/account-guards-BeKbV9D1.cjs +41 -0
  29. package/dist/account-guards-qnFXuXSE.js +36 -0
  30. package/dist/{activity-type.stable-ClO9j_PH.cjs → activity-type-BPXxdHBP.cjs} +2 -1
  31. package/dist/{activity-type.stable-Ml0SOYv9.js → activity-type-C1geyPsH.js} +2 -1
  32. package/dist/address-OxwXIwbF.js +15502 -0
  33. package/dist/address-book-address-validator-BqYM0Uyp.cjs +41 -0
  34. package/dist/{address-book-address-validator-BhqRfmlL.cjs → address-book-address-validator-CGIcqn5W.cjs} +5 -4
  35. package/dist/{address-book-address-validator-VqIOB8PY.js → address-book-address-validator-FkkYim3z.js} +6 -4
  36. package/dist/address-book-address-validator-RSx2Jozy.js +39 -0
  37. package/dist/address-lECYBd9-.cjs +13043 -0
  38. package/dist/{apply-vkey-witnesses-Ctzk7eye.cjs → apply-vkey-witnesses-1jyczTkw.cjs} +1 -1
  39. package/dist/{apply-vkey-witnesses-Dfo7XfhL.js → apply-vkey-witnesses-Byb40-yJ.js} +1 -1
  40. package/dist/artefact-loader-Cqc9eSkL.cjs +182 -0
  41. package/dist/artefact-loader-D0T2f6F7.js +159 -0
  42. package/dist/auto-dismiss-failure-on-success-BWJceNXd.cjs +37 -0
  43. package/dist/auto-dismiss-failure-on-success-Bpod-v4r.js +31 -0
  44. package/dist/bip32-EPpcFrpH.cjs +3851 -0
  45. package/dist/{bip32ed25519-D2In8WzR.cjs → bip32ed25519-CRiG-Kjg.cjs} +5 -4
  46. package/dist/{bip32ed25519-qsLH_eWO.js → bip32ed25519-E-fCUZNv.js} +6 -4
  47. package/dist/bip39-DZomfqeY.js +9 -0
  48. package/dist/bip39-MAck9iod.cjs +26 -0
  49. package/dist/bitcoin-data-provider-D0OT_Uiu.cjs +26 -0
  50. package/dist/bitcoin-data-provider-LN_8oamW.js +21 -0
  51. package/dist/bitcoin.cjs +48 -0
  52. package/dist/bitcoin.d.ts +29805 -1
  53. package/dist/bitcoin.js +22 -0
  54. package/dist/{blake2b-BGHUWMsV.js → blake2b-BL-eR5G2.js} +6 -4
  55. package/dist/{blake2b-DspUqw68.cjs → blake2b-Iy324FwH.cjs} +5 -4
  56. package/dist/{blockfrost-tx-submit-provider-BUEOmdvc.js → blockfrost-tx-submit-provider-BHDLgOIE.js} +9 -162
  57. package/dist/{blockfrost-tx-submit-provider-B5dWbdrZ.cjs → blockfrost-tx-submit-provider-BxC19smv.cjs} +7 -171
  58. package/dist/{blockingWithLatestFrom-BhaE7pnA.js → blockingWithLatestFrom-FUfZFEUG.js} +1 -1
  59. package/dist/{blockingWithLatestFrom-DkuT3G2a.cjs → blockingWithLatestFrom-bmCPgA3c.cjs} +1 -1
  60. package/dist/bn-BFq5a4dk.cjs +2553 -0
  61. package/dist/{SodiumBip32Ed25519-BEfo1Q9m.js → browser-D5jRKv_L.js} +4 -401
  62. package/dist/bytes.vo-CziaOsba.js +24 -0
  63. package/dist/bytes.vo-hecRu3YZ.cjs +35 -0
  64. package/dist/{cardano-network-id-to-network-type-PXvn1ycQ.cjs → cardano-network-id-to-network-type-BwG_d6QP.cjs} +1 -1
  65. package/dist/{cardano-network-id-to-network-type-DHgKhDAK.js → cardano-network-id-to-network-type-CYJheetp.js} +1 -1
  66. package/dist/{cardano-network-id.vo-CbNNKd-k.cjs → cardano-network-id.vo-C5VuBXT3.cjs} +2 -2
  67. package/dist/{cardano-network-id.vo-CTYe-Oix.js → cardano-network-id.vo-CeLJqmb_.js} +2 -2
  68. package/dist/{cardano-tx-id.vo-BMwemq5x.js → cardano-tx-id.vo-DJ70NC0-.js} +1 -1
  69. package/dist/{cardano-tx-id.vo-NY-gCOWJ.cjs → cardano-tx-id.vo-lnyO9XaW.cjs} +1 -1
  70. package/dist/cardano.cjs +31 -24
  71. package/dist/cardano.d.ts +27602 -8121
  72. package/dist/cardano.js +21 -13
  73. package/dist/client-Bw2foG5E.cjs +808 -0
  74. package/dist/const-BB3MccLt.js +153 -0
  75. package/dist/{const-BErUlcZw.cjs → const-BWBAzOmN.cjs} +1 -1
  76. package/dist/const-CSWmOyzn.cjs +42 -0
  77. package/dist/{const-Dy8Ctnpd.cjs → const-D3mCkwrM.cjs} +5 -5
  78. package/dist/const-DW7z3R5k.js +13 -0
  79. package/dist/{const-DeUA7zYl.js → const-DbuUHSzR.js} +5 -5
  80. package/dist/const-DgBzKfz6.cjs +31995 -0
  81. package/dist/{const-DVW6DGkZ.js → const-xGSNCtwQ.js} +1 -1
  82. package/dist/contract-BsIGhsYa.js +171 -0
  83. package/dist/contract-DD6CGnpB.cjs +189 -0
  84. package/dist/create-add-accounts-2mQA0ST2.js +102 -0
  85. package/dist/create-add-accounts-B6mR2NMn.cjs +114 -0
  86. package/dist/{delegation-tx-builder-CF0VunDW.cjs → delegation-tx-builder-Cp30jtj9.cjs} +9 -8
  87. package/dist/{delegation-tx-builder-C8LQxU2R.js → delegation-tx-builder-DUMikSTg.js} +9 -7
  88. package/dist/{deregistration-tx-builder-DcUXA3Ls.cjs → deregistration-tx-builder-B-ZeY-dR.cjs} +10 -9
  89. package/dist/{deregistration-tx-builder-BOBPP8m_.js → deregistration-tx-builder-CRLrK99U.js} +10 -8
  90. package/dist/device-commitment-hex.vo-BIUYUNz1.cjs +31 -0
  91. package/dist/device-commitment-hex.vo-D9FsYLn7.js +26 -0
  92. package/dist/device-key-BToCoS4D.js +2394 -0
  93. package/dist/device-key-D6euvxFi.cjs +2489 -0
  94. package/dist/device-readers-BHJeNR8I.js +3 -0
  95. package/dist/device-readers-Dw7uTNM5.cjs +167 -0
  96. package/dist/device-readers-b4g2j3QL.js +144 -0
  97. package/dist/device-readers-cxcJcWq5.cjs +7 -0
  98. package/dist/dist-B00GN41m.js +141 -0
  99. package/dist/dist-BA1xnF9O.cjs +4413 -0
  100. package/dist/dist-BmHOXLe8.cjs +3814 -0
  101. package/dist/dist-Br0OVXKt.cjs +535 -0
  102. package/dist/dist-C1JK9C1I.cjs +3452 -0
  103. package/dist/dist-DKEViNEI.cjs +1306 -0
  104. package/dist/dist-Dwqx184a.cjs +43175 -0
  105. package/dist/dist-MJof_Ky1.cjs +145 -0
  106. package/dist/dist-p84sVVJ9.cjs +1394 -0
  107. package/dist/{earn-rewards-tx-builder-BRlTueHS.js → earn-rewards-tx-builder-8G_rouIk.js} +9 -7
  108. package/dist/{earn-rewards-tx-builder-RgO0QUY8.cjs → earn-rewards-tx-builder-cOUNV58E.cjs} +9 -8
  109. package/dist/enc-key-circuits-0Dud78IB.js +13 -0
  110. package/dist/enc-key-circuits-yjPHbuo_.cjs +24 -0
  111. package/dist/english-BZ_ebp2-.cjs +2057 -0
  112. package/dist/envelopes-B3QCzo5p.js +2658 -0
  113. package/dist/envelopes-BdE1xlgz.cjs +2777 -0
  114. package/dist/errors-BA3b8aVu.cjs +584 -0
  115. package/dist/errors-pkQdxW-7.js +392 -0
  116. package/dist/experimental.cjs +6820 -0
  117. package/dist/experimental.d.ts +63552 -0
  118. package/dist/experimental.js +6670 -0
  119. package/dist/fees-6C2uEWFK.js +653 -0
  120. package/dist/fees-DtC71fOx.cjs +820 -0
  121. package/dist/gated-call-BrPjKolT.js +3 -0
  122. package/dist/gated-call-CcsuW9L5.js +691 -0
  123. package/dist/gated-call-DdDrxEq5.cjs +732 -0
  124. package/dist/gated-call-Dk7PazhG.cjs +4 -0
  125. package/dist/grant-rules-B7eDuD5G.cjs +5468 -0
  126. package/dist/grant-rules-qLrKUIR3.js +5217 -0
  127. package/dist/grant-status-CDmZpx32.js +296 -0
  128. package/dist/grant-status-CQtwjLsA.cjs +337 -0
  129. package/dist/grants-Bnb-3Hkv.cjs +16 -0
  130. package/dist/grants-CIb6nOV7.cjs +273 -0
  131. package/dist/grants-CYY0AquW.js +202 -0
  132. package/dist/grants-DOsFSotP.js +4 -0
  133. package/dist/{group-cardano-addresses-by-account-C-1GHUu8.cjs → group-cardano-addresses-by-account-BGOtPV-Y.cjs} +4 -40
  134. package/dist/{group-cardano-addresses-by-account-p4dXmCoj.js → group-cardano-addresses-by-account-TqvESg9x.js} +6 -37
  135. package/dist/{guarded-transaction-signer-DZSGHp1K.js → guarded-transaction-signer-BbVDE9Nn.js} +12 -18
  136. package/dist/{guarded-transaction-signer-BLSezbfV.cjs → guarded-transaction-signer-DTLjAkif.cjs} +26 -26
  137. package/dist/hex-BVtOKJki.cjs +263 -0
  138. package/dist/hex-CGYJPK_Z.js +192 -0
  139. package/dist/http-client-B7_kBTYn.js +155 -0
  140. package/dist/http-client-DYQ_7Rdf.cjs +167 -0
  141. package/dist/identity-DxPhxkZs.js +239 -0
  142. package/dist/identity-y41Yle03.cjs +274 -0
  143. package/dist/in-memory-wallet-integration-5BKiAvPT.js +20861 -0
  144. package/dist/{in-memory-wallet-integration-CUTa68ET.js → in-memory-wallet-integration-BCOqDgbt.js} +10 -107
  145. package/dist/in-memory-wallet-integration-Bi8I9Dso.js +128 -0
  146. package/dist/in-memory-wallet-integration-Bsl2ONua.cjs +239 -0
  147. package/dist/in-memory-wallet-integration-DZ0CAyvY.cjs +20865 -0
  148. package/dist/{in-memory-wallet-integration-Dw_S7amX.cjs → in-memory-wallet-integration-DsFm8gLk.cjs} +21 -112
  149. package/dist/{in-memory-wallet-integration-Z4Bp_DTv.cjs → in-memory-wallet-integration-aW_SF-go.cjs} +8 -5
  150. package/dist/{in-memory-wallet-integration-B6cbAlvD.js → in-memory-wallet-integration-mIEc8EfR.js} +9 -5
  151. package/dist/index.cjs +95 -58
  152. package/dist/index.d.ts +45152 -13448
  153. package/dist/index.js +55 -28
  154. package/dist/{init-CVi8J0zk.cjs → init-5SfWgCYD.cjs} +3 -3
  155. package/dist/{init-Ci7aqWCf2.cjs → init-8NVWbo302.cjs} +2 -2
  156. package/dist/init-B-C7n6u6.js +5 -0
  157. package/dist/{init-BFMWRf-L.js → init-B-ppfGO-.js} +1 -1
  158. package/dist/{init-IiR093Jw.cjs → init-B2OUhY5T.cjs} +2 -2
  159. package/dist/{init-DXnus199.cjs → init-B6Eg_o6G.cjs} +5 -5
  160. package/dist/{init-4hDl5bpr.js → init-BAWMHL1k.js} +2 -2
  161. package/dist/init-BC2CZZOu2.cjs +2002 -0
  162. package/dist/{init-a228hXnS.cjs → init-BF-a2p6E.cjs} +65 -73
  163. package/dist/{init-fFirX8OY.cjs → init-BFxlQAD8.cjs} +1 -1
  164. package/dist/init-BJE1ry0O.js +17 -0
  165. package/dist/{init-BVnI4z2J.js → init-BPKBVOHU.js} +2 -2
  166. package/dist/{init-bdqZBd-o.js → init-BSP_xcpB.js} +2 -2
  167. package/dist/{init-CFNOn3lo.js → init-BSd7mznK.js} +3 -3
  168. package/dist/{init-MjR4swi6.js → init-BUJTT0MN.js} +36 -43
  169. package/dist/init-BUpFzYsD.cjs +143 -0
  170. package/dist/{init-BvpYHyLH.cjs → init-BZ-WyAIL.cjs} +3 -3
  171. package/dist/init-BZdfdLX7.js +422 -0
  172. package/dist/{init-CJeOys1w.cjs → init-B_HWNXMD.cjs} +2 -2
  173. package/dist/init-BbIMt6xU.cjs +6 -0
  174. package/dist/{init-BYdN1aqE.cjs → init-BewG6swt.cjs} +2 -2
  175. package/dist/{init-BX0AZAVH2.js → init-Bilqsbng.js} +2 -2
  176. package/dist/{init-Tpn9VkFu.cjs → init-BpwlQ6wF.cjs} +1 -1
  177. package/dist/{init-D5-P0HXK.js → init-BtQOzUjX.js} +1 -1
  178. package/dist/{init-BvGz3qUd2.js → init-CCTscPaY2.js} +2 -2
  179. package/dist/{init-D4hPipCD.cjs → init-CH0yyBof.cjs} +1 -1
  180. package/dist/init-CaIkrXal.cjs +121 -0
  181. package/dist/init-CkZy4IMf.cjs +107 -0
  182. package/dist/{init-Drr2f_Pr.js → init-CmfcY7SV.js} +1 -1
  183. package/dist/{init-Kf3_OLv6.js → init-CnJAlks_.js} +1 -1
  184. package/dist/{init-Cdjt_z-b.cjs → init-CqqmtAIK.cjs} +4 -4
  185. package/dist/{init-Bb0avZeZ.js → init-Cu2znmIc.js} +5 -5
  186. package/dist/init-Cz5LAizN.js +2001 -0
  187. package/dist/init-D4WM3-Mn.cjs +18 -0
  188. package/dist/init-DMi2FGpf.cjs +424 -0
  189. package/dist/{init-lcaUSOOG.js → init-DPTJohPd.js} +1 -1
  190. package/dist/{init-BTYLMF9F2.cjs → init-DexLSprL2.cjs} +258 -61
  191. package/dist/init-DfKPgD21.js +106 -0
  192. package/dist/init-DgzRsI-C.js +141 -0
  193. package/dist/init-Dj4Cn7-P.js +4789 -0
  194. package/dist/init-DktAnYI0.cjs +2354 -0
  195. package/dist/{init-DFwhgert.cjs → init-DmyuM9Lo.cjs} +1 -1
  196. package/dist/{init-CX_zLXc6.js → init-IBnizp1U.js} +5 -5
  197. package/dist/init-MDKB6tGI2.cjs +4790 -0
  198. package/dist/{init-DbZ5byj42.js → init-TbB38KNt2.js} +258 -60
  199. package/dist/init-X6m_cqDy.js +2353 -0
  200. package/dist/{init-DvVgKCaP2.cjs → init-bzvl1JhF.cjs} +2 -2
  201. package/dist/{init-BgrY29gW.js → init-dsYkC_0B.js} +4 -4
  202. package/dist/{init-B5BsI3DR.cjs → init-kvn5FlPS.cjs} +7 -7
  203. package/dist/init-vfy9DW_E.js +119 -0
  204. package/dist/{init-BE_6Jyc4.js → init-w7xl6LsP.js} +3 -3
  205. package/dist/{init-DEsWVyn5.cjs → init-wgiBUmyW.cjs} +1 -1
  206. package/dist/{is-retriable-error-DWv_ob-f.js → is-retriable-error-CmFR20pe.js} +1 -1
  207. package/dist/{is-retriable-error-Dtb4JGjd.cjs → is-retriable-error-D2yo0_lS.cjs} +1 -1
  208. package/dist/jubjub-authoriser-A2c8vmkN.js +84 -0
  209. package/dist/jubjub-authoriser-D3ZDgzcF.cjs +85 -0
  210. package/dist/key-source-CcLTx8HK.js +2761 -0
  211. package/dist/key-source-D2tTAag-.cjs +806 -0
  212. package/dist/{key-value-store-DTX1quRS.js → key-value-store-DZcNDB3n.js} +1 -1
  213. package/dist/{key-value-store-B-XRIwaM.cjs → key-value-store-Htj4RgMe.cjs} +1 -1
  214. package/dist/keyDerivation-DcKRUCVh.cjs +274 -0
  215. package/dist/keyDerivation-QGesIMbS.js +4077 -0
  216. package/dist/{lace-context-FpU3iG3f.cjs → lace-context-BN0b_ury.cjs} +4 -4
  217. package/dist/{lace-context-BQvvxVLm.js → lace-context-DMRJ87Vg.js} +4 -4
  218. package/dist/lib-BC-62Byh.cjs +2359 -0
  219. package/dist/lib-JYqcAX0c.js +2354 -0
  220. package/dist/midnight-account-id.vo--Vcx34Xk.cjs +10 -0
  221. package/dist/midnight-account-id.vo-BuB8fiVO.js +5 -0
  222. package/dist/midnight-engine-B7D2hbHo.js +5 -0
  223. package/dist/midnight-engine-D5ikN_5F.cjs +6 -0
  224. package/dist/midnight-wallet-BOdFD6SX.cjs +24 -0
  225. package/dist/midnight-wallet-CAqZrNVe.js +18 -0
  226. package/dist/midnight.cjs +1412 -0
  227. package/dist/midnight.d.ts +28609 -0
  228. package/dist/midnight.js +1355 -0
  229. package/dist/modules-cardano-BiL9hoM3.js +996 -0
  230. package/dist/modules-cardano-UeXks3sd.cjs +1109 -0
  231. package/dist/node-relay-BOuvdZvC.cjs +49 -0
  232. package/dist/node-relay-CLtc5n1v.js +48 -0
  233. package/dist/outputs-5qdW8s-f.js +196 -0
  234. package/dist/outputs-BEM1GPMf.cjs +339 -0
  235. package/dist/{SodiumBip32Ed25519-BQUcrQWX.cjs → pbkdf2-B1veireP.cjs} +6 -411
  236. package/dist/pending-operation-CfTUNQVD.cjs +26 -0
  237. package/dist/pending-operation-Ljijb1Uh.js +21 -0
  238. package/dist/providers-1on_fwJO.cjs +364 -0
  239. package/dist/providers-C1MsxfMA.js +341 -0
  240. package/dist/redacted-4SJPqt-G.js +81 -0
  241. package/dist/redacted-DwxIDdIF.cjs +86 -0
  242. package/dist/{reselect-DPML1uFN.cjs → reselect-kP4DaAII.cjs} +1 -1
  243. package/dist/{retry-config-oNtfdyrG.cjs → retry-config-BGv5q5QM.cjs} +1 -1
  244. package/dist/{retry-config-VdbuqBsI.js → retry-config-Bu5Yn5ua.js} +1 -1
  245. package/dist/safe-buffer-CqGpSFCQ.js +43 -0
  246. package/dist/safe-buffer-DbXbwhUQ.cjs +48 -0
  247. package/dist/sdk-1SRu8Ype.cjs +1445 -0
  248. package/dist/sdk-BneJm7ey.js +1249 -0
  249. package/dist/sdk-init-9TG1ZvHF.cjs +3795 -0
  250. package/dist/sdk-init-Ce3mD-8F.js +1047 -0
  251. package/dist/secret-box-CSmRp6Tt.js +3777 -0
  252. package/dist/secret-box-ZfbYwBgS.cjs +6970 -0
  253. package/dist/send-proof-progress-DtAcrMP6.cjs +33 -0
  254. package/dist/send-proof-progress-Dx2cUz1B.js +22 -0
  255. package/dist/sha2-DaRJZOjk.cjs +390 -0
  256. package/dist/sha2-c4sB6peD.js +378 -0
  257. package/dist/side-effects-C3BhLrdP.js +768 -0
  258. package/dist/side-effects-DG0R-KFc.cjs +804 -0
  259. package/dist/sign-bitcoin-transaction-with-seed-C7vq9om8.js +147 -0
  260. package/dist/sign-bitcoin-transaction-with-seed-Ds1dwzan.cjs +158 -0
  261. package/dist/signer--2I6poMD.js +2 -0
  262. package/dist/signer-CX21Ups_.cjs +121 -0
  263. package/dist/signer-Cnn9q9oY.js +92 -0
  264. package/dist/signer-DbSxjk0F.cjs +7 -0
  265. package/dist/signer-factory-B5oFuUZ-.cjs +130 -0
  266. package/dist/signer-factory-BAt8FPNc.cjs +281 -0
  267. package/dist/signer-factory-BGE4nL4k.js +129 -0
  268. package/dist/signer-factory-C4pgsiJi.js +281 -0
  269. package/dist/{signer-factory-DySc1wRX.cjs → signer-factory-D95jQIPV.cjs} +9 -6
  270. package/dist/{signer-factory-D4HrwFPl.js → signer-factory-PrpsGX-8.js} +10 -6
  271. package/dist/{slice-BZTQgYSs.js → slice-B-xJcuT1.js} +4 -4
  272. package/dist/slice-B4QOYaOG.cjs +105 -0
  273. package/dist/{slice-CtIMw2YT.cjs → slice-B9QKuhi0.cjs} +3 -3
  274. package/dist/slice-BKnGkAkd.cjs +9031 -0
  275. package/dist/{slice-Bhwmqn1p.cjs → slice-Bf4enJKZ.cjs} +8 -8
  276. package/dist/{slice-DaNVz7_l.js → slice-ByNWnxKS.js} +8 -8
  277. package/dist/{slice-C2UDQrdt.js → slice-C1zOeseG.js} +449 -355
  278. package/dist/{slice-DFLyKRm5.cjs → slice-C4toRM6A.cjs} +2 -2
  279. package/dist/{slice-Cx4dULDA.cjs → slice-C8YVv4Qn.cjs} +1 -1
  280. package/dist/{slice-jnpOgiSZ.cjs → slice-CaMfviYu.cjs} +3 -3
  281. package/dist/{slice-DFvrwUur.js → slice-CgeRsYRg.js} +6 -6
  282. package/dist/slice-Cwt8jShq.js +9013 -0
  283. package/dist/{slice-zNEspZND.js → slice-D0VzpMbY.js} +2 -2
  284. package/dist/slice-D14F78Ca.js +87 -0
  285. package/dist/{slice-F6A1YKOF.cjs → slice-DLflttAu.cjs} +479 -366
  286. package/dist/{slice-C96EdeY1.js → slice-DcO-iNz2.js} +1 -1
  287. package/dist/{slice-C19quNWM.cjs → slice-DimtxfAT.cjs} +2 -2
  288. package/dist/{slice-KiNoGwD9.js → slice-DlTEqIoI.js} +1 -1
  289. package/dist/{slice-ws8PccRX.js → slice-Dn0WojD7.js} +3 -3
  290. package/dist/{slice-BLSxLmie.cjs → slice-Dongunf7.cjs} +4 -4
  291. package/dist/{slice-B6vPum19.js → slice-DpufJAjJ.js} +2 -2
  292. package/dist/{slice-exhhmucR.cjs → slice-DtitVc9L.cjs} +7 -7
  293. package/dist/{slice-Bjj2t2ut.cjs → slice-WMMMc1Xv.cjs} +2 -2
  294. package/dist/slice-a8VuIqzS.cjs +378 -0
  295. package/dist/slice-bRASyM-m.js +354 -0
  296. package/dist/{slice-CBFrgHSH.js → slice-hNyKbDry.js} +3 -3
  297. package/dist/{InMemoryKeyAgent-pyB2gA-N.cjs → src-BayYMIjw.cjs} +20 -8228
  298. package/dist/{InMemoryKeyAgent-D-3HejxN.js → src-CgINPlSx.js} +29 -4811
  299. package/dist/store-AUlTfcI_.js +37 -0
  300. package/dist/store-C7AneHEY.cjs +66 -0
  301. package/dist/submit-BuBCVi-l.cjs +87 -0
  302. package/dist/submit-D37lxFrK.js +70 -0
  303. package/dist/submit-bitcoin-tx-DFLnhidO.js +983 -0
  304. package/dist/submit-bitcoin-tx-sih2hSfb.cjs +1049 -0
  305. package/dist/{subtractValueQuantities-DfZg6oiG.js → subtractValueQuantities-BuDJVxq6.js} +3 -22
  306. package/dist/{subtractValueQuantities-C8qDTtgH.cjs → subtractValueQuantities-CqR606HV.cjs} +2 -32
  307. package/dist/{sync-operation-utils-BrQyWFS_.cjs → sync-operation-utils--Z_IQfyW.cjs} +0 -66
  308. package/dist/{sync-operation-utils-CAaCGvEN.js → sync-operation-utils-BimBpvI3.js} +1 -37
  309. package/dist/token-color.vo-CItgoOjw.js +26 -0
  310. package/dist/token-color.vo-Ca4hUXfh.cjs +37 -0
  311. package/dist/token-id-mapper-BVau2RXa.js +16 -0
  312. package/dist/token-id-mapper-CSeLzM4o.cjs +18 -0
  313. package/dist/{token-id-mapper-pDaTxpw2.js → token-id-mapper-ClBeIdJR.js} +2 -2
  314. package/dist/{token-id-mapper-DYcd8hhQ.cjs → token-id-mapper-D0wFdbOy.cjs} +2 -2
  315. package/dist/types-BhzypKMS.cjs +15 -0
  316. package/dist/types-DTE1ctDB.js +10 -0
  317. package/dist/types-DXSxXGz0.js +12 -0
  318. package/dist/types-Dls6Z5gV.cjs +23 -0
  319. package/dist/{util-P4PX2muj.cjs → util-D1YlKzrc.cjs} +2 -2
  320. package/dist/{util-CQSJU0wT.js → util-DHxtzUzY.js} +2 -2
  321. package/dist/{utils-Bv4OHT4S.cjs → utils-DYf4iZKX.cjs} +7 -1
  322. package/dist/{utils-DrtzChYN.js → utils-DcnoETEP.js} +2 -2
  323. package/dist/utils-Du0YXRcU.cjs +112 -0
  324. package/dist/utils-fF_rnZe3.js +65 -0
  325. package/dist/v8Signatures-ClGpA8mS.cjs +71 -0
  326. package/dist/{vote-delegation-tx-builder-69gOEkCB.cjs → vote-delegation-tx-builder-BjZxCOd3.cjs} +9 -8
  327. package/dist/{vote-delegation-tx-builder-BRJ_pf-c.js → vote-delegation-tx-builder-DRJwPXkD.js} +9 -7
  328. package/dist/watch-FGE4B_Ge.js +13056 -0
  329. package/dist/watch-imlsRZDp.cjs +52207 -0
  330. package/package.json +95 -3
  331. package/dist/Address-4KjeDt_x.cjs +0 -17269
  332. package/dist/Address-_VPkf7Xh.js +0 -17101
  333. package/dist/account-guards.stable-BKyYPj4z.cjs +0 -10
  334. package/dist/account-guards.stable-BdmQ43vC.js +0 -5
  335. package/dist/init-BR9ZIbED.cjs +0 -378
  336. package/dist/init-B_k1RUN_.cjs +0 -21
  337. package/dist/init-Bs3b5ZQH.js +0 -378
  338. package/dist/init-ChfcrHgG2.cjs +0 -4354
  339. package/dist/init-D1uUMMpG.js +0 -19
  340. package/dist/init-DV1OSZiK.js +0 -4352
  341. package/dist/modules-cardano-BINzpXpk.js +0 -5454
  342. package/dist/modules-cardano-Dt8OwoQb.cjs +0 -5640
  343. package/dist/slice-Caf98m0d2.js +0 -107
  344. package/dist/slice-PM6llajQ2.cjs +0 -125
  345. /package/dist/{BigIntMath-CoJWXt1h.cjs → BigIntMath-CBciEQn2.cjs} +0 -0
  346. /package/dist/{BigIntMath-BWUJOsOW.js → BigIntMath-X3EniYDo.js} +0 -0
  347. /package/dist/{ChainId-sdLmlIRk.cjs → ChainId-CNPSU9nn.cjs} +0 -0
  348. /package/dist/{ChainId-CKbm87yQ.js → ChainId-CeoG8Sov.js} +0 -0
  349. /package/dist/{_polyfill-node.process-4fnlkbBi.cjs → _polyfill-node.process-5UxyjoHG.cjs} +0 -0
  350. /package/dist/{account-id.vo-Xpgnu-av.cjs → account-id.vo-DYZBZQsz.cjs} +0 -0
  351. /package/dist/{account-id.vo-DP0BtOoF.js → account-id.vo-KDTWJ-W5.js} +0 -0
  352. /package/dist/{actions-BNagEO6m.cjs → actions-BnHFVSr_.cjs} +0 -0
  353. /package/dist/{actions-BWTyAr7B.js → actions-Cwj0ApZf.js} +0 -0
  354. /package/dist/{asyncoption-po7-RiA5.cjs → asyncoption-CYcZNBRM.cjs} +0 -0
  355. /package/dist/{asyncoption-CzIVH5Y1.js → asyncoption-DZQGfznq.js} +0 -0
  356. /package/dist/{bignum.vo-DhhZ9HP1.js → bignum.vo-Co6tMH2q.js} +0 -0
  357. /package/dist/{bignum.vo-CpkrIXjH.cjs → bignum.vo-Du-bpvar.cjs} +0 -0
  358. /package/dist/{blockchain-network-id.vo-CbgrAY_K.js → blockchain-network-id.vo-C6Jowh8t.js} +0 -0
  359. /package/dist/{blockchain-network-id.vo-5iC6Kffo.cjs → blockchain-network-id.vo-DEQ14dYj.cjs} +0 -0
  360. /package/dist/{composite-signer-factory-DABpCe1_.js → composite-signer-factory-D9RWY-1V.js} +0 -0
  361. /package/dist/{composite-signer-factory-BizuNPP7.cjs → composite-signer-factory-v4Pxn8bA.cjs} +0 -0
  362. /package/dist/{error-ggOihIfv.cjs → error-B6wj9nPv.cjs} +0 -0
  363. /package/dist/{error-DHKO2iS4.js → error-CvEYL98P.js} +0 -0
  364. /package/dist/{esm-browser-DnXNvpcn.cjs → esm-browser-CZbMeVod.cjs} +0 -0
  365. /package/dist/{esm-browser-BBVQuyQv.js → esm-browser-DpAD_RKv.js} +0 -0
  366. /package/dist/{feature-flag-key.vo-DoGK5Bfh.js → feature-flag-key.vo-9reURsOQ.js} +0 -0
  367. /package/dist/{feature-flag-key.vo-CP1Z9s8e.cjs → feature-flag-key.vo-KTIdnzYX.cjs} +0 -0
  368. /package/dist/{first-state-of-status-Bx4-QQ5m.cjs → first-state-of-status-9ICeDNSa.cjs} +0 -0
  369. /package/dist/{first-state-of-status-D7XzEuSa.js → first-state-of-status-BscbKj_t.js} +0 -0
  370. /package/dist/{init-4319psUJ.cjs → init-BT67pd9x.cjs} +0 -0
  371. /package/dist/{init-AFUM1TSg.js → init-CHk1fvJ2.js} +0 -0
  372. /package/dist/{init-ZX90nEJe2.js → init-DIyNx6DZ.js} +0 -0
  373. /package/dist/{init-CTQ2g6Xt.js → init-DtxxFaCx.js} +0 -0
  374. /package/dist/{init-Dt1Xifg_2.cjs → init-J8QobR93.cjs} +0 -0
  375. /package/dist/{init-BNewLCqw.cjs → init-xzZPlQwB.cjs} +0 -0
  376. /package/dist/{is-redux-persist-state-DxJSsvxB.cjs → is-redux-persist-state-C5luvPEH.cjs} +0 -0
  377. /package/dist/{is-redux-persist-state-Bl2n4VMr.js → is-redux-persist-state-CjcqyCoh.js} +0 -0
  378. /package/dist/{isNotNil-CZxBMfL5.js → isNotNil-Be7gCY0m.js} +0 -0
  379. /package/dist/{isNotNil-C_SO1tcC.cjs → isNotNil-CrYIUFry.cjs} +0 -0
  380. /package/dist/{logging-BvMDKgl0.js → logging-BAMMfn5j.js} +0 -0
  381. /package/dist/{logging-Bh9ikph3.cjs → logging-CbxgIX9V.cjs} +0 -0
  382. /package/dist/{metadatum-B-sxBnSY.cjs → metadatum-BGvFqGPt.cjs} +0 -0
  383. /package/dist/{metadatum-Ego4jVas.js → metadatum-BVocMYy_.js} +0 -0
  384. /package/dist/{repo-slice-BzZQUTj8.cjs → repo-slice-BB_W-6OL.cjs} +0 -0
  385. /package/dist/{repo-slice-vCcQJotS.js → repo-slice-BQlssw9M.js} +0 -0
  386. /package/dist/{reselect-BPkMzw3k.js → reselect-JIgawvPe.js} +0 -0
  387. /package/dist/{serializable-BxFUShKY.js → serializable-F7OtYl8d.js} +0 -0
  388. /package/dist/{serializable-DWgkHhv_.cjs → serializable-n3UoxbZc.cjs} +0 -0
  389. /package/dist/{slice-DY-DNtSi.js → slice-5dGBAz6w.js} +0 -0
  390. /package/dist/{slice-BEFoeqgr.cjs → slice-BzJgg9FS.cjs} +0 -0
  391. /package/dist/{src-CPmX4KUh.cjs → src-CeBNkonz.cjs} +0 -0
  392. /package/dist/{src-XOZXPKxe.js → src-Cv1ifqbe.js} +0 -0
  393. /package/dist/{time-CQPt4FdU.js → time-ByoKevdZ.js} +0 -0
  394. /package/dist/{time-Blvoyh9G.cjs → time-qD7edGU0.cjs} +0 -0
  395. /package/dist/{time.vo-Xln4yGBU.js → time.vo-C17-tYq3.js} +0 -0
  396. /package/dist/{time.vo-rwWwJzmP.cjs → time.vo-TmYE-_Y2.cjs} +0 -0
  397. /package/dist/{toEmpty-CzmoZVLN.cjs → toEmpty-Db6V94Mf.cjs} +0 -0
  398. /package/dist/{toEmpty-DzLd-ZCi.js → toEmpty-Djj-QbpI.js} +0 -0
  399. /package/dist/{token-id.vo-l8fuSpem.cjs → token-id.vo-CZ622zcZ.cjs} +0 -0
  400. /package/dist/{token-id.vo-DMKSaMlc.js → token-id.vo-IDI9C_Qm.js} +0 -0
  401. /package/dist/{types-B61DQoFW.js → types-By-XeXWg.js} +0 -0
  402. /package/dist/{types-CUC-Kcqh.cjs → types-Cn6WlQV6.cjs} +0 -0
  403. /package/dist/{util-BV3iGOrz.cjs → util-Cp1h58uD.cjs} +0 -0
  404. /package/dist/{util-BsQKLorV.js → util-DHppXj7U.js} +0 -0
  405. /package/dist/{util-selector-CKIyoAOz.js → util-selector-BWmHpo92.js} +0 -0
  406. /package/dist/{util-selector-C9H5RsNw.cjs → util-selector-glXkO-jR.cjs} +0 -0
  407. /package/dist/{while-active-CNXeLxre.js → while-active-CDBnUlQA.js} +0 -0
  408. /package/dist/{while-active-BsDWSNrO.cjs → while-active-I2TlHtm7.cjs} +0 -0
@@ -0,0 +1,2489 @@
1
+ const require_Address = require("./Address-AMKTo-dA.cjs");
2
+ const require_fees = require("./fees-DtC71fOx.cjs");
3
+ const require_SodiumBip32Ed25519 = require("./SodiumBip32Ed25519-BaD-kjyx.cjs");
4
+ const require_identity = require("./identity-y41Yle03.cjs");
5
+ const require_hex = require("./hex-BVtOKJki.cjs");
6
+ const require_outputs = require("./outputs-BEM1GPMf.cjs");
7
+ const require_grant_status = require("./grant-status-CQtwjLsA.cjs");
8
+ //#region ../../packages/lib/cardano-custody/src/analysis/transaction-facts.ts
9
+ const outpointKey = ({ txId, index }) => `${txId.toLowerCase()}#${index}`;
10
+ const toTxIn = ({ txId, index }) => ({
11
+ txId,
12
+ index
13
+ });
14
+ /** Byte order of lower-case hex, then index: how the ledger orders inputs. */
15
+ const compareOutpoints = (a, b) => {
16
+ const aId = a.txId.toLowerCase();
17
+ const bId = b.txId.toLowerCase();
18
+ if (aId !== bId) return aId < bId ? -1 : 1;
19
+ return a.index - b.index;
20
+ };
21
+ /**
22
+ * Pairs every input the body names with the caller's resolution. Every
23
+ * input, reference input and collateral input must be resolved exactly once,
24
+ * and no resolution may name an outpoint the body does not.
25
+ */
26
+ const resolveBody = (body, resolvedInputs) => {
27
+ const refusals = [];
28
+ const resolutions = /* @__PURE__ */ new Map();
29
+ const sources = [
30
+ [body.inputs, "inputs"],
31
+ [body.referenceInputs ?? [], "referenceInputs"],
32
+ [body.collaterals ?? [], "collaterals"]
33
+ ];
34
+ const named = new Set(sources.flatMap(([inputs]) => inputs.map(outpointKey)));
35
+ for (const [input, txOut] of resolvedInputs) {
36
+ const key = outpointKey(input);
37
+ const view = require_outputs.viewOfOutput(txOut);
38
+ if (!named.has(key)) refusals.push({
39
+ code: "custody-context-mismatch",
40
+ reason: "resolution-not-in-body",
41
+ input: toTxIn(input)
42
+ });
43
+ else if (resolutions.has(key)) refusals.push({
44
+ code: "custody-context-mismatch",
45
+ reason: "duplicate-resolution",
46
+ input: toTxIn(input)
47
+ });
48
+ else if (view.address === void 0) refusals.push({
49
+ code: "custody-context-mismatch",
50
+ reason: "invalid-resolution",
51
+ input: toTxIn(input)
52
+ });
53
+ resolutions.set(key, view);
54
+ }
55
+ const resolve = (inputs, source) => inputs.flatMap((input) => {
56
+ const output = resolutions.get(outpointKey(input));
57
+ if (output) return [{
58
+ input: toTxIn(input),
59
+ output
60
+ }];
61
+ refusals.push({
62
+ code: "custody-context-mismatch",
63
+ reason: "unresolved-input",
64
+ input: toTxIn(input),
65
+ source
66
+ });
67
+ return [];
68
+ });
69
+ const [inputs, referenceInputs, collaterals] = sources.map(([list, source]) => resolve(list, source));
70
+ return refusals.length > 0 ? require_hex.fail(refusals) : require_hex.ok({
71
+ inputs: inputs.sort((a, b) => compareOutpoints(a.input, b.input)),
72
+ referenceInputs,
73
+ collaterals
74
+ });
75
+ };
76
+ /** The key of the withdrawal map in a transaction body. */
77
+ const WITHDRAWALS_KEY = 5n;
78
+ /**
79
+ * The reward accounts the withdrawal maps of a body name, read from its
80
+ * bytes, repeats included: a repeated key, and a repeated map, are lost
81
+ * once the body is decoded.
82
+ */
83
+ const withdrawalAccountsOf = (bodyCbor) => {
84
+ const reader = new require_Address.CborReader(bodyCbor);
85
+ const accounts = [];
86
+ reader.readStartMap();
87
+ while (reader.peekState() !== require_Address.CborReaderState.EndMap) {
88
+ if (reader.readInt() !== WITHDRAWALS_KEY) {
89
+ reader.skipValue();
90
+ continue;
91
+ }
92
+ reader.readStartMap();
93
+ while (reader.peekState() !== require_Address.CborReaderState.EndMap) {
94
+ accounts.push(require_Address.Address.fromBytes(require_Address.HexBlob.fromBytes(reader.readByteString())).toBech32());
95
+ reader.skipValue();
96
+ }
97
+ reader.readEndMap();
98
+ }
99
+ return accounts;
100
+ };
101
+ const readTransaction = ({ body, bodyCbor, witness }, resolved, options) => {
102
+ const mint = new Map([...body.mint ?? /* @__PURE__ */ new Map()].map(([assetId, quantity]) => [assetId.toLowerCase(), quantity]));
103
+ const { invalidBefore, invalidHereafter } = body.validityInterval ?? {};
104
+ return {
105
+ body,
106
+ ...resolved,
107
+ outputs: body.outputs.map(require_outputs.viewOfOutput),
108
+ collateralReturn: body.collateralReturn ? require_outputs.viewOfOutput(body.collateralReturn) : void 0,
109
+ redeemers: witness.redeemers ?? [],
110
+ requiredSigners: new Set((body.requiredExtraSignatures ?? []).map((signer) => signer.toLowerCase())),
111
+ mintPolicies: [...new Set([...mint.keys()].map(require_outputs.policyIdOf))].sort(),
112
+ mint,
113
+ lowerBoundMs: invalidBefore === void 0 ? void 0 : options.slotToPosixMs(invalidBefore),
114
+ upperBoundMs: invalidHereafter === void 0 ? void 0 : options.slotToPosixMs(invalidHereafter),
115
+ witnessScriptHashes: new Set((witness.scripts ?? []).map((script) => require_Address.Script.fromCore(script).hash())),
116
+ withdrawalAccounts: withdrawalAccountsOf(bodyCbor)
117
+ };
118
+ };
119
+ const redeemerData = (facts, purpose, index) => facts.redeemers.find((redeemer) => redeemer.purpose === purpose && redeemer.index === index)?.data;
120
+ const paymentCredentialOf = (view) => (view.address?.asBase() ?? view.address?.asEnterprise() ?? view.address?.asPointer())?.getPaymentCredential();
121
+ const stakeCredentialOf = (certificate) => "stakeCredential" in certificate ? certificate.stakeCredential : void 0;
122
+ const scriptHashOfRewardAccount = (rewardAccount) => {
123
+ const credential = require_Address.Address.fromString(rewardAccount)?.asReward()?.getPaymentCredential();
124
+ return credential?.type === require_Address.CredentialType.ScriptHash ? credential.hash : void 0;
125
+ };
126
+ //#endregion
127
+ //#region ../../packages/lib/cardano-custody/src/analysis/grant-spend.ts
128
+ const valueAt = (views, plutusAddressKey) => require_outputs.addValueDeltas(views.filter((view) => view.plutusAddressKey !== void 0 && view.plutusAddressKey === plutusAddressKey).map((view) => view.value));
129
+ /** `leaving_value` over outputs that are already read. */
130
+ const leavingValueAt = (inputs, outputs, plutusAddressKey) => require_outputs.addValueDeltas([valueAt(inputs, plutusAddressKey), require_outputs.negateValueDelta(valueAt(outputs, plutusAddressKey))]);
131
+ /**
132
+ * Every rule of `stays_within_scope` a net outflow breaks: the scoped asset
133
+ * within its per call and remaining caps; for a token scope, lovelace within
134
+ * both lovelace caps; and no other asset class leaving. A net inflow breaks
135
+ * nothing.
136
+ *
137
+ * @param accountFee - the fee the account pays, which `leaving.lovelace`
138
+ * already includes; each violation whose outflow is lovelace reports it
139
+ * as `fee`
140
+ */
141
+ const findCustodyScopeViolations = (scope, leaving, accountFee) => {
142
+ const violations = [];
143
+ const isLovelaceScope = require_fees.isCustodyLovelaceAsset(scope.asset);
144
+ const lovelaceFee = accountFee === void 0 ? {} : { fee: accountFee };
145
+ const assetFee = isLovelaceScope ? lovelaceFee : {};
146
+ const assetOutflow = require_outputs.quantityOfAsset(leaving, scope.asset);
147
+ if (assetOutflow > scope.perCallCap) violations.push({
148
+ code: "per-call-cap-exceeded",
149
+ outflow: assetOutflow,
150
+ limit: scope.perCallCap,
151
+ ...assetFee
152
+ });
153
+ if (assetOutflow > scope.cap) violations.push({
154
+ code: "cap-exceeded",
155
+ outflow: assetOutflow,
156
+ limit: scope.cap,
157
+ ...assetFee
158
+ });
159
+ if (!isLovelaceScope) {
160
+ if (leaving.lovelace > scope.lovelacePerCallCap) violations.push({
161
+ code: "lovelace-per-call-cap-exceeded",
162
+ outflow: leaving.lovelace,
163
+ limit: scope.lovelacePerCallCap,
164
+ ...lovelaceFee
165
+ });
166
+ if (leaving.lovelace > scope.lovelaceCap) violations.push({
167
+ code: "lovelace-cap-exceeded",
168
+ outflow: leaving.lovelace,
169
+ limit: scope.lovelaceCap,
170
+ ...lovelaceFee
171
+ });
172
+ }
173
+ for (const [assetId, quantity] of leaving.assets) if (quantity > 0n && !require_outputs.isAssetIdOf(assetId, scope.asset)) violations.push({
174
+ code: "foreign-asset-outflow",
175
+ assetId,
176
+ outflow: quantity
177
+ });
178
+ return violations;
179
+ };
180
+ const clampedOutflow = (quantity) => quantity > 0n ? quantity : 0n;
181
+ /**
182
+ * `grant_after_spend`: the grant with `cap`, and for a token grant
183
+ * `lovelaceCap`, lowered by the net outflow of its asset, never raised by a
184
+ * net deposit. These are the most the recreated grant may keep: the
185
+ * contract accepts any cap from zero to this value, so a builder may write
186
+ * lower caps that already count a fee it has not yet computed.
187
+ */
188
+ const custodyGrantAfterSpend = (grant, leaving) => {
189
+ const { scope } = grant;
190
+ return {
191
+ ...grant,
192
+ scope: {
193
+ ...scope,
194
+ cap: scope.cap - clampedOutflow(require_outputs.quantityOfAsset(leaving, scope.asset)),
195
+ lovelaceCap: require_fees.isCustodyLovelaceAsset(scope.asset) ? scope.lovelaceCap : scope.lovelaceCap - clampedOutflow(leaving.lovelace)
196
+ }
197
+ };
198
+ };
199
+ /** `ends_before_expiry`: a finite upper bound no later than the expiry. */
200
+ const findExpiryViolation = (upperBoundMs, expiresAt) => {
201
+ if (upperBoundMs === void 0) return { code: "no-upper-bound" };
202
+ return Number.isSafeInteger(upperBoundMs) && BigInt(upperBoundMs) <= expiresAt ? void 0 : {
203
+ code: "expired",
204
+ upperBoundMs,
205
+ expiresAt
206
+ };
207
+ };
208
+ const findMisplacedAccountTokens = (facts) => [["input", facts.inputs], ["output", facts.outputs]].flatMap(([kind, views]) => views.flatMap((view, index) => require_outputs.holdsMisplacedAccountToken(view, [facts.account.accountValidatorHash]) ? [{
209
+ code: "account-token-misplaced",
210
+ location: {
211
+ kind,
212
+ index
213
+ }
214
+ }] : []));
215
+ /** `pays_only_recipients` and `deposits_are_plain`, per output. */
216
+ const findOutputViolations = (facts, grant) => {
217
+ const { recipients } = grant.scope;
218
+ const recipientKeys = new Set(recipients.map(require_fees.recipientKey));
219
+ const { plutusAddressKey } = facts.grantInput;
220
+ return facts.outputs.flatMap((view, outputIndex) => {
221
+ const violations = [];
222
+ const isBack = view.plutusAddressKey !== void 0 && view.plutusAddressKey === plutusAddressKey;
223
+ if (recipients.length > 0 && !isBack && (view.plutusAddressKey === void 0 || !recipientKeys.has(view.plutusAddressKey))) violations.push({
224
+ code: "recipient-not-allowed",
225
+ outputIndex
226
+ });
227
+ if (isBack && !require_outputs.holdsAccountToken(view, facts.account) && (view.datumKind !== "none" || view.hasReferenceScript)) violations.push({
228
+ code: "deposit-not-plain",
229
+ outputIndex
230
+ });
231
+ return violations;
232
+ });
233
+ };
234
+ /** `referenced_control`: the account's control UTxO among the reference inputs, exactly once. */
235
+ const findReferencedControl = (facts) => {
236
+ const controls = facts.referenceInputs.filter((view) => require_outputs.isControlOutput(view, facts.account));
237
+ if (controls.length !== 1) return { violations: [{
238
+ code: "referenced-control-count",
239
+ count: controls.length
240
+ }] };
241
+ const state = require_outputs.decodedInlineState(controls[0]);
242
+ return state ? {
243
+ state,
244
+ violations: []
245
+ } : { violations: [{ code: "referenced-control-datum" }] };
246
+ };
247
+ /** `is_current`, naming why a grant is not current against the referenced state. */
248
+ const findCurrencyViolation = (grant, state) => {
249
+ if (grant.generation < state.grantGeneration) return {
250
+ code: "grant-not-current",
251
+ liveness: "superseded"
252
+ };
253
+ if (grant.generation > state.grantGeneration) return {
254
+ code: "grant-not-current",
255
+ liveness: "future-generation"
256
+ };
257
+ return state.revoked.includes(grant.slot) ? {
258
+ code: "grant-not-current",
259
+ liveness: "revoked"
260
+ } : void 0;
261
+ };
262
+ const isSameExceptCaps = (actual, expected) => {
263
+ const a = actual.scope;
264
+ const b = expected.scope;
265
+ return actual.slot === expected.slot && actual.grantee === expected.grantee && actual.generation === expected.generation && a.asset.policyId === b.asset.policyId && a.asset.assetName === b.asset.assetName && a.perCallCap === b.perCallCap && a.lovelacePerCallCap === b.lovelacePerCallCap && a.expiresAt === b.expiresAt && a.recipients.length === b.recipients.length && a.recipients.every((recipient, index) => require_fees.recipientKey(recipient) === require_fees.recipientKey(b.recipients[index]));
266
+ };
267
+ const isSameValue = (a, b) => a.lovelace === b.lovelace && a.assets.size === b.assets.size && [...a.assets].every(([assetId, quantity]) => b.assets.get(assetId) === quantity);
268
+ /**
269
+ * `find_grant_output` and the checks on it: one output holding the grant
270
+ * token, at the spent UTxO's address with its exact value and no reference
271
+ * script, carrying the grant with every field kept but caps lowered within
272
+ * `carries_grant_within`.
273
+ */
274
+ const findGrantOutputViolations = (facts, grant, expected) => {
275
+ const assetId = require_outputs.grantAssetIdOf(facts.account, grant.slot);
276
+ const holders = facts.outputs.flatMap((view, outputIndex) => assetId !== void 0 && view.value.assets.get(assetId) === 1n ? [{
277
+ view,
278
+ outputIndex
279
+ }] : []);
280
+ if (holders.length !== 1) return [{
281
+ code: "grant-output-count",
282
+ count: holders.length
283
+ }];
284
+ const [{ view, outputIndex }] = holders;
285
+ const violations = [];
286
+ if (view.plutusAddressKey === void 0 || view.plutusAddressKey !== facts.grantInput.plutusAddressKey) violations.push({
287
+ code: "grant-output-address",
288
+ outputIndex
289
+ });
290
+ if (!isSameValue(view.value, facts.grantInput.value)) violations.push({
291
+ code: "grant-output-value",
292
+ outputIndex
293
+ });
294
+ if (view.hasReferenceScript) violations.push({
295
+ code: "grant-output-reference-script",
296
+ outputIndex
297
+ });
298
+ if (view.inlineDatum === void 0) return [...violations, {
299
+ code: "grant-output-datum",
300
+ outputIndex,
301
+ reason: "not-inline"
302
+ }];
303
+ const actual = require_outputs.decodedInlineGrant(view);
304
+ if (!actual) return [...violations, {
305
+ code: "grant-output-datum",
306
+ outputIndex,
307
+ reason: "malformed"
308
+ }];
309
+ if (!isSameExceptCaps(actual, expected)) violations.push({
310
+ code: "grant-output-datum",
311
+ outputIndex,
312
+ reason: "field-changed"
313
+ });
314
+ for (const field of ["cap", "lovelaceCap"]) {
315
+ const capActual = actual.scope[field];
316
+ const capExpected = expected.scope[field];
317
+ if (capActual < 0n || capActual > capExpected) violations.push({
318
+ code: "grant-output-cap",
319
+ outputIndex,
320
+ field,
321
+ actual: capActual,
322
+ expected: capExpected
323
+ });
324
+ }
325
+ return violations;
326
+ };
327
+ /** Every violation of a grant spend whose views are already read. */
328
+ const grantSpendViolationsOf = (facts) => {
329
+ const grant = require_outputs.decodedInlineGrant(facts.grantInput);
330
+ if (!grant) return [{ code: "grant-datum-invalid" }];
331
+ const violations = [];
332
+ if (!require_outputs.holdsOnlyLovelaceAndGrantToken(facts.grantInput.value, facts.account, grant.slot)) violations.push({ code: "grant-input-value" });
333
+ const tokenInputs = facts.inputs.filter((view) => require_outputs.holdsAccountToken(view, facts.account)).length;
334
+ if (tokenInputs !== 1) violations.push({
335
+ code: "account-token-inputs",
336
+ count: tokenInputs
337
+ });
338
+ const control = findReferencedControl(facts);
339
+ violations.push(...control.violations);
340
+ const currency = control.state && findCurrencyViolation(grant, control.state);
341
+ if (currency) violations.push(currency);
342
+ if (!facts.requiredSigners.has(grant.grantee)) violations.push({
343
+ code: "grantee-not-signer",
344
+ grantee: grant.grantee
345
+ });
346
+ const expiry = findExpiryViolation(facts.upperBoundMs, grant.scope.expiresAt);
347
+ if (expiry) violations.push(expiry);
348
+ const leaving = leavingValueAt(facts.inputs, facts.outputs, facts.grantInput.plutusAddressKey);
349
+ return [
350
+ ...violations,
351
+ ...findCustodyScopeViolations(grant.scope, leaving),
352
+ ...findOutputViolations(facts, grant),
353
+ ...findMisplacedAccountTokens(facts),
354
+ ...findGrantOutputViolations(facts, grant, custodyGrantAfterSpend(grant, leaving)),
355
+ ...[...facts.mint].filter(([assetId]) => require_outputs.policyIdOf(assetId) === facts.account.accountValidatorHash).map(([assetId, quantity]) => ({
356
+ code: "mint-under-policy",
357
+ assetId,
358
+ quantity
359
+ }))
360
+ ];
361
+ };
362
+ //#endregion
363
+ //#region ../../packages/lib/cardano-custody/src/record.ts
364
+ const CUSTODY_ACCOUNT_RECORD_TYPE = "cardano-custody-account";
365
+ const createCustodyAccountRecord = (identity) => canonicalCustodyAccountRecord({
366
+ type: CUSTODY_ACCOUNT_RECORD_TYPE,
367
+ version: 1,
368
+ networkMagic: identity.chainId.networkMagic,
369
+ accountValidatorHash: identity.contractVersion.accountValidatorHash,
370
+ creatorKeyHash: identity.creatorKeyHash,
371
+ stakeScriptHash: identity.stakeScriptHash,
372
+ address: identity.address
373
+ });
374
+ /** A copy holding exactly the record fields, in their canonical order. */
375
+ const canonicalCustodyAccountRecord = (record) => ({
376
+ type: record.type,
377
+ version: record.version,
378
+ networkMagic: record.networkMagic,
379
+ accountValidatorHash: record.accountValidatorHash,
380
+ creatorKeyHash: record.creatorKeyHash,
381
+ stakeScriptHash: record.stakeScriptHash,
382
+ address: record.address
383
+ });
384
+ const RECORD_FIELDS = Object.keys(canonicalCustodyAccountRecord({}));
385
+ const UPSTREAM_RECORD_FIELDS = [
386
+ "owner",
387
+ "stakeScriptHash",
388
+ "address"
389
+ ];
390
+ const malformed$1 = (reason, field) => ({
391
+ step: 1,
392
+ code: "malformed-record",
393
+ reason,
394
+ ...field === void 0 ? {} : { field }
395
+ });
396
+ const parseJson = (input) => {
397
+ if (typeof input !== "string") return input;
398
+ try {
399
+ return JSON.parse(input);
400
+ } catch {
401
+ return;
402
+ }
403
+ };
404
+ const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
405
+ const hasExactFields$1 = (value, fields) => {
406
+ const keys = Object.keys(value);
407
+ return keys.length === fields.length && fields.every((field) => keys.includes(field));
408
+ };
409
+ const parseAddress = (value) => typeof value === "string" && require_Address.Address.isValidBech32(value) ? require_Address.Address.fromBech32(value) : null;
410
+ const HASH_FIELDS = [
411
+ "accountValidatorHash",
412
+ "creatorKeyHash",
413
+ "stakeScriptHash"
414
+ ];
415
+ const readRecordClaims = (value) => {
416
+ if (!hasExactFields$1(value, RECORD_FIELDS)) return require_hex.fail(malformed$1(`expected exactly ${RECORD_FIELDS.join(", ")}`));
417
+ if (value.type !== "cardano-custody-account") return require_hex.fail(malformed$1("unknown record type", "type"));
418
+ if (value.version !== 1) return require_hex.fail(malformed$1("unsupported record version", "version"));
419
+ if (!require_identity.isNetworkMagic(value.networkMagic)) return require_hex.fail(malformed$1("expected a network magic", "networkMagic"));
420
+ const invalidHash = HASH_FIELDS.find((field) => !require_hex.isHash28Hex(value[field]));
421
+ if (invalidHash) return require_hex.fail(malformed$1("expected 28 bytes of lower-case hex", invalidHash));
422
+ const address = parseAddress(value.address);
423
+ if (!address) return require_hex.fail(malformed$1("expected an address", "address"));
424
+ return require_hex.ok({
425
+ networkMagic: value.networkMagic,
426
+ accountValidatorHash: value.accountValidatorHash,
427
+ creatorKeyHash: value.creatorKeyHash,
428
+ stakeScriptHash: value.stakeScriptHash,
429
+ address,
430
+ addressText: value.address
431
+ });
432
+ };
433
+ /**
434
+ * Claims of the upstream `{ owner, stakeScriptHash, address }` record. It
435
+ * names no network or contract version: the network is the selected one, and
436
+ * the account validator is the address's payment script.
437
+ */
438
+ const readUpstreamRecordClaims = (value, selectedNetworkMagic) => {
439
+ if (!hasExactFields$1(value, UPSTREAM_RECORD_FIELDS)) return require_hex.fail(malformed$1(`expected exactly ${UPSTREAM_RECORD_FIELDS.join(", ")}`));
440
+ const invalidHash = ["owner", "stakeScriptHash"].find((field) => !require_hex.isHash28Hex(value[field]));
441
+ if (invalidHash) return require_hex.fail(malformed$1("expected 28 bytes of lower-case hex", invalidHash));
442
+ const address = parseAddress(value.address);
443
+ const payment = address?.asBase()?.getPaymentCredential();
444
+ if (!address || payment?.type !== require_Address.CredentialType.ScriptHash) return require_hex.fail(malformed$1("expected a script base address", "address"));
445
+ return require_hex.ok({
446
+ networkMagic: selectedNetworkMagic,
447
+ accountValidatorHash: payment.hash,
448
+ creatorKeyHash: value.owner,
449
+ stakeScriptHash: value.stakeScriptHash,
450
+ address,
451
+ addressText: value.address
452
+ });
453
+ };
454
+ const readClaims = (input, selectedNetworkMagic) => {
455
+ const value = parseJson(input);
456
+ if (!isPlainObject$1(value)) return require_hex.fail(malformed$1("expected a JSON object"));
457
+ return "owner" in value ? readUpstreamRecordClaims(value, selectedNetworkMagic) : readRecordClaims(value);
458
+ };
459
+ /**
460
+ * Validates an imported record, in order: (1) known type, version and field
461
+ * shapes; (2) a supported contract version; (3) the stake script hash
462
+ * re-derived from the creator key hash; (4) the address re-derived for the
463
+ * record's network, byte for byte; (5) the record's network is the selected
464
+ * one. Step 3 is what proves the record names a genuine custody account: the
465
+ * mint accepts any stake script.
466
+ *
467
+ * Also accepts the upstream `{ owner, stakeScriptHash, address }` record,
468
+ * taking the network from `selectedNetworkMagic`.
469
+ *
470
+ * @param input - the record as JSON text or as an already parsed value
471
+ */
472
+ const parseCustodyAccountRecord = (input, selectedNetworkMagic) => {
473
+ const claims = readClaims(input, selectedNetworkMagic);
474
+ if (!claims.ok) return claims;
475
+ const { networkMagic, accountValidatorHash, creatorKeyHash, stakeScriptHash, address, addressText } = claims.value;
476
+ const contractVersion = require_hex.findCustodyContractVersion(accountValidatorHash);
477
+ if (!contractVersion.ok) return require_hex.fail({
478
+ step: 2,
479
+ ...contractVersion.error
480
+ });
481
+ const identity = require_identity.identityOfValidatedCreator({
482
+ contractVersion: contractVersion.value,
483
+ creatorKeyHash,
484
+ chainId: require_identity.chainIdFromNetworkMagic(networkMagic)
485
+ });
486
+ if (identity.stakeScriptHash !== stakeScriptHash) return require_hex.fail({
487
+ step: 3,
488
+ code: "stake-script-hash-mismatch",
489
+ expected: identity.stakeScriptHash,
490
+ actual: stakeScriptHash
491
+ });
492
+ const derivedAddress = identity.address;
493
+ if (require_Address.Address.fromBech32(derivedAddress).toBytes() !== address.toBytes()) return require_hex.fail({
494
+ step: 4,
495
+ code: "address-mismatch",
496
+ expected: derivedAddress,
497
+ actual: addressText
498
+ });
499
+ if (networkMagic !== selectedNetworkMagic) return require_hex.fail({
500
+ step: 5,
501
+ code: "network-mismatch",
502
+ expected: selectedNetworkMagic,
503
+ actual: networkMagic
504
+ });
505
+ return require_hex.ok({
506
+ record: createCustodyAccountRecord(identity),
507
+ identity
508
+ });
509
+ };
510
+ //#endregion
511
+ //#region ../../packages/lib/cardano-custody/src/codecs/redeemers.ts
512
+ /** Constructor order of `AccountRedeemer`; no constructor has fields. */
513
+ const ACCOUNT_REDEEMER_KINDS = [
514
+ "device",
515
+ "spendWithGrant",
516
+ "sweepGrant",
517
+ "fund"
518
+ ];
519
+ /** Constructor order of `MintRedeemer`; no constructor has fields. */
520
+ const MINT_REDEEMER_KINDS = [
521
+ "createAccount",
522
+ "issueGrants",
523
+ "burnGrants"
524
+ ];
525
+ /** Decodes a union whose constructors all have no fields. */
526
+ const decodeFieldless = (data, kinds, what) => require_fees.decodeResult(() => {
527
+ const index = require_fees.expectConstrIndex(data, "", what);
528
+ const kind = kinds[Number(index)];
529
+ if (kind === void 0) return require_fees.unknownAlternative("", `${what} of ${kinds.join(", ")}`, index);
530
+ require_fees.expectConstr(data, {
531
+ index: Number(index),
532
+ arity: 0
533
+ }, {
534
+ path: "",
535
+ what
536
+ });
537
+ return { kind };
538
+ });
539
+ /** `Device` is constructor 0, `SpendWithGrant` 1, `SweepGrant` 2 and `Fund` 3. */
540
+ const encodeCustodyAccountRedeemer = ({ kind }) => require_fees.constr(ACCOUNT_REDEEMER_KINDS.indexOf(kind));
541
+ const decodeCustodyAccountRedeemer = (data) => decodeFieldless(data, ACCOUNT_REDEEMER_KINDS, "an account redeemer");
542
+ /** `CreateAccount` is constructor 0, `IssueGrants` 1 and `BurnGrants` 2. */
543
+ const encodeCustodyMintRedeemer = ({ kind }) => require_fees.constr(MINT_REDEEMER_KINDS.indexOf(kind));
544
+ const decodeCustodyMintRedeemer = (data) => decodeFieldless(data, MINT_REDEEMER_KINDS, "a mint redeemer");
545
+ /** `Operate`: constructor 0 with no fields. */
546
+ const encodeCustodyStakeRedeemer = () => require_fees.constr(0);
547
+ /**
548
+ * `Run`, the redeemer of a logic script's withdraw and publish handlers:
549
+ * constructor 0 with no fields.
550
+ */
551
+ const encodeCustodyLogicRedeemer = () => require_fees.constr(0);
552
+ //#endregion
553
+ //#region ../../packages/lib/cardano-custody/src/analysis/fee-shares.ts
554
+ const lovelaceOf = (items) => items.reduce((total, { value }) => total + value.coins, 0n);
555
+ const positivePart = (quantity) => quantity > 0n ? quantity : 0n;
556
+ /** How the fee splits between the external inputs and the operated accounts. */
557
+ const feeSharesOf = ({ externalInputs, externalOutputs, fee }) => {
558
+ const payers = new Set(externalInputs.map(({ address }) => address));
559
+ const beyondChange = lovelaceOf(externalInputs) - lovelaceOf(externalOutputs.filter(({ address }) => payers.has(address)));
560
+ const externalShare = beyondChange < fee ? positivePart(beyondChange) : fee;
561
+ return {
562
+ payers: [...payers],
563
+ externalShare,
564
+ accountShare: fee - externalShare
565
+ };
566
+ };
567
+ /**
568
+ * The part of the accounts' fee share each operated account pays, from the
569
+ * lovelace each gives up. Each pays only what the others cannot cover, so an
570
+ * account is named as paying someone whenever some split of the fee has it
571
+ * do so. A single account pays the whole share.
572
+ */
573
+ const accountFeeSharesOf = (givenUp, accountShare) => {
574
+ const total = givenUp.reduce((sum, lovelace) => sum + positivePart(lovelace), 0n);
575
+ return givenUp.map((lovelace) => positivePart(accountShare - (total - positivePart(lovelace))));
576
+ };
577
+ //#endregion
578
+ //#region ../../packages/lib/cardano-custody/src/analysis/state-change.ts
579
+ const isSameOrder = (before, after) => {
580
+ const kept = before.filter((item) => after.includes(item));
581
+ const keptAfter = after.filter((item) => before.includes(item));
582
+ return kept.every((item, index) => item === keptAfter[index]);
583
+ };
584
+ const counterChange = (before, after) => before === after ? void 0 : {
585
+ before,
586
+ after
587
+ };
588
+ const diffCustodyStates = (before, after) => {
589
+ const generation = counterChange(before.grantGeneration, after.grantGeneration);
590
+ const nextSlot = counterChange(before.nextSlot, after.nextSlot);
591
+ const outstanding = counterChange(before.outstanding, after.outstanding);
592
+ return {
593
+ before,
594
+ after,
595
+ ...before.logic === after.logic ? {} : { logic: {
596
+ before: before.logic,
597
+ after: after.logic
598
+ } },
599
+ devicesAdded: after.devices.filter((device) => !before.devices.includes(device)),
600
+ devicesRemoved: before.devices.filter((device) => !after.devices.includes(device)),
601
+ revokedAdded: after.revoked.filter((slot) => !before.revoked.includes(slot)),
602
+ revokedRemoved: before.revoked.filter((slot) => !after.revoked.includes(slot)),
603
+ ...generation ? { grantGeneration: generation } : {},
604
+ ...nextSlot ? { nextSlot } : {},
605
+ ...outstanding ? { outstanding } : {},
606
+ isReordered: !isSameOrder(before.devices, after.devices) || !isSameOrder(before.revoked, after.revoked)
607
+ };
608
+ };
609
+ //#endregion
610
+ //#region ../../packages/lib/cardano-custody/src/analysis/account-inspection.ts
611
+ const accountKey = (account) => `${account.accountValidatorHash}${account.stakeScriptHash}`;
612
+ const hashesOf = (account) => ({
613
+ accountValidatorHash: account.accountValidatorHash,
614
+ stakeScriptHash: account.stakeScriptHash
615
+ });
616
+ const versionOfPolicy = (policyId) => require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS.find((version) => version.accountValidatorHash === policyId);
617
+ /**
618
+ * Every custody account the transaction names: by an input, reference input
619
+ * or output at its address, by a mint of one of its account tokens, or by
620
+ * the record.
621
+ */
622
+ const findCandidates = (facts, record) => {
623
+ const candidates = /* @__PURE__ */ new Map();
624
+ const add = (candidate) => candidates.set(accountKey(candidate), candidate);
625
+ const views = [
626
+ ...facts.inputs.map(({ output }) => output),
627
+ ...facts.referenceInputs.map(({ output }) => output),
628
+ ...facts.outputs
629
+ ];
630
+ for (const { addressClass } of views) if (addressClass.kind === "account") add({
631
+ contractVersion: addressClass.contractVersion,
632
+ accountValidatorHash: addressClass.contractVersion.accountValidatorHash,
633
+ stakeScriptHash: addressClass.stakeScriptHash
634
+ });
635
+ for (const assetId of facts.mint.keys()) {
636
+ const contractVersion = versionOfPolicy(require_outputs.policyIdOf(assetId));
637
+ const name = require_fees.parseCustodyAccountTokenName(require_outputs.assetNameOf(assetId));
638
+ if (contractVersion && name) add({
639
+ contractVersion,
640
+ accountValidatorHash: contractVersion.accountValidatorHash,
641
+ stakeScriptHash: name.stakeScriptHash
642
+ });
643
+ }
644
+ if (record) add({
645
+ contractVersion: record.contractVersion,
646
+ accountValidatorHash: record.contractVersion.accountValidatorHash,
647
+ stakeScriptHash: record.stakeScriptHash
648
+ });
649
+ return [...candidates.values()];
650
+ };
651
+ const VOTE_DELEGATIONS = new Set([
652
+ require_Address.CertificateType.StakeRegistrationDelegation,
653
+ require_Address.CertificateType.StakeVoteDelegation,
654
+ require_Address.CertificateType.StakeVoteRegistrationDelegation,
655
+ require_Address.CertificateType.VoteDelegation,
656
+ require_Address.CertificateType.VoteRegistrationDelegation
657
+ ]);
658
+ /**
659
+ * The certificates on the account's stake credential and the withdrawal
660
+ * from its reward account, apart from the logic run, which withdraws from
661
+ * the logic's credential. Registration with a deposit and pool delegation
662
+ * are what Lace builds; vote and combined delegations are valid on chain
663
+ * but unsupported, and every other certificate is refused.
664
+ */
665
+ const readStakeEffects = (account, facts) => {
666
+ const refusals = [];
667
+ const certificates = [];
668
+ let isStaked = false;
669
+ for (const [certificateIndex, certificate] of (facts.body.certificates ?? []).entries()) {
670
+ const credential = stakeCredentialOf(certificate);
671
+ if (credential?.type !== require_Address.CredentialType.ScriptHash || credential.hash !== account.stakeScriptHash) continue;
672
+ isStaked = true;
673
+ if (certificate.__typename === require_Address.CertificateType.Registration) certificates.push({
674
+ certificateIndex,
675
+ kind: "registration",
676
+ deposit: certificate.deposit
677
+ });
678
+ else if (certificate.__typename === require_Address.CertificateType.StakeDelegation) certificates.push({
679
+ certificateIndex,
680
+ kind: "pool-delegation",
681
+ poolId: certificate.poolId
682
+ });
683
+ else refusals.push({
684
+ code: "custody-unsupported-operation",
685
+ reason: VOTE_DELEGATIONS.has(certificate.__typename) ? "unsupported-certificate" : "refused-certificate",
686
+ certificateIndex,
687
+ certificateType: certificate.__typename
688
+ });
689
+ }
690
+ const withdrawal = (facts.body.withdrawals ?? []).find(({ stakeAddress }) => scriptHashOfRewardAccount(stakeAddress) === account.stakeScriptHash);
691
+ return {
692
+ certificates,
693
+ withdrawal: withdrawal?.quantity,
694
+ refusals,
695
+ isStaked: isStaked || withdrawal !== void 0
696
+ };
697
+ };
698
+ /**
699
+ * The run of the logic the account operates under: the logic its spent
700
+ * control UTxO names, the referenced one for a grant spend, or the control
701
+ * output's at creation. The proxy requires a withdrawal from that logic's
702
+ * stake credential, which makes the ledger run it. The decoded body keeps
703
+ * one withdrawal per reward account; a repeat in the bytes is refused.
704
+ */
705
+ const readLogicRun = (account, facts, logic) => {
706
+ if (logic === void 0) return { refusals: [] };
707
+ const withdrawal = (facts.body.withdrawals ?? []).find(({ stakeAddress }) => scriptHashOfRewardAccount(stakeAddress) === logic);
708
+ return withdrawal ? {
709
+ run: {
710
+ logic,
711
+ draw: withdrawal.quantity
712
+ },
713
+ refusals: []
714
+ } : { refusals: [{
715
+ code: "custody-unsupported-operation",
716
+ reason: "logic-withdrawal-missing",
717
+ account,
718
+ logic
719
+ }] };
720
+ };
721
+ /**
722
+ * A device whose key hash is not 28 bytes can never sign, and the contract
723
+ * does not check key lengths, so an on-chain state may keep one. Lace never
724
+ * adds one, but a device can still remove it in a rewrite that keeps it
725
+ * meanwhile.
726
+ */
727
+ const isCarriedOver = (violation, before) => before !== void 0 && violation.code === "invalid-device-key-hash" && before.devices.includes(violation.device);
728
+ /** The authoring checks a state written by a device or a creation must pass. */
729
+ const checkWrittenState = ({ account, before, after }) => require_fees.findCustodyStateViolations(after).filter((violation) => !isCarriedOver(violation, before)).map((violation) => ({
730
+ code: "custody-malformed-datum",
731
+ reason: "state-violation",
732
+ account,
733
+ violation
734
+ }));
735
+ const isSameAccount = (account, record) => record !== void 0 && record.contractVersion.accountValidatorHash === account.accountValidatorHash && record.stakeScriptHash === account.stakeScriptHash;
736
+ /**
737
+ * Lace creates only the creator-only state: the current logic of the
738
+ * account's contract version, the creator as the single device, generation
739
+ * zero and no grant issued, revoked or outstanding. The logic also accepts
740
+ * any generation at creation.
741
+ */
742
+ const isCreatorOnly = (state, creator, logic) => state.logic === logic && state.devices.length === 1 && state.devices[0] === creator && state.grantGeneration === 0n && state.nextSlot === 0n && state.revoked.length === 0 && state.outstanding === 0n;
743
+ /**
744
+ * At creation the stake validator needs the creator's signature, and Lace
745
+ * creates only the creator-only state; otherwise a device listed in the
746
+ * spent state must sign.
747
+ */
748
+ const checkSigner = ({ account, before, after }, facts, scope) => {
749
+ const { deviceKeyHash } = scope;
750
+ const refusals = [];
751
+ const notSigning = (keyHash) => facts.requiredSigners.has(keyHash) ? [] : [{
752
+ code: "custody-device-not-listed",
753
+ reason: "not-required-signer",
754
+ account,
755
+ keyHash
756
+ }];
757
+ if (!before) {
758
+ if (!isSameAccount(account, scope.record)) return scope.record ? [] : [{
759
+ code: "custody-context-mismatch",
760
+ reason: "creation-without-record",
761
+ account
762
+ }];
763
+ const creator = scope.record.creatorKeyHash;
764
+ if (deviceKeyHash !== void 0 && deviceKeyHash !== creator) refusals.push({
765
+ code: "custody-device-not-listed",
766
+ reason: "not-creator",
767
+ account,
768
+ keyHash: deviceKeyHash
769
+ });
770
+ refusals.push(...notSigning(creator));
771
+ if (!isCreatorOnly(after, creator, scope.record.contractVersion.currentLogicHash)) refusals.push({
772
+ code: "custody-context-mismatch",
773
+ reason: "initial-state",
774
+ account
775
+ });
776
+ return refusals;
777
+ }
778
+ if (deviceKeyHash === void 0) return before.devices.some((device) => facts.requiredSigners.has(device)) ? [] : [{
779
+ code: "custody-device-not-listed",
780
+ reason: "no-device-signer",
781
+ account
782
+ }];
783
+ if (!before.devices.includes(deviceKeyHash)) refusals.push({
784
+ code: "custody-device-not-listed",
785
+ reason: "not-listed",
786
+ account,
787
+ keyHash: deviceKeyHash
788
+ });
789
+ return [...refusals, ...notSigning(deviceKeyHash)];
790
+ };
791
+ /** The redeemers the contract lets spend each kind of account UTxO. */
792
+ const REDEEMERS_OF = {
793
+ control: ["device"],
794
+ grant: ["spendWithGrant", "sweepGrant"],
795
+ reserve: ["fund"],
796
+ fund: ["fund"]
797
+ };
798
+ /**
799
+ * The account inputs by kind, each with its spend redeemer, which must fit
800
+ * the UTxO it spends. A plain deposit rides on any account token spent
801
+ * beside it; a reserve needs the control UTxO, so only a device spends it.
802
+ */
803
+ const readAccountInputs = (account, facts) => {
804
+ const refusals = [];
805
+ const spent = facts.inputs.flatMap((view, redeemerIndex) => {
806
+ if (!require_outputs.isAtAccount(view.output, account)) return [];
807
+ const kind = require_outputs.accountUtxoKindOf(view.output, account);
808
+ const data = redeemerData(facts, require_Address.RedeemerPurpose.spend, redeemerIndex);
809
+ const decoded = data === void 0 ? void 0 : decodeCustodyAccountRedeemer(data);
810
+ if (!decoded?.ok) {
811
+ refusals.push({
812
+ code: "custody-unsupported-operation",
813
+ reason: decoded ? "unknown-redeemer" : "missing-redeemer",
814
+ purpose: "spend",
815
+ index: redeemerIndex
816
+ });
817
+ return [{
818
+ ...view,
819
+ kind,
820
+ redeemer: void 0
821
+ }];
822
+ }
823
+ if (!REDEEMERS_OF[kind].includes(decoded.value.kind)) refusals.push({
824
+ code: "custody-context-mismatch",
825
+ reason: "redeemer-mismatch",
826
+ input: view.input,
827
+ redeemer: decoded.value.kind
828
+ });
829
+ return [{
830
+ ...view,
831
+ kind,
832
+ redeemer: decoded.value
833
+ }];
834
+ });
835
+ const ofKind = (kind) => spent.filter((input) => input.kind === kind);
836
+ const controls = ofKind("control");
837
+ const grants = ofKind("grant");
838
+ const reserves = ofKind("reserve");
839
+ const funds = ofKind("fund");
840
+ if (controls.length === 0) refusals.push(...reserves.map(({ input }) => ({
841
+ code: "custody-unsupported-operation",
842
+ reason: "reserve-without-control",
843
+ input
844
+ })));
845
+ if (funds.length > 0 && controls.length + grants.length === 0) refusals.push({
846
+ code: "custody-unsupported-operation",
847
+ reason: "fund-without-account-token",
848
+ account
849
+ });
850
+ return {
851
+ controls,
852
+ spends: grants.filter(({ redeemer }) => redeemer?.kind === "spendWithGrant"),
853
+ sweeps: grants.filter(({ redeemer }) => redeemer?.kind === "sweepGrant"),
854
+ reserves,
855
+ funds,
856
+ isSpent: spent.length > 0,
857
+ refusals
858
+ };
859
+ };
860
+ const bySlot = (a, b) => a.slot < b.slot ? -1 : a.slot > b.slot ? 1 : 0;
861
+ const mintShapeOf = (entries) => {
862
+ const names = entries.map(([assetId, quantity]) => ({
863
+ name: require_fees.parseCustodyAccountTokenName(require_outputs.assetNameOf(assetId)),
864
+ quantity
865
+ }));
866
+ const [first] = names;
867
+ if (names.length === 1 && first.name?.kind === "state-nft") return {
868
+ kind: "creation",
869
+ stakeScriptHash: first.name.stakeScriptHash
870
+ };
871
+ const stakeScriptHash = first?.name?.stakeScriptHash;
872
+ if (!(stakeScriptHash !== void 0 && names.every(({ name, quantity }) => name?.kind === "grant" && name.stakeScriptHash === stakeScriptHash && (quantity === 1n || quantity === -1n)))) return { kind: "invalid" };
873
+ const issues = names.filter(({ quantity }) => quantity === 1n).length;
874
+ return {
875
+ kind: issues === names.length ? "issue" : issues === 0 ? "burn" : "mixed",
876
+ stakeScriptHash
877
+ };
878
+ };
879
+ /**
880
+ * The account's mint: its state NFT at creation, grant tokens issued at +1
881
+ * or burned at -1, under the one mint redeemer of the policy. A device
882
+ * spend of the control UTxO counts only the account's own grant tokens, so
883
+ * any other name under the policy beside it is refused. A mint of no valid
884
+ * shape is the transaction checks' to refuse.
885
+ */
886
+ const readMint = (account, facts, hasControlInput) => {
887
+ const refusals = [];
888
+ const policy = account.accountValidatorHash;
889
+ const nftAssetId = require_outputs.stateNftAssetIdOf(account);
890
+ const entries = [...facts.mint].filter(([assetId]) => require_outputs.policyIdOf(assetId) === policy);
891
+ const shape = mintShapeOf(entries);
892
+ const isOwnGrantMint = shape.kind !== "invalid" && shape.kind !== "creation" && shape.stakeScriptHash === account.stakeScriptHash;
893
+ const issued = [];
894
+ const burned = [];
895
+ for (const [assetId, quantity] of entries) {
896
+ const name = require_fees.parseCustodyAccountTokenName(require_outputs.assetNameOf(assetId));
897
+ if (isOwnGrantMint && name?.kind === "grant") (quantity === 1n ? issued : burned).push({
898
+ slot: name.slot,
899
+ assetId
900
+ });
901
+ else if (shape.kind !== "invalid" && shape.kind !== "mixed" && (hasControlInput || assetId === nftAssetId && quantity !== 1n)) refusals.push({
902
+ code: "custody-unsupported-operation",
903
+ reason: "unsupported-mint",
904
+ assetId,
905
+ quantity
906
+ });
907
+ }
908
+ issued.sort(bySlot);
909
+ burned.sort(bySlot);
910
+ const isCreation = facts.mint.has(nftAssetId);
911
+ if (isCreation || isOwnGrantMint) {
912
+ const index = facts.mintPolicies.indexOf(policy);
913
+ const data = redeemerData(facts, require_Address.RedeemerPurpose.mint, index);
914
+ const decoded = data === void 0 ? void 0 : decodeCustodyMintRedeemer(data);
915
+ const expected = {
916
+ creation: "createAccount",
917
+ issue: "issueGrants",
918
+ burn: "burnGrants"
919
+ };
920
+ if (!decoded?.ok) refusals.push({
921
+ code: "custody-unsupported-operation",
922
+ reason: decoded ? "unknown-redeemer" : "missing-redeemer",
923
+ purpose: "mint",
924
+ index
925
+ });
926
+ else if (expected[shape.kind] !== void 0 && decoded.value.kind !== expected[shape.kind]) refusals.push({
927
+ code: "custody-unsupported-operation",
928
+ reason: "mint-redeemer-mismatch",
929
+ account,
930
+ redeemer: decoded.value.kind
931
+ });
932
+ }
933
+ if (isOwnGrantMint && !hasControlInput) refusals.push({
934
+ code: "custody-unsupported-operation",
935
+ reason: "grant-mint-without-control",
936
+ account
937
+ });
938
+ for (const count of [issued.length, burned.length]) if (count > 8) refusals.push({
939
+ code: "custody-unsupported-operation",
940
+ reason: "grant-batch-limit",
941
+ account,
942
+ count,
943
+ limit: 8
944
+ });
945
+ return {
946
+ isCreation,
947
+ issued,
948
+ burned,
949
+ refusals
950
+ };
951
+ };
952
+ /** The control input's checks: one control UTxO whose resolution holds a state. */
953
+ const readControlInput = (account, inputs) => {
954
+ const refusals = [];
955
+ const [control] = inputs.controls;
956
+ if (inputs.controls.length > 1) refusals.push({
957
+ code: "custody-context-mismatch",
958
+ reason: "multiple-control-inputs",
959
+ account
960
+ });
961
+ const before = control && require_outputs.decodedInlineState(control.output);
962
+ if (control) {
963
+ if (!before) refusals.push({
964
+ code: "custody-malformed-datum",
965
+ reason: "control-input-datum",
966
+ input: control.input
967
+ });
968
+ if (!require_outputs.holdsOnlyLovelaceAndStateNft(control.output.value, account)) refusals.push({
969
+ code: "custody-malformed-datum",
970
+ reason: "control-input-value",
971
+ input: control.input
972
+ });
973
+ }
974
+ return {
975
+ control,
976
+ before,
977
+ refusals
978
+ };
979
+ };
980
+ /**
981
+ * The account's control UTxO among the reference inputs. Only a grant spend
982
+ * reads the control UTxO without spending it: Lace spends it for every
983
+ * owner operation, delegation and withdrawal included.
984
+ */
985
+ const readReferencedControls = (account, facts, isGrantSpend) => {
986
+ const references = facts.referenceInputs.filter(({ output }) => require_outputs.isControlOutput(output, account));
987
+ return {
988
+ references,
989
+ refusals: isGrantSpend ? [] : references.map(({ input }) => ({
990
+ code: "custody-unsupported-operation",
991
+ reason: "reference-control",
992
+ input
993
+ }))
994
+ };
995
+ };
996
+ /**
997
+ * Registration belongs to creation and creation needs it; delegation and
998
+ * withdrawal need the control UTxO spent, since Lace never references it
999
+ * for them.
1000
+ */
1001
+ const checkStakeOperations = (account, { certificates, withdrawal }, { isCreation, hasControlInput }) => {
1002
+ const refusals = [];
1003
+ const isRegistered = certificates.some(({ kind }) => kind === "registration");
1004
+ if (isRegistered && !isCreation) refusals.push({
1005
+ code: "custody-unsupported-operation",
1006
+ reason: "registration-without-creation",
1007
+ account
1008
+ });
1009
+ if (isCreation && !isRegistered) refusals.push({
1010
+ code: "custody-unsupported-operation",
1011
+ reason: "creation-without-registration",
1012
+ account
1013
+ });
1014
+ if ((withdrawal !== void 0 || certificates.some(({ kind }) => kind === "pool-delegation")) && !hasControlInput) refusals.push({
1015
+ code: "custody-unsupported-operation",
1016
+ reason: "stake-operation-without-control",
1017
+ account
1018
+ });
1019
+ return refusals;
1020
+ };
1021
+ /**
1022
+ * The control output's checks. With a control input or a creation there is
1023
+ * exactly one, holding only lovelace and the NFT and an account state, with
1024
+ * no reference script, which the proxy refuses there; without either there
1025
+ * is none.
1026
+ */
1027
+ const readControlOutput = (account, facts, hasStateSource) => {
1028
+ const refusals = [];
1029
+ const controls = facts.outputs.flatMap((view, outputIndex) => require_outputs.isControlOutput(view, account) ? [{
1030
+ view,
1031
+ outputIndex
1032
+ }] : []);
1033
+ if (!hasStateSource && controls.length > 0) refusals.push({
1034
+ code: "custody-context-mismatch",
1035
+ reason: "control-output-without-control-input",
1036
+ account
1037
+ });
1038
+ if (hasStateSource && controls.length !== 1) refusals.push({
1039
+ code: "custody-malformed-datum",
1040
+ reason: "control-output-count",
1041
+ account,
1042
+ count: controls.length
1043
+ });
1044
+ const control = controls.length === 1 ? controls[0] : void 0;
1045
+ const after = control && require_outputs.decodedInlineState(control.view);
1046
+ if (hasStateSource && control) {
1047
+ if (!require_outputs.holdsOnlyLovelaceAndStateNft(control.view.value, account)) refusals.push({
1048
+ code: "custody-malformed-datum",
1049
+ reason: "control-output-value",
1050
+ outputIndex: control.outputIndex
1051
+ });
1052
+ if (!after) refusals.push({
1053
+ code: "custody-malformed-datum",
1054
+ reason: "control-output-datum",
1055
+ outputIndex: control.outputIndex
1056
+ });
1057
+ if (control.view.hasReferenceScript) refusals.push({
1058
+ code: "custody-unsupported-operation",
1059
+ reason: "control-output-reference-script",
1060
+ outputIndex: control.outputIndex
1061
+ });
1062
+ }
1063
+ return {
1064
+ count: controls.length,
1065
+ control,
1066
+ after,
1067
+ refusals
1068
+ };
1069
+ };
1070
+ /** What one issued grant breaks of `issues_grant`, or its output and grant. */
1071
+ const readIssuedGrant = (account, facts, { assetId }) => {
1072
+ const holders = facts.outputs.flatMap((view, outputIndex) => view.value.assets.get(assetId) === 1n ? [{
1073
+ view,
1074
+ outputIndex
1075
+ }] : []);
1076
+ if (holders.length !== 1) return { problem: {
1077
+ code: "output-count",
1078
+ count: holders.length
1079
+ } };
1080
+ const [{ view, outputIndex }] = holders;
1081
+ if (!require_outputs.isAtAccount(view, account)) return { problem: {
1082
+ code: "address",
1083
+ outputIndex
1084
+ } };
1085
+ if (view.value.assets.size !== 1) return { problem: {
1086
+ code: "value",
1087
+ outputIndex
1088
+ } };
1089
+ if (view.hasReferenceScript) return { problem: {
1090
+ code: "reference-script",
1091
+ outputIndex
1092
+ } };
1093
+ if (view.inlineDatum === void 0) return { problem: {
1094
+ code: "datum-not-inline",
1095
+ outputIndex
1096
+ } };
1097
+ const decoded = require_fees.decodeCustodyGrant(view.inlineDatum);
1098
+ return decoded.ok ? {
1099
+ view,
1100
+ outputIndex,
1101
+ grant: decoded.value
1102
+ } : { problem: {
1103
+ code: "datum-malformed",
1104
+ outputIndex
1105
+ } };
1106
+ };
1107
+ /**
1108
+ * The grants a device rewrite issues: `issues_grant` for each minted token,
1109
+ * in slot order from the spent state's next slot, each under the generation
1110
+ * of the control output, plus the rules Lace applies to every grant it
1111
+ * authors and an expiry after `now`.
1112
+ */
1113
+ const readIssuedGrants = (account, facts, { issued, before, after, now }) => {
1114
+ const refusals = [];
1115
+ const reports = [];
1116
+ const refuse = (slot, problem) => refusals.push({
1117
+ code: "custody-malformed-datum",
1118
+ reason: "grant-output-invalid",
1119
+ account,
1120
+ slot,
1121
+ problem
1122
+ });
1123
+ for (const [index, mint] of issued.entries()) {
1124
+ const { slot } = mint;
1125
+ if (before) {
1126
+ const expected = before.nextSlot + BigInt(index);
1127
+ if (slot !== expected) refuse(slot, {
1128
+ code: "not-next-slot",
1129
+ expected
1130
+ });
1131
+ }
1132
+ const read = readIssuedGrant(account, facts, mint);
1133
+ if ("problem" in read) {
1134
+ refuse(slot, read.problem);
1135
+ continue;
1136
+ }
1137
+ const { view, outputIndex, grant } = read;
1138
+ if (grant.slot !== slot) refuse(slot, {
1139
+ code: "slot-mismatch",
1140
+ outputIndex,
1141
+ datumSlot: grant.slot
1142
+ });
1143
+ if (after && grant.generation !== after.grantGeneration) refuse(slot, {
1144
+ code: "generation-mismatch",
1145
+ outputIndex,
1146
+ generation: grant.generation,
1147
+ expected: after.grantGeneration
1148
+ });
1149
+ refusals.push(...require_fees.findCustodyGrantViolations(grant).map((violation) => ({
1150
+ code: "custody-malformed-datum",
1151
+ reason: "grant-violation",
1152
+ account,
1153
+ slot,
1154
+ violation
1155
+ })));
1156
+ if (require_fees.isCustodyGrantExpired(grant, now)) refusals.push({
1157
+ code: "custody-malformed-datum",
1158
+ reason: "expired-grant",
1159
+ account,
1160
+ slot,
1161
+ expiresAt: grant.scope.expiresAt
1162
+ });
1163
+ reports.push({
1164
+ outputIndex,
1165
+ assetId: mint.assetId,
1166
+ grant,
1167
+ lovelace: view.value.lovelace
1168
+ });
1169
+ }
1170
+ return {
1171
+ reports,
1172
+ refusals
1173
+ };
1174
+ };
1175
+ /**
1176
+ * `is_dead` against the spent state: issued under an older generation, its
1177
+ * slot revoked, or the validity interval starting strictly after its expiry.
1178
+ */
1179
+ const deathOf = (grant, state, lowerBoundMs) => {
1180
+ if (grant.generation < state.grantGeneration) return "superseded";
1181
+ if (state.revoked.includes(grant.slot)) return "revoked";
1182
+ return lowerBoundMs !== void 0 && Number.isSafeInteger(lowerBoundMs) && BigInt(lowerBoundMs) > grant.scope.expiresAt ? "expired" : void 0;
1183
+ };
1184
+ /**
1185
+ * The grants a device rewrite sweeps: each grant UTxO spent with
1186
+ * `SweepGrant` carries its grant inline beside only its token, is dead
1187
+ * against the spent state, and has its token burned; every burned token is
1188
+ * a swept grant's.
1189
+ */
1190
+ const readSweptGrants = (account, facts, { sweeps, burned, before, hasControlInput }) => {
1191
+ const refusals = [];
1192
+ const reports = [];
1193
+ if (sweeps.length > 0 && !hasControlInput) refusals.push({
1194
+ code: "custody-unsupported-operation",
1195
+ reason: "sweep-without-control",
1196
+ account
1197
+ });
1198
+ for (const { input, output } of sweeps) {
1199
+ const grant = require_outputs.decodedInlineGrant(output);
1200
+ if (!grant) {
1201
+ refusals.push({
1202
+ code: "custody-malformed-datum",
1203
+ reason: "grant-input-datum",
1204
+ input
1205
+ });
1206
+ continue;
1207
+ }
1208
+ const assetId = require_outputs.grantAssetIdOf(account, grant.slot);
1209
+ if (!assetId || !require_outputs.holdsOnlyLovelaceAndGrantToken(output.value, account, grant.slot)) {
1210
+ refusals.push({
1211
+ code: "custody-malformed-datum",
1212
+ reason: "grant-input-value",
1213
+ input
1214
+ });
1215
+ continue;
1216
+ }
1217
+ if (facts.mint.get(assetId) !== -1n) refusals.push({
1218
+ code: "custody-unsupported-operation",
1219
+ reason: "sweep-burn-mismatch",
1220
+ account,
1221
+ slot: grant.slot
1222
+ });
1223
+ if (!before) continue;
1224
+ const death = deathOf(grant, before, facts.lowerBoundMs);
1225
+ if (!death) {
1226
+ refusals.push({
1227
+ code: "custody-unsupported-operation",
1228
+ reason: "sweep-not-dead",
1229
+ account,
1230
+ input,
1231
+ slot: grant.slot
1232
+ });
1233
+ continue;
1234
+ }
1235
+ reports.push({
1236
+ input,
1237
+ assetId,
1238
+ grant,
1239
+ lovelace: output.value.lovelace,
1240
+ death
1241
+ });
1242
+ }
1243
+ const sweptSlots = sweeps.flatMap(({ output }) => {
1244
+ const grant = require_outputs.decodedInlineGrant(output);
1245
+ return grant ? [grant.slot] : [];
1246
+ });
1247
+ for (const { slot } of burned) if (!sweptSlots.includes(slot)) refusals.push({
1248
+ code: "custody-unsupported-operation",
1249
+ reason: "sweep-burn-mismatch",
1250
+ account,
1251
+ slot
1252
+ });
1253
+ return {
1254
+ reports,
1255
+ refusals
1256
+ };
1257
+ };
1258
+ /**
1259
+ * Every control state the transaction spends, references or writes for the
1260
+ * account names a logic of its contract version's catalogue: the logic is
1261
+ * the account's rule set, and any registered script may be named there, so
1262
+ * an unknown one runs rules Lace has not reviewed. Each unknown logic is
1263
+ * refused once.
1264
+ */
1265
+ const checkKnownLogics = (account, version, states) => {
1266
+ const unknown = /* @__PURE__ */ new Set();
1267
+ for (const state of states) if (state && !require_hex.findCustodyLogic(version, state.logic).ok) unknown.add(state.logic);
1268
+ return [...unknown].map((logic) => ({
1269
+ code: "custody-unsupported-version",
1270
+ reason: "unknown-logic",
1271
+ account,
1272
+ logic
1273
+ }));
1274
+ };
1275
+ /**
1276
+ * The counter rules of `recreates_control_output`, and the rules Lace adds:
1277
+ * the logic stays, since Lace does not sign an upgrade; a slot leaves the
1278
+ * revoked list only with its grant token burned in the same rewrite, or
1279
+ * with a generation step that supersedes every grant; a slot joins it only
1280
+ * once, and only after it was issued.
1281
+ */
1282
+ const checkStateChange = (account, change, { issued, burned }) => {
1283
+ const { before, after } = change;
1284
+ const refusals = [];
1285
+ if (change.logic) refusals.push({
1286
+ code: "custody-unsupported-operation",
1287
+ reason: "logic-changed",
1288
+ account,
1289
+ before: change.logic.before,
1290
+ after: change.logic.after
1291
+ });
1292
+ if (after.grantGeneration < before.grantGeneration) refusals.push({
1293
+ code: "custody-malformed-datum",
1294
+ reason: "generation-decreased",
1295
+ account,
1296
+ before: before.grantGeneration,
1297
+ after: after.grantGeneration
1298
+ });
1299
+ const minted = BigInt(issued.length);
1300
+ const counters = [[
1301
+ "nextSlot",
1302
+ before.nextSlot + minted,
1303
+ after.nextSlot
1304
+ ], [
1305
+ "outstanding",
1306
+ before.outstanding + minted - BigInt(burned.length),
1307
+ after.outstanding
1308
+ ]];
1309
+ for (const [counter, expected, actual] of counters) if (actual !== expected) refusals.push({
1310
+ code: "custody-malformed-datum",
1311
+ reason: "counter-mismatch",
1312
+ account,
1313
+ counter,
1314
+ expected,
1315
+ actual
1316
+ });
1317
+ if (after.grantGeneration === before.grantGeneration) {
1318
+ for (const slot of change.revokedRemoved) if (!burned.some((mint) => mint.slot === slot)) refusals.push({
1319
+ code: "custody-unsupported-operation",
1320
+ reason: "revoked-slot-restored",
1321
+ account,
1322
+ slot
1323
+ });
1324
+ }
1325
+ const invalid = change.revokedAdded.filter((slot, index) => slot < 0n || slot >= before.nextSlot || change.revokedAdded.indexOf(slot) !== index);
1326
+ for (const slot of new Set(after.revoked)) {
1327
+ const previousCount = before.revoked.filter((value) => value === slot).length;
1328
+ if (after.revoked.filter((value) => value === slot).length > Math.max(1, previousCount)) invalid.push(slot);
1329
+ }
1330
+ for (const slot of new Set(invalid)) refusals.push({
1331
+ code: "custody-unsupported-operation",
1332
+ reason: "revoked-slot-invalid",
1333
+ account,
1334
+ slot
1335
+ });
1336
+ return refusals;
1337
+ };
1338
+ /**
1339
+ * Grant spends are refused unless the caller allows them. An allowed one is
1340
+ * refused for every rule of the logic's grant spend rule it breaks, so an
1341
+ * accepted grant spend is one the contract accepts, and for any input
1342
+ * outside the account address: an agent spends from the account alone.
1343
+ */
1344
+ const checkGrantSpends = ({ account, spends }, facts, scope) => [...spends.flatMap(({ input, output }) => scope.options.allowGrantSpend ? grantSpendViolationsOf({
1345
+ grantInput: output,
1346
+ inputs: facts.inputs.map((view) => view.output),
1347
+ referenceInputs: facts.referenceInputs.map((view) => view.output),
1348
+ outputs: facts.outputs,
1349
+ mint: facts.mint,
1350
+ requiredSigners: facts.requiredSigners,
1351
+ upperBoundMs: facts.upperBoundMs,
1352
+ account
1353
+ }).map((violation) => ({
1354
+ code: "custody-unsupported-operation",
1355
+ reason: "grant-spend-violation",
1356
+ account,
1357
+ input,
1358
+ violation
1359
+ })) : [{
1360
+ code: "custody-unsupported-operation",
1361
+ reason: "grant-spend",
1362
+ account,
1363
+ input
1364
+ }]), ...scope.options.allowGrantSpend && spends.length > 0 ? facts.inputs.filter(({ output }) => !require_outputs.isAtAccount(output, account)).map(({ input }) => ({
1365
+ code: "custody-unsupported-operation",
1366
+ reason: "grant-spend-foreign-input",
1367
+ account,
1368
+ input
1369
+ })) : []];
1370
+ /** The grant a single grant spend uses and the output that recreates it. */
1371
+ const grantSpendReportOf = (account, spends, facts) => {
1372
+ if (spends.length !== 1) return void 0;
1373
+ const [{ input, output }] = spends;
1374
+ const grant = require_outputs.decodedInlineGrant(output);
1375
+ const assetId = grant && require_outputs.grantAssetIdOf(account, grant.slot);
1376
+ const outputIndex = facts.outputs.findIndex((view) => assetId !== void 0 && view.value.assets.get(assetId) === 1n);
1377
+ const after = outputIndex === -1 ? void 0 : require_outputs.decodedInlineGrant(facts.outputs[outputIndex]);
1378
+ return grant && after ? {
1379
+ input,
1380
+ grant,
1381
+ outputIndex,
1382
+ after
1383
+ } : void 0;
1384
+ };
1385
+ /**
1386
+ * The operations a device rewrite performs, from its state change and the
1387
+ * grants it issues and sweeps. One revoked slot beside one issued grant is a
1388
+ * replacement; a generation step of one that empties the revoked list
1389
+ * under the same logic revokes every grant, where an upgrade has the same
1390
+ * counters.
1391
+ */
1392
+ const stateOperationsOf = (change, issuedSlots, sweptSlots) => {
1393
+ const { before, after, revokedAdded } = change;
1394
+ const isRevokeAll = !change.logic && after.grantGeneration === before.grantGeneration + 1n && after.revoked.length === 0;
1395
+ const isReplace = !change.grantGeneration && revokedAdded.length === 1 && issuedSlots.length === 1;
1396
+ return [
1397
+ ...isReplace ? [{
1398
+ kind: "replace-grant",
1399
+ slot: revokedAdded[0],
1400
+ issuedSlot: issuedSlots[0]
1401
+ }] : issuedSlots.map((slot) => ({
1402
+ kind: "issue-grant",
1403
+ slot
1404
+ })),
1405
+ ...isRevokeAll ? [{ kind: "revoke-all-grants" }] : [...isReplace ? [] : revokedAdded.map((slot) => ({
1406
+ kind: "revoke-grant",
1407
+ slot
1408
+ })), ...change.grantGeneration ? [{ kind: "change-grant-generation" }] : []],
1409
+ ...sweptSlots.length > 0 ? [{
1410
+ kind: "sweep-grants",
1411
+ slots: sweptSlots
1412
+ }] : [],
1413
+ ...change.devicesAdded.map((device) => ({
1414
+ kind: "add-device",
1415
+ device
1416
+ })),
1417
+ ...change.devicesRemoved.map((device) => ({
1418
+ kind: "remove-device",
1419
+ device
1420
+ })),
1421
+ ...change.isReordered ? [{ kind: "reorder-state" }] : []
1422
+ ];
1423
+ };
1424
+ /**
1425
+ * Lovelace leaving the account's address, its reward account and its logic
1426
+ * run's draw; negative is a net inflow. Lovelace is fungible, so what the
1427
+ * logic run draws is credited to the account: any of it that leaves counts
1428
+ * as the account's.
1429
+ */
1430
+ const lovelaceGivenUp = ({ netOutflow, stake, logicRun }) => netOutflow.lovelace + (stake.withdrawal ?? 0n) + (logicRun?.draw ?? 0n);
1431
+ /**
1432
+ * Whether an owner operation pays value out of the account: any asset that
1433
+ * leaves it, or lovelace given up beyond its share of the fee. An account
1434
+ * that pays only its own fee spends nothing. Account tokens are minted and
1435
+ * burned, never paid out: placement keeps them at their accounts.
1436
+ */
1437
+ const isOwnerSpend = (facts, feeShare) => [...facts.netOutflow.assets].some(([assetId, quantity]) => quantity > 0n && require_outputs.policyIdOf(assetId) !== facts.account.accountValidatorHash) || lovelaceGivenUp(facts) > feeShare;
1438
+ const operationsOf = (facts, feeShare) => {
1439
+ const { isCreation, grantSpend, stateChange, stake } = facts;
1440
+ return [
1441
+ ...isCreation ? [{ kind: "create" }] : [],
1442
+ ...grantSpend ? [{
1443
+ kind: "grant-spend",
1444
+ slot: grantSpend.grant.slot
1445
+ }] : [],
1446
+ ...stateChange ? stateOperationsOf(stateChange, facts.issued.map(({ grant }) => grant.slot), facts.swept.map(({ grant }) => grant.slot)) : [],
1447
+ ...!facts.isGrantSpend && isOwnerSpend(facts, feeShare) ? [{ kind: "owner-spend" }] : [],
1448
+ ...stake.certificates.flatMap((certificate) => certificate.kind === "pool-delegation" ? [{
1449
+ kind: "delegate",
1450
+ poolId: certificate.poolId
1451
+ }] : []),
1452
+ ...stake.withdrawal === void 0 ? [] : [{
1453
+ kind: "withdraw-rewards",
1454
+ quantity: stake.withdrawal
1455
+ }]
1456
+ ];
1457
+ };
1458
+ /** Each operated account's part of the accounts' fee share, in order. */
1459
+ const feeSharesOfAccounts = (operated, { accountShare }) => accountFeeSharesOf(operated.map(({ operationFacts }) => lovelaceGivenUp(operationFacts)), accountShare);
1460
+ /** The reports of the operated accounts, each with its operations named. */
1461
+ const withOperations = (operated, feeShares) => {
1462
+ const shares = feeSharesOfAccounts(operated, feeShares);
1463
+ return operated.map(({ report, operationFacts }, index) => ({
1464
+ ...report,
1465
+ operations: operationsOf(operationFacts, shares[index])
1466
+ }));
1467
+ };
1468
+ /**
1469
+ * A logic run that draws lovelace must leave it at the account. Lovelace
1470
+ * is fungible, so the draw is credited to the account, and it stays there
1471
+ * when the account gives up no more lovelace than its part of the fee. A
1472
+ * fee payer that takes change back could have paid the whole fee, so the
1473
+ * accounts' part is then nothing: otherwise the payer's change could hold
1474
+ * the draw. A grant spend pays out of the account by definition, so it may
1475
+ * draw nothing.
1476
+ */
1477
+ const checkLogicDraws = (operated, feeShares, isChangeToPayers) => {
1478
+ const shares = isChangeToPayers ? operated.map(() => 0n) : feeSharesOfAccounts(operated, feeShares);
1479
+ return operated.flatMap(({ operationFacts }, index) => {
1480
+ const { account, logicRun, isGrantSpend } = operationFacts;
1481
+ if (!logicRun || logicRun.draw === 0n) return [];
1482
+ return isGrantSpend || lovelaceGivenUp(operationFacts) > shares[index] ? [{
1483
+ code: "custody-unsupported-operation",
1484
+ reason: "logic-draw-leaves-account",
1485
+ account,
1486
+ logic: logicRun.logic,
1487
+ quantity: logicRun.draw
1488
+ }] : [];
1489
+ });
1490
+ };
1491
+ const isOwnReserve = (view) => view.inlineDatum !== void 0 && !view.hasReferenceScript && require_grant_status.isCustodyReserveDatum(view.inlineDatum);
1492
+ const inspectAccount = (candidate, facts, scope) => {
1493
+ const account = hashesOf(candidate);
1494
+ const { networkId } = scope.options.chainId;
1495
+ const inputs = readAccountInputs(account, facts);
1496
+ const { control, before, ...controlInput } = readControlInput(account, inputs);
1497
+ const hasControlInput = control !== void 0;
1498
+ const isGrantSpend = inputs.spends.length > 0;
1499
+ const mint = readMint(account, facts, hasControlInput);
1500
+ const stake = readStakeEffects(candidate, facts);
1501
+ const controlOutput = readControlOutput(account, facts, hasControlInput || mint.isCreation);
1502
+ const { after } = controlOutput;
1503
+ const referenced = readReferencedControls(account, facts, isGrantSpend);
1504
+ const referencedStates = referenced.references.map(({ output }) => require_outputs.decodedInlineState(output));
1505
+ const states = [
1506
+ before,
1507
+ after,
1508
+ ...referencedStates
1509
+ ];
1510
+ const logicRun = readLogicRun(account, facts, before?.logic ?? (isGrantSpend ? referencedStates[0]?.logic : void 0) ?? (mint.isCreation ? after?.logic : void 0));
1511
+ const issued = readIssuedGrants(account, facts, {
1512
+ issued: mint.issued,
1513
+ before,
1514
+ after,
1515
+ now: scope.options.now
1516
+ });
1517
+ const swept = readSweptGrants(account, facts, {
1518
+ sweeps: inputs.sweeps,
1519
+ burned: mint.burned,
1520
+ before,
1521
+ hasControlInput
1522
+ });
1523
+ const refusals = [
1524
+ ...inputs.refusals,
1525
+ ...controlInput.refusals,
1526
+ ...mint.refusals,
1527
+ ...referenced.refusals,
1528
+ ...checkKnownLogics(account, candidate.contractVersion, states),
1529
+ ...logicRun.refusals,
1530
+ ...stake.refusals,
1531
+ ...checkStakeOperations(account, stake, {
1532
+ isCreation: mint.isCreation,
1533
+ hasControlInput
1534
+ }),
1535
+ ...controlOutput.refusals,
1536
+ ...issued.refusals,
1537
+ ...swept.refusals,
1538
+ ...checkGrantSpends({
1539
+ account,
1540
+ spends: inputs.spends
1541
+ }, facts, scope)
1542
+ ];
1543
+ const netOutflow = leavingValueAt(facts.inputs.map(({ output }) => output), facts.outputs, require_outputs.accountPlutusAddressKey(account));
1544
+ let stateChange;
1545
+ if (after && (before || mint.isCreation)) {
1546
+ const written = {
1547
+ account,
1548
+ before,
1549
+ after
1550
+ };
1551
+ refusals.push(...checkWrittenState(written), ...checkSigner(written, facts, scope));
1552
+ if (before) {
1553
+ stateChange = diffCustodyStates(before, after);
1554
+ refusals.push(...checkStateChange(account, stateChange, mint));
1555
+ }
1556
+ }
1557
+ const [reference] = referenced.references;
1558
+ const [referencedState] = referencedStates;
1559
+ const grantSpend = grantSpendReportOf(account, inputs.spends, facts);
1560
+ return {
1561
+ isOperated: inputs.isSpent || mint.isCreation || mint.issued.length + mint.burned.length > 0 || stake.isStaked || controlOutput.count > 0,
1562
+ refusals,
1563
+ logics: states.flatMap((state) => state ? [state.logic] : []),
1564
+ report: {
1565
+ contractVersion: candidate.contractVersion,
1566
+ ...account,
1567
+ address: require_outputs.accountAddressOf(account, networkId),
1568
+ rewardAccount: require_outputs.accountRewardAccountOf(account, networkId),
1569
+ ...control && before ? { controlInput: {
1570
+ input: control.input,
1571
+ state: before,
1572
+ lovelace: control.output.value.lovelace
1573
+ } } : {},
1574
+ ...controlOutput.control && after ? { controlOutput: {
1575
+ outputIndex: controlOutput.control.outputIndex,
1576
+ state: after,
1577
+ lovelace: controlOutput.control.view.value.lovelace
1578
+ } } : {},
1579
+ ...isGrantSpend && reference && referencedState ? { referencedControl: {
1580
+ input: reference.input,
1581
+ state: referencedState,
1582
+ lovelace: reference.output.value.lovelace
1583
+ } } : {},
1584
+ fundInputs: inputs.funds.map(({ input, output }) => ({
1585
+ input,
1586
+ value: output.txOut.value,
1587
+ hasReferenceScript: output.hasReferenceScript
1588
+ })),
1589
+ reserveInputs: inputs.reserves.map(({ input, output }) => ({
1590
+ input,
1591
+ value: output.txOut.value,
1592
+ datum: output.datumKind === "hash" ? "hash" : "inline",
1593
+ isOwnFormat: isOwnReserve(output),
1594
+ hasReferenceScript: output.hasReferenceScript
1595
+ })),
1596
+ accountOutputs: facts.outputs.flatMap((view, outputIndex) => require_outputs.isAtAccount(view, account) ? [{
1597
+ outputIndex,
1598
+ value: view.txOut.value,
1599
+ datum: view.datumKind,
1600
+ kind: require_outputs.accountUtxoKindOf(view, account)
1601
+ }] : []),
1602
+ issuedGrants: issued.reports,
1603
+ sweptGrants: swept.reports,
1604
+ ...grantSpend ? { grantSpend } : {},
1605
+ netOutflow,
1606
+ certificates: stake.certificates,
1607
+ ...stake.withdrawal === void 0 ? {} : { withdrawal: stake.withdrawal },
1608
+ ...logicRun.run && logicRun.run.draw > 0n ? { logicDraw: logicRun.run.draw } : {},
1609
+ ...stateChange ? { stateChange } : {}
1610
+ },
1611
+ operationFacts: {
1612
+ account,
1613
+ isCreation: mint.isCreation,
1614
+ isGrantSpend,
1615
+ grantSpend,
1616
+ stateChange,
1617
+ issued: issued.reports,
1618
+ swept: swept.reports,
1619
+ netOutflow,
1620
+ stake,
1621
+ logicRun: logicRun.run
1622
+ }
1623
+ };
1624
+ };
1625
+ //#endregion
1626
+ //#region ../../packages/lib/cardano-custody/src/analysis/auxiliary-data.ts
1627
+ /**
1628
+ * The metadata a transaction carries, read from its auxiliary data and
1629
+ * bound to the signed body by the auxiliary data hash. Undefined without
1630
+ * any. A body that commits to auxiliary data the transaction does not
1631
+ * carry, or auxiliary data the body does not commit to by that hash, is
1632
+ * refused: a signature over the body would then authorise metadata consent
1633
+ * never saw. So is auxiliary data holding anything but metadata.
1634
+ */
1635
+ const readCustodyMetadata = (transaction, body) => {
1636
+ let auxiliaryData;
1637
+ let carried;
1638
+ try {
1639
+ const serialized = transaction.auxiliaryData();
1640
+ auxiliaryData = serialized?.toCore();
1641
+ carried = serialized ? require_Address.blake2b.hash(serialized.toCbor(), 32) : void 0;
1642
+ } catch {
1643
+ return require_hex.fail({
1644
+ code: "custody-unsupported-operation",
1645
+ reason: "auxiliary-data-unreadable"
1646
+ });
1647
+ }
1648
+ if (body.auxiliaryDataHash?.toLowerCase() !== carried) return require_hex.fail({
1649
+ code: "custody-unsupported-operation",
1650
+ reason: "auxiliary-data-hash-mismatch"
1651
+ });
1652
+ if ((auxiliaryData?.scripts?.length ?? 0) > 0) return require_hex.fail({
1653
+ code: "custody-unsupported-operation",
1654
+ reason: "auxiliary-data-scripts"
1655
+ });
1656
+ const metadata = auxiliaryData?.blob;
1657
+ return require_hex.ok(metadata && metadata.size > 0 ? metadata : void 0);
1658
+ };
1659
+ /**
1660
+ * A grant spend carrying metadata. An agent spends only what its grant
1661
+ * allows, and attaching data to the account's transactions is not among it.
1662
+ */
1663
+ const checkGrantSpendMetadata = (operated, metadata) => metadata === void 0 ? [] : operated.flatMap(({ report }) => report.grantSpend ? [{
1664
+ code: "custody-unsupported-operation",
1665
+ reason: "grant-spend-metadata",
1666
+ account: hashesOf(report),
1667
+ input: report.grantSpend.input
1668
+ }] : []);
1669
+ //#endregion
1670
+ //#region ../../packages/lib/cardano-custody/src/analysis/refusals.ts
1671
+ /**
1672
+ * The order checks run in: the record, the caller's context, the account
1673
+ * UTxOs it resolves, a grant spend, unsupported shapes, the written datums,
1674
+ * the signing device, the initial state at creation and locked outputs.
1675
+ */
1676
+ const STAGE = {
1677
+ "unsupported-contract-version": 0,
1678
+ "invalid-record": 0,
1679
+ "unresolved-input": 1,
1680
+ "resolution-not-in-body": 1,
1681
+ "duplicate-resolution": 1,
1682
+ "invalid-resolution": 1,
1683
+ "network-mismatch": 1,
1684
+ "redeemer-mismatch": 1,
1685
+ "redeemer-on-key-input": 1,
1686
+ "reference-script-mismatch": 1,
1687
+ "unused-account-validator": 1,
1688
+ "unused-logic": 1,
1689
+ "multiple-control-inputs": 1,
1690
+ "foreign-account": 1,
1691
+ "account-not-operated": 1,
1692
+ "creation-without-record": 1,
1693
+ "control-output-without-control-input": 1,
1694
+ "control-input-datum": 2,
1695
+ "unknown-logic": 2,
1696
+ "control-input-value": 2,
1697
+ "grant-input-datum": 2,
1698
+ "grant-input-value": 2,
1699
+ "grant-spend": 3,
1700
+ "grant-spend-violation": 3,
1701
+ "grant-spend-foreign-input": 3,
1702
+ "grant-spend-metadata": 3,
1703
+ "unsupported-body-field": 4,
1704
+ "auxiliary-data-hash-mismatch": 4,
1705
+ "auxiliary-data-scripts": 4,
1706
+ "auxiliary-data-unreadable": 4,
1707
+ "missing-redeemer": 4,
1708
+ "unknown-redeemer": 4,
1709
+ "duplicate-redeemer": 4,
1710
+ "mint-redeemer-mismatch": 4,
1711
+ "foreign-certificate": 4,
1712
+ "refused-certificate": 4,
1713
+ "unsupported-certificate": 4,
1714
+ "foreign-withdrawal": 4,
1715
+ "duplicate-withdrawal": 4,
1716
+ "logic-draw-leaves-account": 4,
1717
+ "logic-withdrawal-missing": 4,
1718
+ "foreign-mint": 4,
1719
+ "unsupported-mint": 4,
1720
+ "issue-sweep-mixed": 4,
1721
+ "grant-batch-limit": 4,
1722
+ "grant-mint-without-control": 4,
1723
+ "reference-control": 4,
1724
+ "foreign-reference-script": 4,
1725
+ "creation-without-registration": 4,
1726
+ "fund-without-account-token": 4,
1727
+ "reserve-without-control": 4,
1728
+ "sweep-without-control": 4,
1729
+ "sweep-burn-mismatch": 4,
1730
+ "registration-without-creation": 4,
1731
+ "stake-operation-without-control": 4,
1732
+ "account-token-misplaced": 4,
1733
+ "foreign-reserve": 4,
1734
+ "deposit-reference-script": 4,
1735
+ "control-output-count": 5,
1736
+ "control-output-datum": 5,
1737
+ "control-output-value": 5,
1738
+ "control-output-reference-script": 5,
1739
+ "state-violation": 5,
1740
+ "counter-mismatch": 5,
1741
+ "generation-decreased": 5,
1742
+ "logic-changed": 5,
1743
+ "revoked-slot-restored": 5,
1744
+ "revoked-slot-invalid": 5,
1745
+ "grant-output-invalid": 5,
1746
+ "grant-violation": 5,
1747
+ "expired-grant": 5,
1748
+ "sweep-not-dead": 5,
1749
+ "no-device-signer": 6,
1750
+ "not-creator": 6,
1751
+ "not-listed": 6,
1752
+ "not-required-signer": 6,
1753
+ "initial-state": 7,
1754
+ "datum-hash": 8,
1755
+ "unspendable-address": 8
1756
+ };
1757
+ /** Refusals in check order; refusals of one stage keep the order found. */
1758
+ const inCheckOrder = (refusals) => refusals.map((refusal, position) => ({
1759
+ refusal,
1760
+ position
1761
+ })).sort((a, b) => STAGE[a.refusal.reason] - STAGE[b.refusal.reason] || a.position - b.position).map(({ refusal }) => refusal);
1762
+ //#endregion
1763
+ //#region ../../packages/lib/cardano-custody/src/analysis/transaction-checks.ts
1764
+ const UNSUPPORTED_BODY_FIELDS = [
1765
+ "accountBalanceIntervals",
1766
+ "directDeposits",
1767
+ "guards",
1768
+ "proposalProcedures",
1769
+ "subTransactions",
1770
+ "update",
1771
+ "votingProcedures"
1772
+ ];
1773
+ /**
1774
+ * A redeemer pointer named twice has no single reading, so the redeemer a
1775
+ * check reads might not be the one a script runs with.
1776
+ */
1777
+ const checkRedeemerPointers = (facts) => {
1778
+ const seen = /* @__PURE__ */ new Set();
1779
+ const refusals = [];
1780
+ for (const { purpose, index } of facts.redeemers) {
1781
+ const pointer = `${purpose}:${index}`;
1782
+ if (seen.has(pointer)) refusals.push({
1783
+ code: "custody-unsupported-operation",
1784
+ reason: "duplicate-redeemer",
1785
+ purpose,
1786
+ index
1787
+ });
1788
+ seen.add(pointer);
1789
+ }
1790
+ return refusals;
1791
+ };
1792
+ /**
1793
+ * Consistency checks that catch resolutions hiding a custody spend. The
1794
+ * ledger refuses a spend redeemer for an input no script locks, and an
1795
+ * account validator witness that no input or mint needs, so either means a
1796
+ * resolution misstates what the transaction spends.
1797
+ */
1798
+ const checkSpendWitnesses = (facts) => {
1799
+ const refusals = [];
1800
+ for (const [index, { input, output }] of facts.inputs.entries()) if (redeemerData(facts, require_Address.RedeemerPurpose.spend, index) !== void 0 && paymentCredentialOf(output)?.type !== require_Address.CredentialType.ScriptHash) refusals.push({
1801
+ code: "custody-context-mismatch",
1802
+ reason: "redeemer-on-key-input",
1803
+ input
1804
+ });
1805
+ for (const version of require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS) {
1806
+ const { accountValidatorHash } = version;
1807
+ const isNeeded = facts.inputs.some(({ output: { addressClass } }) => addressClass.kind === "account" && addressClass.contractVersion === version) || facts.mintPolicies.includes(accountValidatorHash);
1808
+ if (facts.witnessScriptHashes.has(accountValidatorHash) && !isNeeded) refusals.push({
1809
+ code: "custody-context-mismatch",
1810
+ reason: "unused-account-validator",
1811
+ accountValidatorHash
1812
+ });
1813
+ }
1814
+ return refusals;
1815
+ };
1816
+ /**
1817
+ * Consistency checks on the known logics a transaction attaches or runs: a
1818
+ * logic runs through a withdrawal from its stake credential, and only over
1819
+ * a control UTxO that names it, spent, referenced or written. The ledger
1820
+ * refuses a witness nothing needs, and a run with no such control UTxO
1821
+ * fails, so a logic attached without a run, or run with no control UTxO
1822
+ * naming it among `logics`, means a resolution hides that control UTxO or
1823
+ * the transaction cannot succeed.
1824
+ */
1825
+ const checkLogicUse = (facts, logics) => {
1826
+ const named = new Set(logics);
1827
+ const withdrawn = new Set((facts.body.withdrawals ?? []).flatMap(({ stakeAddress }) => {
1828
+ const scriptHash = scriptHashOfRewardAccount(stakeAddress);
1829
+ return scriptHash === void 0 ? [] : [scriptHash];
1830
+ }));
1831
+ return [...new Set([...withdrawn, ...facts.witnessScriptHashes].filter(require_outputs.isKnownCustodyLogic))].filter((logic) => !withdrawn.has(logic) || !named.has(logic)).map((logic) => ({
1832
+ code: "custody-context-mismatch",
1833
+ reason: "unused-logic",
1834
+ logic
1835
+ }));
1836
+ };
1837
+ /**
1838
+ * A reward account the body's withdrawal map names more than once. The
1839
+ * decoded body keeps one of its withdrawals, so what inspection reads might
1840
+ * not be what the bytes say.
1841
+ */
1842
+ const checkWithdrawalAccounts = (facts) => {
1843
+ const seen = /* @__PURE__ */ new Set();
1844
+ const repeated = /* @__PURE__ */ new Set();
1845
+ for (const rewardAccount of facts.withdrawalAccounts) {
1846
+ if (seen.has(rewardAccount)) repeated.add(rewardAccount);
1847
+ seen.add(rewardAccount);
1848
+ }
1849
+ return [...repeated].map((rewardAccount) => ({
1850
+ code: "custody-unsupported-operation",
1851
+ reason: "duplicate-withdrawal",
1852
+ rewardAccount
1853
+ }));
1854
+ };
1855
+ const isSameOutpoint = (a, b) => a.txId.toLowerCase() === b.txId.toLowerCase() && a.index === b.index;
1856
+ /**
1857
+ * Reference inputs that carry a reference script must be the contract's
1858
+ * parked scripts on the inspected network: the output reference a supported
1859
+ * version pins, resolved with exactly the script it pins there. Any other
1860
+ * reference script is code Lace has not pinned.
1861
+ */
1862
+ const checkReferenceScripts = (facts, networkMagic) => {
1863
+ const parked = require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS.flatMap((version) => version.referenceScripts[networkMagic] ?? []);
1864
+ return facts.referenceInputs.flatMap(({ input, output }) => {
1865
+ const pinned = parked.find(({ txIn }) => isSameOutpoint(txIn, input));
1866
+ if (pinned) return output.referenceScriptHash === pinned.scriptHash ? [] : [{
1867
+ code: "custody-context-mismatch",
1868
+ reason: "reference-script-mismatch",
1869
+ input
1870
+ }];
1871
+ return output.hasReferenceScript ? [{
1872
+ code: "custody-unsupported-operation",
1873
+ reason: "foreign-reference-script",
1874
+ input
1875
+ }] : [];
1876
+ });
1877
+ };
1878
+ /**
1879
+ * Certificates and withdrawals on credentials of no custody account the
1880
+ * transaction names. A withdrawal may also run a known logic, which
1881
+ * `checkLogicUse` ties to the control UTxOs naming it.
1882
+ */
1883
+ const checkForeignStakeEffects = (facts, candidates, networkId) => {
1884
+ const refusals = [];
1885
+ const stakeScriptHashes = new Set(candidates.map(({ stakeScriptHash }) => stakeScriptHash));
1886
+ for (const [certificateIndex, certificate] of (facts.body.certificates ?? []).entries()) {
1887
+ const credential = stakeCredentialOf(certificate);
1888
+ if (credential?.type !== require_Address.CredentialType.ScriptHash || !stakeScriptHashes.has(credential.hash)) refusals.push({
1889
+ code: "custody-unsupported-operation",
1890
+ reason: "foreign-certificate",
1891
+ certificateIndex,
1892
+ certificateType: certificate.__typename
1893
+ });
1894
+ }
1895
+ for (const { stakeAddress } of facts.body.withdrawals ?? []) {
1896
+ const scriptHash = scriptHashOfRewardAccount(stakeAddress);
1897
+ if (scriptHash === void 0 || !(stakeScriptHashes.has(scriptHash) || require_outputs.isKnownCustodyLogic(scriptHash))) refusals.push({
1898
+ code: "custody-unsupported-operation",
1899
+ reason: "foreign-withdrawal",
1900
+ rewardAccount: stakeAddress
1901
+ });
1902
+ else if (require_Address.Address.fromString(stakeAddress)?.getNetworkId() !== networkId) refusals.push({
1903
+ code: "custody-context-mismatch",
1904
+ reason: "network-mismatch",
1905
+ address: stakeAddress
1906
+ });
1907
+ }
1908
+ return refusals;
1909
+ };
1910
+ /**
1911
+ * Mints of other policies, and custody mints of a shape no mint redeemer
1912
+ * accepts: one state NFT name, or grant names of one account that all issue
1913
+ * or all burn.
1914
+ */
1915
+ const checkMint = (facts) => {
1916
+ const byPolicy = /* @__PURE__ */ new Map();
1917
+ for (const entry of facts.mint) {
1918
+ const policyId = require_outputs.policyIdOf(entry[0]);
1919
+ byPolicy.set(policyId, [...byPolicy.get(policyId) ?? [], entry]);
1920
+ }
1921
+ return [...byPolicy].flatMap(([policyId, entries]) => {
1922
+ const refuseEach = (reason) => entries.map(([assetId, quantity]) => ({
1923
+ code: "custody-unsupported-operation",
1924
+ reason,
1925
+ assetId,
1926
+ quantity
1927
+ }));
1928
+ if (!versionOfPolicy(policyId)) return refuseEach("foreign-mint");
1929
+ const shape = mintShapeOf(entries);
1930
+ if (shape.kind === "invalid") return refuseEach("unsupported-mint");
1931
+ return shape.kind === "mixed" ? [{
1932
+ code: "custody-unsupported-operation",
1933
+ reason: "issue-sweep-mixed",
1934
+ account: {
1935
+ accountValidatorHash: policyId,
1936
+ stakeScriptHash: shape.stakeScriptHash
1937
+ }
1938
+ }] : [];
1939
+ });
1940
+ };
1941
+ /**
1942
+ * What an output at a custody account that holds no account token must be:
1943
+ * a plain deposit, or a reserve in the format Lace writes, `d87980` with no
1944
+ * reference script. A datum hash locks value behind a preimage no account
1945
+ * builder holds; another datum or a reference script raises the fee and
1946
+ * minimum of every later spend.
1947
+ */
1948
+ const findAccountOutputRefusal = (view, location) => {
1949
+ if (view.datumKind === "hash") return {
1950
+ code: "custody-locked-output",
1951
+ reason: "datum-hash",
1952
+ location
1953
+ };
1954
+ if (view.inlineDatum !== void 0) return view.hasReferenceScript || !require_grant_status.isCustodyReserveDatum(view.inlineDatum) ? {
1955
+ code: "custody-unsupported-operation",
1956
+ reason: "foreign-reserve",
1957
+ location
1958
+ } : void 0;
1959
+ return view.hasReferenceScript ? {
1960
+ code: "custody-unsupported-operation",
1961
+ reason: "deposit-reference-script",
1962
+ location
1963
+ } : void 0;
1964
+ };
1965
+ /**
1966
+ * Rules over every output and resolution: custody addresses on the named
1967
+ * network, account tokens at their own account addresses, no value locked
1968
+ * where no redeemer can reach it, and account outputs without an account
1969
+ * token in a shape Lace writes. Outputs holding an account token are the
1970
+ * control and grant outputs, which their account's checks read.
1971
+ */
1972
+ const checkOutputs = (facts, networkId) => {
1973
+ const refusals = [];
1974
+ const custodyViews = [
1975
+ ...[
1976
+ ...facts.inputs,
1977
+ ...facts.referenceInputs,
1978
+ ...facts.collaterals
1979
+ ].map(({ output }) => output),
1980
+ ...facts.outputs,
1981
+ ...facts.collateralReturn ? [facts.collateralReturn] : []
1982
+ ].filter(({ addressClass }) => addressClass.kind !== "other");
1983
+ for (const view of custodyViews) if (view.address?.getNetworkId() !== networkId) refusals.push({
1984
+ code: "custody-context-mismatch",
1985
+ reason: "network-mismatch",
1986
+ address: view.txOut.address
1987
+ });
1988
+ const accountValidatorHashes = require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS.map((version) => version.accountValidatorHash);
1989
+ for (const { input, output } of facts.inputs) if (require_outputs.holdsMisplacedAccountToken(output, accountValidatorHashes)) refusals.push({
1990
+ code: "custody-unsupported-operation",
1991
+ reason: "account-token-misplaced",
1992
+ location: {
1993
+ kind: "input",
1994
+ input
1995
+ }
1996
+ });
1997
+ const locatedOutputs = [...facts.outputs.map((view, index) => ({
1998
+ view,
1999
+ location: {
2000
+ kind: "output",
2001
+ index
2002
+ }
2003
+ })), ...facts.collateralReturn ? [{
2004
+ view: facts.collateralReturn,
2005
+ location: { kind: "collateral-return" }
2006
+ }] : []];
2007
+ for (const { view, location } of locatedOutputs) {
2008
+ if (location.kind === "output" && require_outputs.holdsMisplacedAccountToken(view, accountValidatorHashes)) refusals.push({
2009
+ code: "custody-unsupported-operation",
2010
+ reason: "account-token-misplaced",
2011
+ location
2012
+ });
2013
+ const { addressClass } = view;
2014
+ if (addressClass.kind === "unspendable") refusals.push({
2015
+ code: "custody-locked-output",
2016
+ reason: "unspendable-address",
2017
+ location
2018
+ });
2019
+ else if (addressClass.kind === "account" && require_outputs.holdsNoAccountToken(view.value, addressClass.contractVersion.accountValidatorHash)) {
2020
+ const refusal = findAccountOutputRefusal(view, location);
2021
+ if (refusal) refusals.push(refusal);
2022
+ }
2023
+ }
2024
+ return refusals;
2025
+ };
2026
+ const checkBodyFields = (facts) => {
2027
+ const body = facts.body;
2028
+ return UNSUPPORTED_BODY_FIELDS.filter((field) => body[field] !== void 0).map((field) => ({
2029
+ code: "custody-unsupported-operation",
2030
+ reason: "unsupported-body-field",
2031
+ field
2032
+ }));
2033
+ };
2034
+ /** With a record, the transaction operates that account and no other. */
2035
+ const checkRecordAccount = (operated, record) => {
2036
+ if (!record) return [];
2037
+ const refusals = operated.filter((account) => !isSameAccount(account, record)).map((account) => ({
2038
+ code: "custody-context-mismatch",
2039
+ reason: "foreign-account",
2040
+ account: hashesOf(account)
2041
+ }));
2042
+ if (!operated.some((account) => isSameAccount(account, record))) refusals.push({
2043
+ code: "custody-context-mismatch",
2044
+ reason: "account-not-operated",
2045
+ account: {
2046
+ accountValidatorHash: record.contractVersion.accountValidatorHash,
2047
+ stakeScriptHash: record.stakeScriptHash
2048
+ }
2049
+ });
2050
+ return refusals;
2051
+ };
2052
+ //#endregion
2053
+ //#region ../../packages/lib/cardano-custody/src/analysis/inspect.ts
2054
+ const readRecord = (options) => {
2055
+ if (options.accountRecord === void 0) return require_hex.ok(void 0);
2056
+ const parsed = parseCustodyAccountRecord(options.accountRecord, options.chainId.networkMagic);
2057
+ if (parsed.ok) return require_hex.ok(parsed.value.identity);
2058
+ return require_hex.fail(parsed.error.code === "unsupported-contract-version" ? {
2059
+ code: "custody-unsupported-version",
2060
+ reason: "unsupported-contract-version",
2061
+ accountValidatorHash: parsed.error.accountValidatorHash
2062
+ } : {
2063
+ code: "custody-context-mismatch",
2064
+ reason: "invalid-record",
2065
+ recordError: parsed.error
2066
+ });
2067
+ };
2068
+ const paymentKeyHashOf = (view) => {
2069
+ const credential = paymentCredentialOf(view);
2070
+ return credential?.type === require_Address.CredentialType.KeyHash ? credential.hash : void 0;
2071
+ };
2072
+ const externalInputOf = ({ input, output }, deviceKeyHash) => ({
2073
+ input,
2074
+ address: output.txOut.address,
2075
+ value: output.txOut.value,
2076
+ isDeviceKey: deviceKeyHash !== void 0 && paymentKeyHashOf(output) === deviceKeyHash
2077
+ });
2078
+ const validityOf = ({ validityInterval }, slotToPosixMs) => {
2079
+ const bounds = {};
2080
+ if (validityInterval?.invalidBefore !== void 0) {
2081
+ const slot = validityInterval.invalidBefore;
2082
+ bounds.invalidBefore = {
2083
+ slot,
2084
+ posixMs: slotToPosixMs(slot)
2085
+ };
2086
+ }
2087
+ if (validityInterval?.invalidHereafter !== void 0) {
2088
+ const slot = validityInterval.invalidHereafter;
2089
+ bounds.invalidHereafter = {
2090
+ slot,
2091
+ posixMs: slotToPosixMs(slot)
2092
+ };
2093
+ }
2094
+ return bounds;
2095
+ };
2096
+ /**
2097
+ * Reads every custody effect of a transaction from its bytes and the
2098
+ * caller's resolutions of its inputs, and refuses what Lace never signs.
2099
+ * The SDK checks its own builds with it before asking for a signature, and
2100
+ * the hosted signer derives its consent and its refusals from it, so both
2101
+ * apply one set of rules.
2102
+ *
2103
+ * It takes the `Serialization.Transaction` whose body hash is signed, so
2104
+ * what it reads is what a signature commits to. Resolutions come from the
2105
+ * caller and are not checked against the chain; an inline datum in a
2106
+ * resolution wins over a datum hash reported beside it. A custody account
2107
+ * is any address whose payment credential is a supported account validator
2108
+ * and whose stake credential is an inline script.
2109
+ *
2110
+ * Every operation on an account runs the logic its control datum names,
2111
+ * through a withdrawal from the logic's stake credential, and that logic
2112
+ * must be one of the contract version's catalogue. A reference input may
2113
+ * carry a reference script only as a contract script parked at the output
2114
+ * reference the version pins on the inspected network.
2115
+ *
2116
+ * Witness-set redeemers are bound to the signed body only through its
2117
+ * script data hash, which inspection does not recompute. A spend redeemer
2118
+ * must fit the UTxO it spends: `Device` the control UTxO, `SpendWithGrant`
2119
+ * or `SweepGrant` a grant UTxO, `Fund` a deposit. `SpendWithGrant` is
2120
+ * refused unless `allowGrantSpend` is set, and the mint redeemer must fit
2121
+ * the mint: `CreateAccount` a state NFT, `IssueGrants` grant tokens minted,
2122
+ * `BurnGrants` grant tokens burned. Another redeemer set under the same body
2123
+ * cannot change what the body does: it can only run a contract branch that
2124
+ * the device's signature does not authorise.
2125
+ */
2126
+ const inspectCustodyTransaction = (transaction, resolvedInputs, options) => {
2127
+ const record = readRecord(options);
2128
+ if (!record.ok) return require_hex.fail({ refusals: [record.error] });
2129
+ const serializedBody = transaction.body();
2130
+ const body = serializedBody.toCore();
2131
+ const resolved = resolveBody(body, resolvedInputs);
2132
+ if (!resolved.ok) return require_hex.fail({ refusals: resolved.error });
2133
+ const metadata = readCustodyMetadata(transaction, body);
2134
+ if (!metadata.ok) return require_hex.fail({ refusals: [metadata.error] });
2135
+ const facts = readTransaction({
2136
+ body,
2137
+ bodyCbor: serializedBody.toCbor(),
2138
+ witness: transaction.witnessSet().toCore()
2139
+ }, resolved.value, options);
2140
+ const { networkId } = options.chainId;
2141
+ const scope = {
2142
+ options,
2143
+ record: record.value,
2144
+ deviceKeyHash: options.deviceKeyHash?.toLowerCase()
2145
+ };
2146
+ const candidates = findCandidates(facts, record.value);
2147
+ const outcomes = candidates.map((candidate) => inspectAccount(candidate, facts, scope));
2148
+ const operated = outcomes.filter(({ isOperated }) => isOperated);
2149
+ const isOperatedAccount = (view) => operated.some(({ report }) => require_outputs.isAtAccount(view, report));
2150
+ const externalInputs = facts.inputs.filter(({ output }) => !isOperatedAccount(output)).map((view) => externalInputOf(view, scope.deviceKeyHash));
2151
+ const externalOutputs = facts.outputs.flatMap((view, outputIndex) => view.addressClass.kind === "other" ? [{
2152
+ outputIndex,
2153
+ address: view.txOut.address,
2154
+ value: view.txOut.value
2155
+ }] : []);
2156
+ const feeShares = feeSharesOf({
2157
+ externalInputs,
2158
+ externalOutputs,
2159
+ fee: body.fee
2160
+ });
2161
+ const refusals = inCheckOrder([
2162
+ ...checkBodyFields(facts),
2163
+ ...checkWithdrawalAccounts(facts),
2164
+ ...checkForeignStakeEffects(facts, candidates, networkId),
2165
+ ...checkMint(facts),
2166
+ ...checkRedeemerPointers(facts),
2167
+ ...checkSpendWitnesses(facts),
2168
+ ...checkLogicUse(facts, outcomes.flatMap(({ logics }) => logics)),
2169
+ ...checkReferenceScripts(facts, options.chainId.networkMagic),
2170
+ ...checkOutputs(facts, networkId),
2171
+ ...checkRecordAccount(operated.map(({ report }) => report), record.value),
2172
+ ...outcomes.flatMap((outcome) => outcome.refusals),
2173
+ ...checkGrantSpendMetadata(operated, metadata.value),
2174
+ ...checkLogicDraws(operated, feeShares, externalOutputs.some(({ address }) => feeShares.payers.includes(address)))
2175
+ ]);
2176
+ if (refusals.length > 0) return require_hex.fail({ refusals });
2177
+ return require_hex.ok({
2178
+ accounts: withOperations(operated, feeShares),
2179
+ deposits: facts.outputs.flatMap((view, outputIndex) => view.addressClass.kind === "account" && view.datumKind !== "hash" && !isOperatedAccount(view) ? [{
2180
+ outputIndex,
2181
+ address: view.txOut.address,
2182
+ value: view.txOut.value,
2183
+ accountValidatorHash: view.addressClass.contractVersion.accountValidatorHash,
2184
+ stakeScriptHash: view.addressClass.stakeScriptHash,
2185
+ datum: view.datumKind
2186
+ }] : []),
2187
+ externalOutputs,
2188
+ externalInputs,
2189
+ collateralInputs: facts.collaterals.map((view) => externalInputOf(view, scope.deviceKeyHash)),
2190
+ ...body.collateralReturn ? { collateralReturn: {
2191
+ address: body.collateralReturn.address,
2192
+ value: body.collateralReturn.value
2193
+ } } : {},
2194
+ requiredSigners: [...facts.requiredSigners],
2195
+ validity: validityOf(body, options.slotToPosixMs),
2196
+ fee: body.fee,
2197
+ feeShares,
2198
+ ...body.donation === void 0 ? {} : { donation: body.donation },
2199
+ ...metadata.value === void 0 ? {} : { metadata: metadata.value }
2200
+ });
2201
+ };
2202
+ //#endregion
2203
+ //#region ../../packages/lib/cardano-custody/src/analysis/requested-scope.ts
2204
+ /**
2205
+ * The recipient a bech32 Shelley payment address names, when the text is
2206
+ * spelled exactly as `toBech32` writes it: lower case, with the prefix of the
2207
+ * network its header names. Bech32 also decodes an upper-case spelling and
2208
+ * ignores the prefix, so a mainnet-looking address could otherwise stand for
2209
+ * a testnet recipient.
2210
+ */
2211
+ const canonicalRecipientOf = (value) => {
2212
+ if (typeof value !== "string" || !require_Address.Address.isValidBech32(value)) return;
2213
+ const address = require_Address.Address.fromBech32(value);
2214
+ const recipient = require_fees.custodyRecipientFromAddress(address);
2215
+ return recipient.ok && address.toBech32() === value ? {
2216
+ address,
2217
+ recipient: recipient.value
2218
+ } : void 0;
2219
+ };
2220
+ const recipientKeysOn = (recipients, networkId) => {
2221
+ const keys = [];
2222
+ for (const text of recipients) {
2223
+ const canonical = canonicalRecipientOf(text);
2224
+ if (canonical?.address.getNetworkId() !== networkId) return void 0;
2225
+ keys.push(require_fees.recipientKey(canonical.recipient));
2226
+ }
2227
+ return keys;
2228
+ };
2229
+ const hasSameRecipients = (requested, actual, networkId) => {
2230
+ const requestedKeys = recipientKeysOn(requested, networkId);
2231
+ const { recipients } = actual.scope;
2232
+ return requestedKeys !== void 0 && requestedKeys.length === recipients.length && requestedKeys.every((key, index) => key === require_fees.recipientKey(recipients[index]));
2233
+ };
2234
+ /**
2235
+ * The fields where a requested scope differs from a grant a transaction
2236
+ * issues, in declaration order; empty when they match. Every field must be
2237
+ * equal exactly. Recipients must be the same Plutus addresses in the same
2238
+ * order, and every requested recipient must be on the account network,
2239
+ * because the datum carries no network and the grant would otherwise send
2240
+ * to another network's address with the same credentials.
2241
+ */
2242
+ const compareRequestedGrantScope = (requested, actual, chainId) => {
2243
+ const { scope } = actual;
2244
+ const isEqual = {
2245
+ slot: requested.slot === actual.slot,
2246
+ grantee: requested.grantee === actual.grantee,
2247
+ asset: requested.asset.policyId === scope.asset.policyId && requested.asset.assetName === scope.asset.assetName,
2248
+ perCallCap: requested.perCallCap === scope.perCallCap,
2249
+ cap: requested.cap === scope.cap,
2250
+ lovelacePerCallCap: requested.lovelacePerCallCap === scope.lovelacePerCallCap,
2251
+ lovelaceCap: requested.lovelaceCap === scope.lovelaceCap,
2252
+ expiresAt: requested.expiresAt === scope.expiresAt,
2253
+ recipients: hasSameRecipients(requested.recipients, actual, chainId.networkId)
2254
+ };
2255
+ return Object.keys(isEqual).filter((field) => !isEqual[field]);
2256
+ };
2257
+ const serializeCustodyRequestedGrantScope = (scope) => ({
2258
+ slot: scope.slot.toString(),
2259
+ grantee: scope.grantee,
2260
+ asset: {
2261
+ policyId: scope.asset.policyId,
2262
+ assetName: scope.asset.assetName
2263
+ },
2264
+ perCallCap: scope.perCallCap.toString(),
2265
+ cap: scope.cap.toString(),
2266
+ lovelacePerCallCap: scope.lovelacePerCallCap.toString(),
2267
+ lovelaceCap: scope.lovelaceCap.toString(),
2268
+ expiresAt: scope.expiresAt.toString(),
2269
+ recipients: [...scope.recipients]
2270
+ });
2271
+ const SCOPE_FIELDS = [
2272
+ "slot",
2273
+ "grantee",
2274
+ "asset",
2275
+ "perCallCap",
2276
+ "cap",
2277
+ "lovelacePerCallCap",
2278
+ "lovelaceCap",
2279
+ "expiresAt",
2280
+ "recipients"
2281
+ ];
2282
+ const ASSET_FIELDS = ["policyId", "assetName"];
2283
+ const INTEGER_FIELDS = [
2284
+ "slot",
2285
+ "perCallCap",
2286
+ "cap",
2287
+ "lovelacePerCallCap",
2288
+ "lovelaceCap",
2289
+ "expiresAt"
2290
+ ];
2291
+ const MAX_ASSET_NAME_BYTES = 32;
2292
+ /** Canonical decimal: no sign, no leading zero, no exponent. */
2293
+ const NON_NEGATIVE_DECIMAL = /^(?:0|[1-9]\d*)$/;
2294
+ const isNonNegativeDecimal = (value) => typeof value === "string" && NON_NEGATIVE_DECIMAL.test(value);
2295
+ const malformed = (reason, field) => require_hex.fail({
2296
+ code: "malformed-requested-scope",
2297
+ reason,
2298
+ ...field === void 0 ? {} : { field }
2299
+ });
2300
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2301
+ const hasExactFields = (value, fields) => {
2302
+ const keys = Object.keys(value);
2303
+ return keys.length === fields.length && fields.every((field) => keys.includes(field));
2304
+ };
2305
+ const isShelleyPaymentAddress = (value) => canonicalRecipientOf(value) !== void 0;
2306
+ const isWellFormedAsset = (asset) => {
2307
+ const { policyId, assetName } = asset;
2308
+ if (policyId === "") return assetName === "";
2309
+ return require_hex.isHash28Hex(policyId) && require_hex.isLowerCaseHex(assetName) && assetName.length <= MAX_ASSET_NAME_BYTES * 2;
2310
+ };
2311
+ /**
2312
+ * Reads a requested scope from protocol JSON, refusing anything but the
2313
+ * exact shape `serializeCustodyRequestedGrantScope` writes: no missing or
2314
+ * extra field, canonical non-negative decimal integers, a positive expiry,
2315
+ * a 28-byte grantee, a lovelace or 28-byte policy asset with a name of at
2316
+ * most 32 bytes, and at most 8 Shelley payment addresses in canonical
2317
+ * bech32. Network membership of the recipients is checked when comparing.
2318
+ *
2319
+ * @param input - the scope as JSON text or as an already parsed value
2320
+ */
2321
+ const parseCustodyRequestedGrantScope = (input) => {
2322
+ let value = input;
2323
+ if (typeof input === "string") try {
2324
+ value = JSON.parse(input);
2325
+ } catch {
2326
+ return malformed("expected JSON");
2327
+ }
2328
+ if (!isPlainObject(value) || !hasExactFields(value, SCOPE_FIELDS)) return malformed(`expected exactly ${SCOPE_FIELDS.join(", ")}`);
2329
+ const integerField = INTEGER_FIELDS.find((field) => !isNonNegativeDecimal(value[field]));
2330
+ if (integerField) return malformed("expected a non-negative decimal string", integerField);
2331
+ if (value.expiresAt === "0") return malformed("expected a positive expiry", "expiresAt");
2332
+ if (!require_hex.isHash28Hex(value.grantee)) return malformed("expected 28 bytes of lower-case hex", "grantee");
2333
+ const { asset, recipients } = value;
2334
+ if (!isPlainObject(asset) || !hasExactFields(asset, ASSET_FIELDS) || !isWellFormedAsset(asset)) return malformed("expected lovelace or a policy id and asset name", "asset");
2335
+ if (!Array.isArray(recipients) || recipients.length > 8 || !recipients.every(isShelleyPaymentAddress)) return malformed(`expected at most 8 bech32 payment addresses`, "recipients");
2336
+ const integers = value;
2337
+ return require_hex.ok({
2338
+ slot: BigInt(integers.slot),
2339
+ grantee: value.grantee,
2340
+ asset: {
2341
+ policyId: asset.policyId,
2342
+ assetName: asset.assetName
2343
+ },
2344
+ perCallCap: BigInt(integers.perCallCap),
2345
+ cap: BigInt(integers.cap),
2346
+ lovelacePerCallCap: BigInt(integers.lovelacePerCallCap),
2347
+ lovelaceCap: BigInt(integers.lovelaceCap),
2348
+ expiresAt: BigInt(integers.expiresAt),
2349
+ recipients
2350
+ });
2351
+ };
2352
+ //#endregion
2353
+ //#region ../../packages/lib/cardano-custody/src/device-key.ts
2354
+ /**
2355
+ * The CIP-1854 path `m/1854'/1815'/a'/0/i` of custody device and creator
2356
+ * keys. The account index `a` belongs to the contract version and network:
2357
+ * `custodyDeviceAccountIndex` gives it.
2358
+ */
2359
+ const CUSTODY_DEVICE_KEY_PATH = {
2360
+ purpose: 1854,
2361
+ coinType: 1815,
2362
+ role: 0
2363
+ };
2364
+ /** First index that is a hardened derivation; device keys use soft ones only. */
2365
+ const HARDENED_INDEX = 2147483648;
2366
+ /**
2367
+ * The device key at `m/1854'/1815'/a'/0/index`, derived from the CIP-1854
2368
+ * account extended public key of account `a` by soft derivation, so no
2369
+ * secret is needed.
2370
+ */
2371
+ const deriveCustodyDeviceKey = async (accountPublicKey, index) => {
2372
+ if (!Number.isInteger(index) || index < 0 || index >= HARDENED_INDEX) return require_hex.fail({
2373
+ code: "invalid-device-index",
2374
+ index
2375
+ });
2376
+ await require_Address.ready();
2377
+ try {
2378
+ const publicKey = require_SodiumBip32Ed25519.Bip32PublicKey.fromHex(accountPublicKey).derive([CUSTODY_DEVICE_KEY_PATH.role, index]).toRawKey();
2379
+ return require_hex.ok({
2380
+ index,
2381
+ publicKey: publicKey.hex(),
2382
+ keyHash: publicKey.hash().hex()
2383
+ });
2384
+ } catch {
2385
+ return require_hex.fail({ code: "invalid-account-public-key" });
2386
+ }
2387
+ };
2388
+ /**
2389
+ * A short form of a device key hash for people to compare across devices:
2390
+ * its first and last 8 hex characters.
2391
+ */
2392
+ const custodyDeviceFingerprint = (keyHash) => `${keyHash.slice(0, 8)}…${keyHash.slice(-8)}`;
2393
+ //#endregion
2394
+ Object.defineProperty(exports, "CUSTODY_ACCOUNT_RECORD_TYPE", {
2395
+ enumerable: true,
2396
+ get: function() {
2397
+ return CUSTODY_ACCOUNT_RECORD_TYPE;
2398
+ }
2399
+ });
2400
+ Object.defineProperty(exports, "canonicalCustodyAccountRecord", {
2401
+ enumerable: true,
2402
+ get: function() {
2403
+ return canonicalCustodyAccountRecord;
2404
+ }
2405
+ });
2406
+ Object.defineProperty(exports, "compareRequestedGrantScope", {
2407
+ enumerable: true,
2408
+ get: function() {
2409
+ return compareRequestedGrantScope;
2410
+ }
2411
+ });
2412
+ Object.defineProperty(exports, "createCustodyAccountRecord", {
2413
+ enumerable: true,
2414
+ get: function() {
2415
+ return createCustodyAccountRecord;
2416
+ }
2417
+ });
2418
+ Object.defineProperty(exports, "custodyDeviceFingerprint", {
2419
+ enumerable: true,
2420
+ get: function() {
2421
+ return custodyDeviceFingerprint;
2422
+ }
2423
+ });
2424
+ Object.defineProperty(exports, "custodyGrantAfterSpend", {
2425
+ enumerable: true,
2426
+ get: function() {
2427
+ return custodyGrantAfterSpend;
2428
+ }
2429
+ });
2430
+ Object.defineProperty(exports, "deriveCustodyDeviceKey", {
2431
+ enumerable: true,
2432
+ get: function() {
2433
+ return deriveCustodyDeviceKey;
2434
+ }
2435
+ });
2436
+ Object.defineProperty(exports, "encodeCustodyAccountRedeemer", {
2437
+ enumerable: true,
2438
+ get: function() {
2439
+ return encodeCustodyAccountRedeemer;
2440
+ }
2441
+ });
2442
+ Object.defineProperty(exports, "encodeCustodyLogicRedeemer", {
2443
+ enumerable: true,
2444
+ get: function() {
2445
+ return encodeCustodyLogicRedeemer;
2446
+ }
2447
+ });
2448
+ Object.defineProperty(exports, "encodeCustodyMintRedeemer", {
2449
+ enumerable: true,
2450
+ get: function() {
2451
+ return encodeCustodyMintRedeemer;
2452
+ }
2453
+ });
2454
+ Object.defineProperty(exports, "encodeCustodyStakeRedeemer", {
2455
+ enumerable: true,
2456
+ get: function() {
2457
+ return encodeCustodyStakeRedeemer;
2458
+ }
2459
+ });
2460
+ Object.defineProperty(exports, "findCustodyScopeViolations", {
2461
+ enumerable: true,
2462
+ get: function() {
2463
+ return findCustodyScopeViolations;
2464
+ }
2465
+ });
2466
+ Object.defineProperty(exports, "inspectCustodyTransaction", {
2467
+ enumerable: true,
2468
+ get: function() {
2469
+ return inspectCustodyTransaction;
2470
+ }
2471
+ });
2472
+ Object.defineProperty(exports, "parseCustodyAccountRecord", {
2473
+ enumerable: true,
2474
+ get: function() {
2475
+ return parseCustodyAccountRecord;
2476
+ }
2477
+ });
2478
+ Object.defineProperty(exports, "parseCustodyRequestedGrantScope", {
2479
+ enumerable: true,
2480
+ get: function() {
2481
+ return parseCustodyRequestedGrantScope;
2482
+ }
2483
+ });
2484
+ Object.defineProperty(exports, "serializeCustodyRequestedGrantScope", {
2485
+ enumerable: true,
2486
+ get: function() {
2487
+ return serializeCustodyRequestedGrantScope;
2488
+ }
2489
+ });