@laceio/sdk 0.1.1-canary.3 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. package/dist/{TransactionBuilder-CCjOn7NA.cjs → TransactionBuilder-DrB3NVTi.cjs} +0 -42
  2. package/dist/{TransactionBuilder-CIzdw18M.js → TransactionBuilder-V6aLx4SV.js} +1 -1
  3. package/dist/{_polyfill-buffer-patch-CM8X7JiY.cjs → _polyfill-buffer-patch-BnPVerlG.cjs} +2 -21
  4. package/dist/account-guards.stable-B1DSSh9D.cjs +10 -0
  5. package/dist/account-guards.stable-JRQ5qx2V.js +5 -0
  6. package/dist/{activity-type-Ch6ljxBy.cjs → activity-type.stable-BJEfeLxL.cjs} +1 -2
  7. package/dist/{activity-type-GsJGWs-3.js → activity-type.stable-C76_hWd7.js} +1 -2
  8. package/dist/{address-book-address-validator-0J9XAADH.js → address-book-address-validator-DIUolI-s.js} +2 -2
  9. package/dist/{address-book-address-validator-Dte_YQ2L.cjs → address-book-address-validator-DNwQGlOB.cjs} +2 -2
  10. package/dist/bitcoin.cjs +0 -45
  11. package/dist/bitcoin.d.ts +1 -29906
  12. package/dist/bitcoin.js +0 -20
  13. package/dist/{blockfrost-tx-submit-provider-CLNQ5ycR.cjs → blockfrost-tx-submit-provider-BAIzV5o3.cjs} +167 -3
  14. package/dist/{blockfrost-tx-submit-provider-CBWQ0wK9.js → blockfrost-tx-submit-provider-DPTrapz-.js} +157 -5
  15. package/dist/{secret-box-DrjLZuS1.cjs → bytes.vo-Ck5u40RA.cjs} +35 -1
  16. package/dist/{secret-box-DUR0ACYz.js → bytes.vo-TgiwogWF.js} +25 -3
  17. package/dist/{cardano-network-id.vo-Cfpvbod9.js → cardano-network-id.vo-CK1tNQDg.js} +1 -1
  18. package/dist/{cardano-network-id.vo-BEDPn6Li.cjs → cardano-network-id.vo-DSxrFk9S.cjs} +1 -1
  19. package/dist/cardano.cjs +18 -22
  20. package/dist/cardano.d.ts +3774 -18959
  21. package/dist/cardano.js +11 -16
  22. package/dist/codecs-B7X9pAsK.cjs +1945 -0
  23. package/dist/codecs-CC1Xjntz.js +1882 -0
  24. package/dist/{const-D-eZIS4n.cjs → const-BDEg2Trq.cjs} +4 -4
  25. package/dist/{const-iwzZEE_w.cjs → const-BY8B3_0_.cjs} +1 -1
  26. package/dist/{const-BJfIK_G-.js → const-C84qHkjp.js} +4 -4
  27. package/dist/{const-CjmfjBP3.js → const-DXk2tFyv.js} +1 -1
  28. package/dist/{delegation-tx-builder-DZFjHDS8.js → delegation-tx-builder-CGVqlaFE.js} +5 -5
  29. package/dist/{delegation-tx-builder-CCjiakoy.cjs → delegation-tx-builder-CRpLP6jH.cjs} +6 -6
  30. package/dist/{deregistration-tx-builder-Bf6xAYp4.js → deregistration-tx-builder-B-CrUyX6.js} +5 -5
  31. package/dist/{deregistration-tx-builder-CQP35yex.cjs → deregistration-tx-builder-DZGwB4cz.cjs} +6 -6
  32. package/dist/{earn-rewards-tx-builder-Bsga3yFb.js → earn-rewards-tx-builder-BDbTDZmD.js} +5 -5
  33. package/dist/{earn-rewards-tx-builder-GuxofYns.cjs → earn-rewards-tx-builder-D5sUT5v8.cjs} +6 -6
  34. package/dist/{group-cardano-addresses-by-account-Bp0g4wEY.cjs → group-cardano-addresses-by-account-B533oVXR.cjs} +39 -3
  35. package/dist/{group-cardano-addresses-by-account-SR8MMrZf.js → group-cardano-addresses-by-account-RHKoEskR.js} +35 -5
  36. package/dist/{guarded-transaction-signer-MFpPg98D.cjs → guarded-transaction-signer-Bxl6_XNd.cjs} +18 -17
  37. package/dist/{guarded-transaction-signer-P6Rp1faj.js → guarded-transaction-signer-CpKLj8Kp.js} +15 -8
  38. package/dist/{in-memory-wallet-integration-t9I00R_V.cjs → in-memory-wallet-integration-CnLYeX4-.cjs} +108 -17
  39. package/dist/{in-memory-wallet-integration-CljprQQV.js → in-memory-wallet-integration-Croiuopx.js} +104 -7
  40. package/dist/{in-memory-wallet-integration-ChIOirMW.js → in-memory-wallet-integration-D-qn65Kx.js} +2 -5
  41. package/dist/{in-memory-wallet-integration-Cm6P8sk6.cjs → in-memory-wallet-integration-DVh9sRXa.cjs} +2 -3
  42. package/dist/index.cjs +47 -84
  43. package/dist/index.d.ts +13480 -40888
  44. package/dist/index.js +24 -52
  45. package/dist/{init-DUF4ar8P.cjs → init-B64zCLxG.cjs} +4 -4
  46. package/dist/{init-Y3mkB4XS.js → init-BAJJzBza.js} +2 -2
  47. package/dist/{init-EhzD7a8i.cjs → init-BJVU54pI.cjs} +1 -1
  48. package/dist/{init-D6BxBs7e.cjs → init-BKhH6mVR.cjs} +1 -1
  49. package/dist/{init-EP0CUWNK.cjs → init-BsCBAGWf.cjs} +2 -2
  50. package/dist/{init-Dy-05ktr.cjs → init-BtzMZ7Pk.cjs} +1 -1
  51. package/dist/init-Bv53hpLM.js +19 -0
  52. package/dist/{init-BopV9ed4.cjs → init-C-jErzp5.cjs} +2 -2
  53. package/dist/{init-Bp9GsPS_.cjs → init-C4Bkib9u.cjs} +56 -46
  54. package/dist/{init-lzHt4Cli.cjs → init-C7IeJwQR.cjs} +59 -256
  55. package/dist/{init-C-G32J12.cjs → init-CB__aLD7.cjs} +2 -2
  56. package/dist/{init-CyAY4Lzl.js → init-CFYsq82V.js} +1 -1
  57. package/dist/{init-0zzs9aqU.js → init-CLHuAMB-.js} +4 -4
  58. package/dist/{init-DA7zmptx.js → init-CNLGJJRx.js} +1 -1
  59. package/dist/{init-DS5qN63m.js → init-CR6tpw1B.js} +32 -24
  60. package/dist/init-CRbjI7p1.js +3494 -0
  61. package/dist/{init-CSDpdu0Q.cjs → init-CSHwHnfg.cjs} +2 -2
  62. package/dist/{init-CNM5m7M2.js → init-Cg9XdAru.js} +4 -4
  63. package/dist/{init-B3-1ghyx.js → init-CigestoM.js} +2 -2
  64. package/dist/{init-Dv3zKj0r.cjs → init-D6DBUsD3.cjs} +4 -4
  65. package/dist/init-DAna1KCg.cjs +3495 -0
  66. package/dist/{init-D74mn8nH2.js → init-DDdhguVC.js} +2 -2
  67. package/dist/init-DGe47B22.cjs +364 -0
  68. package/dist/init-DMtyvYfC.cjs +21 -0
  69. package/dist/{init-BrdVzxei.cjs → init-DX4WyB0R.cjs} +3 -3
  70. package/dist/{init-C8URA2OX.js → init-DcYY6zMD.js} +1 -1
  71. package/dist/{init-DFMiP2ZH.cjs → init-DkIc5nqF.cjs} +1 -1
  72. package/dist/{init-kEfUTuG2.js → init-Dku4b2lG.js} +1 -1
  73. package/dist/{init-NdhG_VrF.cjs → init-DxUHsN53.cjs} +6 -6
  74. package/dist/{init-CfTbSEKy.js → init-FMGDET-9.js} +60 -259
  75. package/dist/{init-DrkOb-wd.cjs → init-LfyMo-sS.cjs} +2 -2
  76. package/dist/{init-NCjgwCZy.js → init-Rxohwpmu.js} +2 -2
  77. package/dist/init-TkPDD750.js +363 -0
  78. package/dist/{init-C2RgrfZe.js → init-TkPq_Ny_.js} +1 -1
  79. package/dist/{init-5KcGcBb5.cjs → init-U8c9yHYx.cjs} +1 -1
  80. package/dist/{init-CX0AMQUn.js → init-aJHl-7AO.js} +2 -2
  81. package/dist/{init-rmt7-fQE.cjs → init-anYu6XB7.cjs} +2 -2
  82. package/dist/{init-Fw89ZmrR.js → init-w9GEs73W.js} +2 -2
  83. package/dist/{init-Dnsv155v.js → init-y2Ra0vZP.js} +3 -3
  84. package/dist/{init-DFjT8YvT.js → init-zSzCjoor.js} +4 -4
  85. package/dist/{is-retriable-error-BUiVJDwH.js → is-retriable-error-BU0e3hVI.js} +1 -1
  86. package/dist/{is-retriable-error-Dpt860if.cjs → is-retriable-error-CeeuHqDa.cjs} +1 -1
  87. package/dist/{lace-context-BZciB9c7.js → lace-context-CZcNHr4M.js} +4 -4
  88. package/dist/{lace-context-DgR6vLli.cjs → lace-context-fJTDghdp.cjs} +4 -4
  89. package/dist/{libsodium-wrappers-BhbOA8dQ.js → libsodium-wrappers-UH0LaOUE.js} +1647 -3
  90. package/dist/modules-cardano-BDvuL4Xx.js +5248 -0
  91. package/dist/modules-cardano-D2G3De2P.cjs +5432 -0
  92. package/dist/{retry-config-Dmgt28wM.js → retry-config-BcUmMS--.js} +1 -1
  93. package/dist/{retry-config-BaCfVC1H.cjs → retry-config-DC5IMaH7.cjs} +1 -1
  94. package/dist/{seam-cardano-B_etH4F4.js → seam-cardano-CATqBEyK.js} +1 -1
  95. package/dist/{signer-factory-DObB55D4.js → signer-factory-9URFliA5.js} +3 -6
  96. package/dist/{signer-factory-Ds6G2AZk.cjs → signer-factory-BWGUmnvm.cjs} +3 -4
  97. package/dist/{slice-qt0uBExh.js → slice-B2PhoWAg.js} +37 -325
  98. package/dist/{slice-DyNs7oWp.js → slice-BNu73oBa.js} +2 -2
  99. package/dist/{slice-GwiiJRoq.js → slice-CB4SnzMm.js} +3 -3
  100. package/dist/slice-CGVHXHDt.cjs +125 -0
  101. package/dist/{slice-CU80pbiu.js → slice-CnyWevM9.js} +3 -3
  102. package/dist/{slice-CwyYrh1H.js → slice-CobanYgn.js} +2 -2
  103. package/dist/{slice-7G2xxYKJ.cjs → slice-CuHUaPZx.cjs} +3 -3
  104. package/dist/{slice-DayJvMRD.cjs → slice-D11m2EKl.cjs} +1 -1
  105. package/dist/{slice-Bg6HEr3K.cjs → slice-DGcNrbPR.cjs} +4 -4
  106. package/dist/{slice-B4CdYoPu.cjs → slice-DZmQJNV8.cjs} +2 -2
  107. package/dist/slice-DhvF7L6X.js +107 -0
  108. package/dist/{slice-C9TT5KhY.cjs → slice-Do2R2CYW.cjs} +6 -6
  109. package/dist/{slice-Bs2aMERu.cjs → slice-DtaO72qY.cjs} +3 -3
  110. package/dist/{slice-C34iOOUH.cjs → slice-HeHCv3bl.cjs} +7 -7
  111. package/dist/{slice-D-9aPKG-.js → slice-d0p6-xj5.js} +6 -6
  112. package/dist/{slice-CK07EBCo.js → slice-eIwcwKHI.js} +4 -4
  113. package/dist/{slice-DBpJ2QGf.js → slice-maaskdUn.js} +6 -6
  114. package/dist/{slice-DTZTiJle.cjs → slice-mxLQ_FeY.cjs} +39 -352
  115. package/dist/{slice-DnG-yvpI.cjs → slice-oqm_r1cS.cjs} +2 -2
  116. package/dist/{slice-Da426sf9.js → slice-zJ9XVYjZ.js} +1 -1
  117. package/dist/{sync-operation-utils-31iFLExW.js → sync-operation-utils-BYXLO80m.js} +38 -2
  118. package/dist/{sync-operation-utils-COIZ66-I.cjs → sync-operation-utils-SdPbwFoD.cjs} +67 -1
  119. package/dist/{token-id-mapper-D29Ggv8h.js → token-id-mapper-B2ivlLv3.js} +2 -2
  120. package/dist/{token-id-mapper-CVyQJUKY.cjs → token-id-mapper-DsPAL6Nh.cjs} +2 -2
  121. package/dist/{util-Cp5cZkfK.js → util-BaLzwa32.js} +1 -1
  122. package/dist/{util-Bu4W5lF4.cjs → util-CGzqQ8IV.cjs} +1 -1
  123. package/dist/{utils-DsElZgCI.cjs → utils-5JvjbqH3.cjs} +1 -7
  124. package/dist/{utils-BfBEUvWA.js → utils-o8aMjfln.js} +2 -2
  125. package/dist/{vote-delegation-tx-builder-BUg1S2Ep.cjs → vote-delegation-tx-builder-BpEMSti4.cjs} +6 -6
  126. package/dist/{vote-delegation-tx-builder-C6FiS7zI.js → vote-delegation-tx-builder-CmKv6GoG.js} +5 -5
  127. package/package.json +4 -96
  128. package/dist/_polyfill-buffer-patch-yNxN4d5E.js +0 -1467
  129. package/dist/_web-crypto-random-bytes-Bo-GTcRS.js +0 -197
  130. package/dist/acc-module-4iIUv6My.cjs +0 -27654
  131. package/dist/acc-module-Cmj8EsIb.js +0 -27624
  132. package/dist/account-BOQruszJ.js +0 -6
  133. package/dist/account-BUOj4pYJ.cjs +0 -11
  134. package/dist/account-call-D68oF6bh.js +0 -267
  135. package/dist/account-call-DIIa2rK_.cjs +0 -314
  136. package/dist/account-call-NgLhYein.cjs +0 -4
  137. package/dist/account-call-n-Nkp35e.js +0 -3
  138. package/dist/account-guards-BtjTGxDZ.cjs +0 -42
  139. package/dist/account-guards-D9Lu5T3W.js +0 -36
  140. package/dist/account-tokens-DvkOFVON.js +0 -16
  141. package/dist/account-tokens-nwcvwLu4.cjs +0 -21
  142. package/dist/address-B9ntKL8K.js +0 -13761
  143. package/dist/address-BXp-mY8H.cjs +0 -11270
  144. package/dist/address-book-address-validator-DK0dnsRS.cjs +0 -41
  145. package/dist/address-book-address-validator-Di_ywo8M.js +0 -39
  146. package/dist/artefact-loader-54uK5RAa.js +0 -160
  147. package/dist/artefact-loader-DZPYrq-6.cjs +0 -183
  148. package/dist/auto-dismiss-failure-on-success-C-qoJuid.cjs +0 -37
  149. package/dist/auto-dismiss-failure-on-success-s8MGDEIu.js +0 -31
  150. package/dist/bip32-D6wd_QMn.cjs +0 -3851
  151. package/dist/bitcoin-data-provider-BYPSmuZS.js +0 -21
  152. package/dist/bitcoin-data-provider-w1oEDSU3.cjs +0 -26
  153. package/dist/bn-d3nSOIf7.cjs +0 -2553
  154. package/dist/browser-B1eMbDwh.js +0 -7468
  155. package/dist/bytes.vo-BB7_4JK6.js +0 -23
  156. package/dist/bytes.vo-BNRquqaO.cjs +0 -34
  157. package/dist/cardano-DfPwhwm-.js +0 -3
  158. package/dist/client-DXBLHk7K.cjs +0 -808
  159. package/dist/codecs-Cswkg7Tm.cjs +0 -4051
  160. package/dist/codecs-Dc1Krpbt.js +0 -3903
  161. package/dist/const-BhYOdznB.cjs +0 -42
  162. package/dist/const-BsnjG2_Q.cjs +0 -32071
  163. package/dist/const-CUnCl5U0.js +0 -13
  164. package/dist/const-D0CbQlo4.js +0 -154
  165. package/dist/contract-BD_312b0.js +0 -171
  166. package/dist/contract-DevBp9u8.cjs +0 -189
  167. package/dist/create-add-accounts-Brqk84vF.js +0 -102
  168. package/dist/create-add-accounts-DzXIh7Ot.cjs +0 -114
  169. package/dist/device-commitment-hex.vo-5wFtQvDS.js +0 -26
  170. package/dist/device-commitment-hex.vo-CT3RmAug.cjs +0 -31
  171. package/dist/device-readers-70i7tQqU.cjs +0 -8
  172. package/dist/device-readers-B7--ezKM.js +0 -4
  173. package/dist/device-readers-BWOmz9eD.js +0 -145
  174. package/dist/device-readers-CIgYZN7f.cjs +0 -168
  175. package/dist/dist-BMi5ZjEq.cjs +0 -43175
  176. package/dist/dist-BtIq4XFn.cjs +0 -535
  177. package/dist/dist-C3SHC_Ho.cjs +0 -1394
  178. package/dist/dist-CykEjYEX.cjs +0 -3814
  179. package/dist/dist-Dcsxh3be.cjs +0 -3452
  180. package/dist/dist-DqJfSlTd.cjs +0 -4413
  181. package/dist/dist-nTZ70qj7.cjs +0 -1306
  182. package/dist/enc-key-circuits-C8fGAMEF.cjs +0 -24
  183. package/dist/enc-key-circuits-DA2Xs3vZ.js +0 -13
  184. package/dist/english-Byb9M0E_.cjs +0 -2057
  185. package/dist/envelopes-BkH1Qdxk.js +0 -1620
  186. package/dist/envelopes-ETZEJS_O.cjs +0 -1721
  187. package/dist/errors-BZ8hnhCM.cjs +0 -584
  188. package/dist/errors-DIJTAsLa.js +0 -392
  189. package/dist/esm-HBSUQw3w.js +0 -384
  190. package/dist/esm-LZuaZjMf.cjs +0 -389
  191. package/dist/experimental.cjs +0 -7075
  192. package/dist/experimental.d.ts +0 -85525
  193. package/dist/experimental.js +0 -6917
  194. package/dist/gated-call-Boy6rXdC.js +0 -3
  195. package/dist/gated-call-DJWY299W.cjs +0 -736
  196. package/dist/gated-call-DjoRPqn1.js +0 -695
  197. package/dist/gated-call-SomdJ5nj.cjs +0 -4
  198. package/dist/grant-circuits-_1x--moV.cjs +0 -49
  199. package/dist/grant-circuits-swuzEdUl.js +0 -26
  200. package/dist/grant-rules-7Zqi_3BN.cjs +0 -7114
  201. package/dist/grant-rules-_xJHAT2M.js +0 -6833
  202. package/dist/grant-status-B2y49iIu.js +0 -2688
  203. package/dist/grant-status-DOr3t_u8.cjs +0 -2820
  204. package/dist/grants--b0LQQzZ.cjs +0 -266
  205. package/dist/grants-BIs6qm3b.js +0 -213
  206. package/dist/grants-CSgfYlOO.js +0 -5
  207. package/dist/grants-D61131nK.cjs +0 -17
  208. package/dist/hex-D9ITYbwt.cjs +0 -263
  209. package/dist/hex-mkXcV3pN.js +0 -191
  210. package/dist/http-client-B3N-TMxu.cjs +0 -167
  211. package/dist/http-client-TRlhDZGh.js +0 -155
  212. package/dist/identity-BMLuufva.cjs +0 -263
  213. package/dist/identity-DII6f-h7.js +0 -233
  214. package/dist/in-memory-wallet-integration-B2xzxORj.cjs +0 -20741
  215. package/dist/in-memory-wallet-integration-CBRiPjLO.js +0 -130
  216. package/dist/in-memory-wallet-integration-CULUB5Tf.cjs +0 -240
  217. package/dist/in-memory-wallet-integration-TM9bBphA.js +0 -20738
  218. package/dist/init-8v_n8boO.js +0 -1177
  219. package/dist/init-B5X_sGj9.cjs +0 -6
  220. package/dist/init-B8U3ABhS.cjs +0 -107
  221. package/dist/init-BJn-8_hf.js +0 -141
  222. package/dist/init-BmzfDaKL.js +0 -1142
  223. package/dist/init-BpHTq2R5.js +0 -105
  224. package/dist/init-BpSITBfF.js +0 -39
  225. package/dist/init-C00gw1zi.js +0 -422
  226. package/dist/init-CR7BfztQ.cjs +0 -18
  227. package/dist/init-Cp8EJGbm.js +0 -5
  228. package/dist/init-CuW7rHNA.js +0 -3584
  229. package/dist/init-Cz0rz5fU.cjs +0 -3585
  230. package/dist/init-D8OeObDF.cjs +0 -1141
  231. package/dist/init-D9g1HXYf.cjs +0 -40
  232. package/dist/init-DGjXwWs2.cjs +0 -143
  233. package/dist/init-DHrCbpkW.cjs +0 -121
  234. package/dist/init-DU5yYX5u.js +0 -2374
  235. package/dist/init-DlqJuHhl.js +0 -119
  236. package/dist/init-DrRdf5sZ.cjs +0 -1176
  237. package/dist/init-DuYENxci.js +0 -17
  238. package/dist/init-GsT2AEa4.cjs +0 -424
  239. package/dist/init-ZjNDDC-T.cjs +0 -2375
  240. package/dist/jubjub-authoriser-BxE3oN2y.js +0 -86
  241. package/dist/jubjub-authoriser-C6SDEP9I.cjs +0 -87
  242. package/dist/key-source-5oUBFiC7.cjs +0 -583
  243. package/dist/key-source-B08HukZg.js +0 -2550
  244. package/dist/keyDerivation-D3k9XyHg.js +0 -4077
  245. package/dist/keyDerivation-u0nBA93T.cjs +0 -274
  246. package/dist/lib--iqvW4O_.js +0 -2354
  247. package/dist/lib-Yk6Upl5D.cjs +0 -2359
  248. package/dist/midnight-account-id.vo-0x9X-r3l.cjs +0 -10
  249. package/dist/midnight-account-id.vo-DjCAGcqc.js +0 -5
  250. package/dist/midnight-engine-DpemplKO.js +0 -5
  251. package/dist/midnight-engine-xU1wotne.cjs +0 -6
  252. package/dist/midnight-wallet-DIsAlSn6.js +0 -18
  253. package/dist/midnight-wallet-XTZc2li7.cjs +0 -24
  254. package/dist/midnight.cjs +0 -1408
  255. package/dist/midnight.d.ts +0 -28934
  256. package/dist/midnight.js +0 -1352
  257. package/dist/modules-cardano-BrS68BbA.js +0 -995
  258. package/dist/modules-cardano-D843LvB0.cjs +0 -1108
  259. package/dist/node-relay-Cv0cNknM.js +0 -48
  260. package/dist/node-relay-DblH5fKv.cjs +0 -49
  261. package/dist/outputs-Bd2yZVjx.js +0 -720
  262. package/dist/outputs-CJoQBIA2.cjs +0 -990
  263. package/dist/pbkdf2-CIW-oWrm.cjs +0 -1337
  264. package/dist/pending-operation-B0_P78Jo.cjs +0 -26
  265. package/dist/pending-operation-BIS6mpjB.js +0 -21
  266. package/dist/providers-BLgJ0uQs.cjs +0 -366
  267. package/dist/providers-CwnT4vEE.js +0 -343
  268. package/dist/redacted-BmE2aXqr.js +0 -81
  269. package/dist/redacted-CRZJLS3w.cjs +0 -86
  270. package/dist/safe-buffer-B_vMqJWU.js +0 -43
  271. package/dist/safe-buffer-DYOwEfHB.cjs +0 -48
  272. package/dist/sdk-Dd0MKXFz.cjs +0 -1439
  273. package/dist/sdk-_yO75IHD.js +0 -1241
  274. package/dist/sdk-init-CYViTJY_.cjs +0 -3841
  275. package/dist/sdk-init-OMHyOGOK.js +0 -4138
  276. package/dist/send-proof-progress-C-ljq1x5.js +0 -30
  277. package/dist/send-proof-progress-vmsaSfbz.cjs +0 -47
  278. package/dist/side-effects-BnqYz7Nl.js +0 -768
  279. package/dist/side-effects-CngaeXi0.cjs +0 -804
  280. package/dist/sign-bitcoin-transaction-with-seed-D4z62-jV.js +0 -147
  281. package/dist/sign-bitcoin-transaction-with-seed-DsLYXnCY.cjs +0 -158
  282. package/dist/signer-3fk8lZtE.cjs +0 -7
  283. package/dist/signer-CsL28DrK.cjs +0 -121
  284. package/dist/signer-DGluibtn.js +0 -92
  285. package/dist/signer-RqB5pu3K.js +0 -2
  286. package/dist/signer-factory-BA7WFJVZ.js +0 -282
  287. package/dist/signer-factory-Cwd6o-lG.cjs +0 -281
  288. package/dist/signer-factory-DhZiE03y.cjs +0 -149
  289. package/dist/signer-factory-hzSRaIw2.js +0 -148
  290. package/dist/slice-BPNuHKfw.js +0 -9091
  291. package/dist/slice-CxKWDuBP.js +0 -87
  292. package/dist/slice-Czfp6t84.cjs +0 -436
  293. package/dist/slice-DnBWfA-k.cjs +0 -9109
  294. package/dist/slice-HjLXJyQG2.cjs +0 -105
  295. package/dist/slice-oWnc7YVD.js +0 -412
  296. package/dist/src-BDzYIksE.cjs +0 -20637
  297. package/dist/src-BFoefPv_.js +0 -20755
  298. package/dist/store-B1Lq3cEf.cjs +0 -66
  299. package/dist/store-DuBG8qh5.js +0 -37
  300. package/dist/submit-BBYjnZu8.cjs +0 -87
  301. package/dist/submit-D_yxYM_T.js +0 -70
  302. package/dist/submit-bitcoin-tx-BG3Gemov.js +0 -983
  303. package/dist/submit-bitcoin-tx-Y0CtvFN7.cjs +0 -1049
  304. package/dist/token-id-mapper-Bnds3xKH.cjs +0 -18
  305. package/dist/token-id-mapper-CnKHQ8DV.js +0 -16
  306. package/dist/tokens-Cevp7rdU.cjs +0 -62
  307. package/dist/tokens-Diw4MLRM.js +0 -44
  308. package/dist/types-B8uiJn-X.js +0 -12
  309. package/dist/types-CJT6XF2c.js +0 -10
  310. package/dist/types-CLRUYpfe.cjs +0 -23
  311. package/dist/types-DWbVZpnF.cjs +0 -15
  312. package/dist/types-T0cjaR75.js +0 -33
  313. package/dist/types-Zj78LtYu.cjs +0 -50
  314. package/dist/unwrap-provider-result-BZSiwabU.js +0 -32
  315. package/dist/unwrap-provider-result-ZqfBsTkk.cjs +0 -39
  316. package/dist/utils-DFwzEvbv.js +0 -65
  317. package/dist/utils-IXxR-Djh.cjs +0 -112
  318. package/dist/v8Signatures-CO_Uloy7.cjs +0 -71
  319. package/dist/watch-D-RoItM5.js +0 -21997
  320. package/dist/watch-DMF2s-II.cjs +0 -52281
  321. /package/dist/{account-id.vo-DwqFjK5u.cjs → account-id.vo-LvCBVn5r.cjs} +0 -0
  322. /package/dist/{account-id.vo-OPz2v-JZ.js → account-id.vo-MYSI7YFm.js} +0 -0
  323. /package/dist/{actions-iLXdgL5Y.js → actions-9QSQR-GB.js} +0 -0
  324. /package/dist/{actions-B2BJ4RTy.cjs → actions-DdGbj7c7.cjs} +0 -0
  325. /package/dist/{apply-vkey-witnesses-ZnFS0jnJ.cjs → apply-vkey-witnesses-CLzVSWxl.cjs} +0 -0
  326. /package/dist/{apply-vkey-witnesses-BKx3lcs0.js → apply-vkey-witnesses-ClZbpk5w.js} +0 -0
  327. /package/dist/{asyncoption-CruEYOBI.js → asyncoption-Cw07zPH7.js} +0 -0
  328. /package/dist/{asyncoption-i0VbqVTq.cjs → asyncoption-DYoGuQfP.cjs} +0 -0
  329. /package/dist/{bignum.vo-CEXOIew0.js → bignum.vo-B0rK_PQ1.js} +0 -0
  330. /package/dist/{bignum.vo-hx2kuqlA.cjs → bignum.vo-DsG3kNiZ.cjs} +0 -0
  331. /package/dist/{bip32ed25519-B7Yat217.cjs → bip32ed25519-1ApqeAZk.cjs} +0 -0
  332. /package/dist/{bip32ed25519-FQ8qvdcq.js → bip32ed25519-DetAdGwD.js} +0 -0
  333. /package/dist/{blake2b-DEeVUdKR.cjs → blake2b-B9tftbvG.cjs} +0 -0
  334. /package/dist/{blake2b-BiQTw3GC.js → blake2b-DOEVc57D.js} +0 -0
  335. /package/dist/{blockchain-network-id.vo-BAKEwGjG.cjs → blockchain-network-id.vo-BXpZhvvQ.cjs} +0 -0
  336. /package/dist/{blockchain-network-id.vo-CiVykuqG.js → blockchain-network-id.vo-DNRkuT5B.js} +0 -0
  337. /package/dist/{blockingWithLatestFrom-Cd8c8EPG.js → blockingWithLatestFrom-C5wp_uA-.js} +0 -0
  338. /package/dist/{blockingWithLatestFrom-D8kBPBMX.cjs → blockingWithLatestFrom-DY8OPg0w.cjs} +0 -0
  339. /package/dist/{cardano-network-id-to-network-type-C0ufJOyE.cjs → cardano-network-id-to-network-type-BDTF-HmK.cjs} +0 -0
  340. /package/dist/{cardano-network-id-to-network-type-BWwzeEtM.js → cardano-network-id-to-network-type-CrZfUcyP.js} +0 -0
  341. /package/dist/{cardano-tx-id.vo-TLjJZHCI.cjs → cardano-tx-id.vo-BPhV-AmZ.cjs} +0 -0
  342. /package/dist/{cardano-tx-id.vo-eVnKuzlm.js → cardano-tx-id.vo-DF9NzZXk.js} +0 -0
  343. /package/dist/{composite-signer-factory-m5cVs9h_.cjs → composite-signer-factory-BpF1mIYA.cjs} +0 -0
  344. /package/dist/{composite-signer-factory-CR8YHA1y.js → composite-signer-factory-_46QsNBz.js} +0 -0
  345. /package/dist/{error-DIZOY2Lw.js → error-BCp9NkPi.js} +0 -0
  346. /package/dist/{error-Cb3EnD0u.cjs → error-CNIquqT5.cjs} +0 -0
  347. /package/dist/{esm-browser-MG97cre9.js → esm-browser-C93Q-EOI.js} +0 -0
  348. /package/dist/{esm-browser-bweqfBlL.cjs → esm-browser-DTc9IK3D.cjs} +0 -0
  349. /package/dist/{feature-flag-key.vo-RlfHL3hq.cjs → feature-flag-key.vo-BPz-ZAp6.cjs} +0 -0
  350. /package/dist/{feature-flag-key.vo-DN_-4-sG.js → feature-flag-key.vo-ofrB6ZVQ.js} +0 -0
  351. /package/dist/{first-state-of-status-unPynOuM.js → first-state-of-status-DQnMT3ya.js} +0 -0
  352. /package/dist/{first-state-of-status-CbT7hPa4.cjs → first-state-of-status-OROkXcRv.cjs} +0 -0
  353. /package/dist/{init-zUoRG_EC2.cjs → init-C5CEZ1m0.cjs} +0 -0
  354. /package/dist/{init-DHaMxHM1.js → init-CKipPE_c.js} +0 -0
  355. /package/dist/{init-Atisw4x2.cjs → init-DFQQ9dTW.cjs} +0 -0
  356. /package/dist/{init-829HRYcz.js → init-Es3wHW7D.js} +0 -0
  357. /package/dist/{init-BgNobbNq.cjs → init-IAk_CkGa.cjs} +0 -0
  358. /package/dist/{init-atCFur0v.js → init-cqN3uX2j.js} +0 -0
  359. /package/dist/{is-redux-persist-state-BCFnSm36.cjs → is-redux-persist-state-CTn3WZVh.cjs} +0 -0
  360. /package/dist/{is-redux-persist-state-yBfq0h_E.js → is-redux-persist-state-Cxcd0_bM.js} +0 -0
  361. /package/dist/{key-value-store--W3SrMaw.js → key-value-store-8cMzeNyG.js} +0 -0
  362. /package/dist/{key-value-store-C2Xt5Emp.cjs → key-value-store-DUAepXQf.cjs} +0 -0
  363. /package/dist/{repo-slice-RxFm0zDZ.js → repo-slice-DwyMhCR1.js} +0 -0
  364. /package/dist/{repo-slice-7X4AYYVm.cjs → repo-slice-WxetRhMY.cjs} +0 -0
  365. /package/dist/{reselect-CGoLzEPv.js → reselect-C06X06oX.js} +0 -0
  366. /package/dist/{reselect-Bd94u0K-.cjs → reselect-CRDqmQY9.cjs} +0 -0
  367. /package/dist/{serializable-BHdKIgk3.js → serializable-B8GahgE1.js} +0 -0
  368. /package/dist/{serializable-BxHkoPdk.cjs → serializable-CRdnFaLZ.cjs} +0 -0
  369. /package/dist/{slice-obCSLm1x.js → slice-B87b52iM.js} +0 -0
  370. /package/dist/{slice-Bk0Sv05J.js → slice-Be7hj4Iy.js} +0 -0
  371. /package/dist/{slice-BiRsDKOK.cjs → slice-D11FHQuQ.cjs} +0 -0
  372. /package/dist/{slice-Dqw1ecum.cjs → slice-FioKeHtA.cjs} +0 -0
  373. /package/dist/{src-Cqp0B2M_.cjs → src-BTPIvlDI.cjs} +0 -0
  374. /package/dist/{src-BlxEp6Mm.js → src-vq1wS9sZ.js} +0 -0
  375. /package/dist/{time.vo-D3wWRw9L.cjs → time.vo-CQ8pSXz2.cjs} +0 -0
  376. /package/dist/{time.vo-tNheinXm.js → time.vo-DkYYGYp6.js} +0 -0
  377. /package/dist/{toEmpty-8Zax1MPd.js → toEmpty-CgAl3097.js} +0 -0
  378. /package/dist/{toEmpty-CYAbupM4.cjs → toEmpty-DAMmNB5r.cjs} +0 -0
  379. /package/dist/{token-id.vo-DXv5uS9n.cjs → token-id.vo-BxG9TG0O.cjs} +0 -0
  380. /package/dist/{token-id.vo-Bvwhr3ZE.js → token-id.vo-ClsroxHO.js} +0 -0
  381. /package/dist/{util-CzeB3hfa.cjs → util-CmdAhheh.cjs} +0 -0
  382. /package/dist/{util-C8a4fA9p.js → util-DxzlYNS5.js} +0 -0
  383. /package/dist/{util-selector-BWPH68_l.cjs → util-selector-BcAncU4L.cjs} +0 -0
  384. /package/dist/{util-selector-BSTsxgne.js → util-selector-CcK-GAdE.js} +0 -0
  385. /package/dist/{while-active-B0lWy_6l.js → while-active-BpppWuCs.js} +0 -0
  386. /package/dist/{while-active-Dsowp0Fi.cjs → while-active-Dhc2LwOm.cjs} +0 -0
@@ -1,2820 +0,0 @@
1
- const require_rolldown_runtime = require("./rolldown-runtime-Br2OG8yv.cjs");
2
- const require_hex = require("./hex-D9ITYbwt.cjs");
3
- const require_outputs = require("./outputs-CJoQBIA2.cjs");
4
- const require_tokens = require("./tokens-Cevp7rdU.cjs");
5
- const require_identity = require("./identity-BMLuufva.cjs");
6
- let _laceio_cardano_crypto = require("@laceio/cardano/crypto");
7
- _laceio_cardano_crypto = require_rolldown_runtime.__toESM(_laceio_cardano_crypto);
8
- let _laceio_cardano_core = require("@laceio/cardano/core");
9
- let _laceio_cardano_util = require("@laceio/cardano/util");
10
- let _laceio_cardano_tx_construction = require("@laceio/cardano/tx-construction");
11
- //#region ../../packages/lib/cardano-custody/src/analysis/transaction-facts.ts
12
- const outpointKey = ({ txId, index }) => `${txId.toLowerCase()}#${index}`;
13
- const toTxIn = ({ txId, index }) => ({
14
- txId,
15
- index
16
- });
17
- /** Byte order of lower-case hex, then index: how the ledger orders inputs. */
18
- const compareOutpoints = (a, b) => {
19
- const aId = a.txId.toLowerCase();
20
- const bId = b.txId.toLowerCase();
21
- if (aId !== bId) return aId < bId ? -1 : 1;
22
- return a.index - b.index;
23
- };
24
- /**
25
- * Pairs every input the body names with the caller's resolution. Every
26
- * input, reference input and collateral input must be resolved exactly once,
27
- * and no resolution may name an outpoint the body does not.
28
- */
29
- const resolveBody = (body, resolvedInputs) => {
30
- const refusals = [];
31
- const resolutions = /* @__PURE__ */ new Map();
32
- const sources = [
33
- [body.inputs, "inputs"],
34
- [body.referenceInputs ?? [], "referenceInputs"],
35
- [body.collaterals ?? [], "collaterals"]
36
- ];
37
- const named = new Set(sources.flatMap(([inputs]) => inputs.map(outpointKey)));
38
- for (const [input, txOut] of resolvedInputs) {
39
- const key = outpointKey(input);
40
- const view = require_outputs.viewOfOutput(txOut);
41
- if (!named.has(key)) refusals.push({
42
- code: "custody-context-mismatch",
43
- reason: "resolution-not-in-body",
44
- input: toTxIn(input)
45
- });
46
- else if (resolutions.has(key)) refusals.push({
47
- code: "custody-context-mismatch",
48
- reason: "duplicate-resolution",
49
- input: toTxIn(input)
50
- });
51
- else if (view.address === void 0) refusals.push({
52
- code: "custody-context-mismatch",
53
- reason: "invalid-resolution",
54
- input: toTxIn(input)
55
- });
56
- resolutions.set(key, view);
57
- }
58
- const resolve = (inputs, source) => inputs.flatMap((input) => {
59
- const output = resolutions.get(outpointKey(input));
60
- if (output) return [{
61
- input: toTxIn(input),
62
- output
63
- }];
64
- refusals.push({
65
- code: "custody-context-mismatch",
66
- reason: "unresolved-input",
67
- input: toTxIn(input),
68
- source
69
- });
70
- return [];
71
- });
72
- const [inputs, referenceInputs, collaterals] = sources.map(([list, source]) => resolve(list, source));
73
- return refusals.length > 0 ? require_hex.fail(refusals) : require_hex.ok({
74
- inputs: inputs.sort((a, b) => compareOutpoints(a.input, b.input)),
75
- referenceInputs,
76
- collaterals
77
- });
78
- };
79
- /** The key of the withdrawal map in a transaction body. */
80
- const WITHDRAWALS_KEY = 5n;
81
- /**
82
- * The reward accounts the withdrawal maps of a body name, read from its
83
- * bytes, repeats included: a repeated key, and a repeated map, are lost
84
- * once the body is decoded.
85
- */
86
- const withdrawalAccountsOf = (bodyCbor) => {
87
- const reader = new _laceio_cardano_core.Serialization.CborReader(bodyCbor);
88
- const accounts = [];
89
- reader.readStartMap();
90
- while (reader.peekState() !== _laceio_cardano_core.Serialization.CborReaderState.EndMap) {
91
- if (reader.readInt() !== WITHDRAWALS_KEY) {
92
- reader.skipValue();
93
- continue;
94
- }
95
- reader.readStartMap();
96
- while (reader.peekState() !== _laceio_cardano_core.Serialization.CborReaderState.EndMap) {
97
- accounts.push(_laceio_cardano_core.Cardano.Address.fromBytes(_laceio_cardano_util.HexBlob.fromBytes(reader.readByteString())).toBech32());
98
- reader.skipValue();
99
- }
100
- reader.readEndMap();
101
- }
102
- return accounts;
103
- };
104
- const readTransaction = ({ body, bodyCbor, witness }, resolved, options) => {
105
- const mint = new Map([...body.mint ?? /* @__PURE__ */ new Map()].map(([assetId, quantity]) => [assetId.toLowerCase(), quantity]));
106
- const { invalidBefore, invalidHereafter } = body.validityInterval ?? {};
107
- return {
108
- body,
109
- ...resolved,
110
- outputs: body.outputs.map(require_outputs.viewOfOutput),
111
- collateralReturn: body.collateralReturn ? require_outputs.viewOfOutput(body.collateralReturn) : void 0,
112
- redeemers: witness.redeemers ?? [],
113
- requiredSigners: new Set((body.requiredExtraSignatures ?? []).map((signer) => signer.toLowerCase())),
114
- mintPolicies: [...new Set([...mint.keys()].map(require_outputs.policyIdOf))].sort(),
115
- mint,
116
- lowerBoundMs: invalidBefore === void 0 ? void 0 : options.slotToPosixMs(invalidBefore),
117
- upperBoundMs: invalidHereafter === void 0 ? void 0 : options.slotToPosixMs(invalidHereafter),
118
- witnessScriptHashes: new Set((witness.scripts ?? []).map((script) => _laceio_cardano_core.Serialization.Script.fromCore(script).hash())),
119
- withdrawalAccounts: withdrawalAccountsOf(bodyCbor)
120
- };
121
- };
122
- const redeemerData = (facts, purpose, index) => facts.redeemers.find((redeemer) => redeemer.purpose === purpose && redeemer.index === index)?.data;
123
- const paymentCredentialOf = (view) => (view.address?.asBase() ?? view.address?.asEnterprise() ?? view.address?.asPointer())?.getPaymentCredential();
124
- const stakeCredentialOf = (certificate) => "stakeCredential" in certificate ? certificate.stakeCredential : void 0;
125
- const scriptHashOfRewardAccount = (rewardAccount) => {
126
- const credential = _laceio_cardano_core.Cardano.Address.fromString(rewardAccount)?.asReward()?.getPaymentCredential();
127
- return credential?.type === _laceio_cardano_core.Cardano.CredentialType.ScriptHash ? credential.hash : void 0;
128
- };
129
- //#endregion
130
- //#region ../../packages/lib/cardano-custody/src/analysis/grant-spend.ts
131
- const valueAt = (views, plutusAddressKey) => require_outputs.addValueDeltas(views.filter((view) => view.plutusAddressKey !== void 0 && view.plutusAddressKey === plutusAddressKey).map((view) => view.value));
132
- /** `leaving_value` over outputs that are already read. */
133
- const leavingValueAt = (inputs, outputs, plutusAddressKey) => require_outputs.addValueDeltas([valueAt(inputs, plutusAddressKey), require_outputs.negateValueDelta(valueAt(outputs, plutusAddressKey))]);
134
- /**
135
- * Every rule of `stays_within_scope` a net outflow breaks: the scoped asset
136
- * within its per call and remaining caps; for a token scope, lovelace within
137
- * both lovelace caps; and no other asset class leaving. A net inflow breaks
138
- * nothing.
139
- *
140
- * @param accountFee - the fee the account pays, which `leaving.lovelace`
141
- * already includes; each violation whose outflow is lovelace reports it
142
- * as `fee`
143
- */
144
- const findCustodyScopeViolations = (scope, leaving, accountFee) => {
145
- const violations = [];
146
- const isLovelaceScope = require_outputs.isCustodyLovelaceAsset(scope.asset);
147
- const lovelaceFee = accountFee === void 0 ? {} : { fee: accountFee };
148
- const assetFee = isLovelaceScope ? lovelaceFee : {};
149
- const assetOutflow = require_outputs.quantityOfAsset(leaving, scope.asset);
150
- if (assetOutflow > scope.perCallCap) violations.push({
151
- code: "per-call-cap-exceeded",
152
- outflow: assetOutflow,
153
- limit: scope.perCallCap,
154
- ...assetFee
155
- });
156
- if (assetOutflow > scope.cap) violations.push({
157
- code: "cap-exceeded",
158
- outflow: assetOutflow,
159
- limit: scope.cap,
160
- ...assetFee
161
- });
162
- if (!isLovelaceScope) {
163
- if (leaving.lovelace > scope.lovelacePerCallCap) violations.push({
164
- code: "lovelace-per-call-cap-exceeded",
165
- outflow: leaving.lovelace,
166
- limit: scope.lovelacePerCallCap,
167
- ...lovelaceFee
168
- });
169
- if (leaving.lovelace > scope.lovelaceCap) violations.push({
170
- code: "lovelace-cap-exceeded",
171
- outflow: leaving.lovelace,
172
- limit: scope.lovelaceCap,
173
- ...lovelaceFee
174
- });
175
- }
176
- for (const [assetId, quantity] of leaving.assets) if (quantity > 0n && !require_outputs.isAssetIdOf(assetId, scope.asset)) violations.push({
177
- code: "foreign-asset-outflow",
178
- assetId,
179
- outflow: quantity
180
- });
181
- return violations;
182
- };
183
- const clampedOutflow = (quantity) => quantity > 0n ? quantity : 0n;
184
- /**
185
- * `grant_after_spend`: the grant with `cap`, and for a token grant
186
- * `lovelaceCap`, lowered by the net outflow of its asset, never raised by a
187
- * net deposit. These are the most the recreated grant may keep: the
188
- * contract accepts any cap from zero to this value, so a builder may write
189
- * lower caps that already count a fee it has not yet computed.
190
- */
191
- const custodyGrantAfterSpend = (grant, leaving) => {
192
- const { scope } = grant;
193
- return {
194
- ...grant,
195
- scope: {
196
- ...scope,
197
- cap: scope.cap - clampedOutflow(require_outputs.quantityOfAsset(leaving, scope.asset)),
198
- lovelaceCap: require_outputs.isCustodyLovelaceAsset(scope.asset) ? scope.lovelaceCap : scope.lovelaceCap - clampedOutflow(leaving.lovelace)
199
- }
200
- };
201
- };
202
- /** `ends_before_expiry`: a finite upper bound no later than the expiry. */
203
- const findExpiryViolation = (upperBoundMs, expiresAt) => {
204
- if (upperBoundMs === void 0) return { code: "no-upper-bound" };
205
- return Number.isSafeInteger(upperBoundMs) && BigInt(upperBoundMs) <= expiresAt ? void 0 : {
206
- code: "expired",
207
- upperBoundMs,
208
- expiresAt
209
- };
210
- };
211
- const findMisplacedAccountTokens = (facts) => [["input", facts.inputs], ["output", facts.outputs]].flatMap(([kind, views]) => views.flatMap((view, index) => require_outputs.holdsMisplacedAccountToken(view, [facts.account.accountValidatorHash]) ? [{
212
- code: "account-token-misplaced",
213
- location: {
214
- kind,
215
- index
216
- }
217
- }] : []));
218
- /** `pays_only_recipients` and `deposits_are_plain`, per output. */
219
- const findOutputViolations = (facts, grant) => {
220
- const { recipients } = grant.scope;
221
- const recipientKeys = new Set(recipients.map(require_outputs.recipientKey));
222
- const { plutusAddressKey } = facts.grantInput;
223
- return facts.outputs.flatMap((view, outputIndex) => {
224
- const violations = [];
225
- const isBack = view.plutusAddressKey !== void 0 && view.plutusAddressKey === plutusAddressKey;
226
- if (recipients.length > 0 && !isBack && (view.plutusAddressKey === void 0 || !recipientKeys.has(view.plutusAddressKey))) violations.push({
227
- code: "recipient-not-allowed",
228
- outputIndex
229
- });
230
- if (isBack && !require_outputs.holdsAccountToken(view, facts.account) && (view.datumKind !== "none" || view.hasReferenceScript)) violations.push({
231
- code: "deposit-not-plain",
232
- outputIndex
233
- });
234
- return violations;
235
- });
236
- };
237
- /** `referenced_control`: the account's control UTxO among the reference inputs, exactly once. */
238
- const findReferencedControl = (facts) => {
239
- const controls = facts.referenceInputs.filter((view) => require_outputs.isControlOutput(view, facts.account));
240
- if (controls.length !== 1) return { violations: [{
241
- code: "referenced-control-count",
242
- count: controls.length
243
- }] };
244
- const state = require_outputs.decodedInlineState(controls[0]);
245
- return state ? {
246
- state,
247
- violations: []
248
- } : { violations: [{ code: "referenced-control-datum" }] };
249
- };
250
- /** `is_current`, naming why a grant is not current against the referenced state. */
251
- const findCurrencyViolation = (grant, state) => {
252
- if (grant.generation < state.grantGeneration) return {
253
- code: "grant-not-current",
254
- liveness: "superseded"
255
- };
256
- if (grant.generation > state.grantGeneration) return {
257
- code: "grant-not-current",
258
- liveness: "future-generation"
259
- };
260
- return state.revoked.includes(grant.slot) ? {
261
- code: "grant-not-current",
262
- liveness: "revoked"
263
- } : void 0;
264
- };
265
- const isSameExceptCaps = (actual, expected) => {
266
- const a = actual.scope;
267
- const b = expected.scope;
268
- 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_outputs.recipientKey(recipient) === require_outputs.recipientKey(b.recipients[index]));
269
- };
270
- const isSameValue = (a, b) => a.lovelace === b.lovelace && a.assets.size === b.assets.size && [...a.assets].every(([assetId, quantity]) => b.assets.get(assetId) === quantity);
271
- /**
272
- * `find_grant_output` and the checks on it: one output holding the grant
273
- * token, at the spent UTxO's address with its exact value and no reference
274
- * script, carrying the grant with every field kept but caps lowered within
275
- * `carries_grant_within`.
276
- */
277
- const findGrantOutputViolations = (facts, grant, expected) => {
278
- const assetId = require_outputs.grantAssetIdOf(facts.account, grant.slot);
279
- const holders = facts.outputs.flatMap((view, outputIndex) => assetId !== void 0 && view.value.assets.get(assetId) === 1n ? [{
280
- view,
281
- outputIndex
282
- }] : []);
283
- if (holders.length !== 1) return [{
284
- code: "grant-output-count",
285
- count: holders.length
286
- }];
287
- const [{ view, outputIndex }] = holders;
288
- const violations = [];
289
- if (view.plutusAddressKey === void 0 || view.plutusAddressKey !== facts.grantInput.plutusAddressKey) violations.push({
290
- code: "grant-output-address",
291
- outputIndex
292
- });
293
- if (!isSameValue(view.value, facts.grantInput.value)) violations.push({
294
- code: "grant-output-value",
295
- outputIndex
296
- });
297
- if (view.hasReferenceScript) violations.push({
298
- code: "grant-output-reference-script",
299
- outputIndex
300
- });
301
- if (view.inlineDatum === void 0) return [...violations, {
302
- code: "grant-output-datum",
303
- outputIndex,
304
- reason: "not-inline"
305
- }];
306
- const actual = require_outputs.decodedInlineGrant(view);
307
- if (!actual) return [...violations, {
308
- code: "grant-output-datum",
309
- outputIndex,
310
- reason: "malformed"
311
- }];
312
- if (!isSameExceptCaps(actual, expected)) violations.push({
313
- code: "grant-output-datum",
314
- outputIndex,
315
- reason: "field-changed"
316
- });
317
- for (const field of ["cap", "lovelaceCap"]) {
318
- const capActual = actual.scope[field];
319
- const capExpected = expected.scope[field];
320
- if (capActual < 0n || capActual > capExpected) violations.push({
321
- code: "grant-output-cap",
322
- outputIndex,
323
- field,
324
- actual: capActual,
325
- expected: capExpected
326
- });
327
- }
328
- return violations;
329
- };
330
- /** Every violation of a grant spend whose views are already read. */
331
- const grantSpendViolationsOf = (facts) => {
332
- const grant = require_outputs.decodedInlineGrant(facts.grantInput);
333
- if (!grant) return [{ code: "grant-datum-invalid" }];
334
- const violations = [];
335
- if (!require_outputs.holdsOnlyLovelaceAndGrantToken(facts.grantInput.value, facts.account, grant.slot)) violations.push({ code: "grant-input-value" });
336
- const tokenInputs = facts.inputs.filter((view) => require_outputs.holdsAccountToken(view, facts.account)).length;
337
- if (tokenInputs !== 1) violations.push({
338
- code: "account-token-inputs",
339
- count: tokenInputs
340
- });
341
- const control = findReferencedControl(facts);
342
- violations.push(...control.violations);
343
- const currency = control.state && findCurrencyViolation(grant, control.state);
344
- if (currency) violations.push(currency);
345
- if (!facts.requiredSigners.has(grant.grantee)) violations.push({
346
- code: "grantee-not-signer",
347
- grantee: grant.grantee
348
- });
349
- const expiry = findExpiryViolation(facts.upperBoundMs, grant.scope.expiresAt);
350
- if (expiry) violations.push(expiry);
351
- const leaving = leavingValueAt(facts.inputs, facts.outputs, facts.grantInput.plutusAddressKey);
352
- return [
353
- ...violations,
354
- ...findCustodyScopeViolations(grant.scope, leaving),
355
- ...findOutputViolations(facts, grant),
356
- ...findMisplacedAccountTokens(facts),
357
- ...findGrantOutputViolations(facts, grant, custodyGrantAfterSpend(grant, leaving)),
358
- ...[...facts.mint].filter(([assetId]) => require_outputs.policyIdOf(assetId) === facts.account.accountValidatorHash).map(([assetId, quantity]) => ({
359
- code: "mint-under-policy",
360
- assetId,
361
- quantity
362
- }))
363
- ];
364
- };
365
- //#endregion
366
- //#region ../../packages/lib/cardano-custody/src/record.ts
367
- const CUSTODY_ACCOUNT_RECORD_TYPE = "cardano-custody-account";
368
- const createCustodyAccountRecord = (identity) => canonicalCustodyAccountRecord({
369
- type: CUSTODY_ACCOUNT_RECORD_TYPE,
370
- version: 1,
371
- networkMagic: identity.chainId.networkMagic,
372
- accountValidatorHash: identity.contractVersion.accountValidatorHash,
373
- creatorKeyHash: identity.creatorKeyHash,
374
- stakeScriptHash: identity.stakeScriptHash,
375
- address: identity.address
376
- });
377
- /** A copy holding exactly the record fields, in their canonical order. */
378
- const canonicalCustodyAccountRecord = (record) => ({
379
- type: record.type,
380
- version: record.version,
381
- networkMagic: record.networkMagic,
382
- accountValidatorHash: record.accountValidatorHash,
383
- creatorKeyHash: record.creatorKeyHash,
384
- stakeScriptHash: record.stakeScriptHash,
385
- address: record.address
386
- });
387
- const RECORD_FIELDS = Object.keys(canonicalCustodyAccountRecord({}));
388
- const UPSTREAM_RECORD_FIELDS = [
389
- "owner",
390
- "stakeScriptHash",
391
- "address"
392
- ];
393
- const malformed$1 = (reason, field) => ({
394
- step: 1,
395
- code: "malformed-record",
396
- reason,
397
- ...field === void 0 ? {} : { field }
398
- });
399
- const parseJson = (input) => {
400
- if (typeof input !== "string") return input;
401
- try {
402
- return JSON.parse(input);
403
- } catch {
404
- return;
405
- }
406
- };
407
- const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
408
- const hasExactFields$1 = (value, fields) => {
409
- const keys = Object.keys(value);
410
- return keys.length === fields.length && fields.every((field) => keys.includes(field));
411
- };
412
- const parseAddress = (value) => typeof value === "string" && _laceio_cardano_core.Cardano.Address.isValidBech32(value) ? _laceio_cardano_core.Cardano.Address.fromBech32(value) : null;
413
- const HASH_FIELDS = [
414
- "accountValidatorHash",
415
- "creatorKeyHash",
416
- "stakeScriptHash"
417
- ];
418
- const readRecordClaims = (value) => {
419
- if (!hasExactFields$1(value, RECORD_FIELDS)) return require_hex.fail(malformed$1(`expected exactly ${RECORD_FIELDS.join(", ")}`));
420
- if (value.type !== "cardano-custody-account") return require_hex.fail(malformed$1("unknown record type", "type"));
421
- if (value.version !== 1) return require_hex.fail(malformed$1("unsupported record version", "version"));
422
- if (!require_identity.isNetworkMagic(value.networkMagic)) return require_hex.fail(malformed$1("expected a network magic", "networkMagic"));
423
- const invalidHash = HASH_FIELDS.find((field) => !require_hex.isHash28Hex(value[field]));
424
- if (invalidHash) return require_hex.fail(malformed$1("expected 28 bytes of lower-case hex", invalidHash));
425
- const address = parseAddress(value.address);
426
- if (!address) return require_hex.fail(malformed$1("expected an address", "address"));
427
- return require_hex.ok({
428
- networkMagic: value.networkMagic,
429
- accountValidatorHash: value.accountValidatorHash,
430
- creatorKeyHash: value.creatorKeyHash,
431
- stakeScriptHash: value.stakeScriptHash,
432
- address,
433
- addressText: value.address
434
- });
435
- };
436
- /**
437
- * Claims of the upstream `{ owner, stakeScriptHash, address }` record. It
438
- * names no network or contract version: the network is the selected one, and
439
- * the account validator is the address's payment script.
440
- */
441
- const readUpstreamRecordClaims = (value, selectedNetworkMagic) => {
442
- if (!hasExactFields$1(value, UPSTREAM_RECORD_FIELDS)) return require_hex.fail(malformed$1(`expected exactly ${UPSTREAM_RECORD_FIELDS.join(", ")}`));
443
- const invalidHash = ["owner", "stakeScriptHash"].find((field) => !require_hex.isHash28Hex(value[field]));
444
- if (invalidHash) return require_hex.fail(malformed$1("expected 28 bytes of lower-case hex", invalidHash));
445
- const address = parseAddress(value.address);
446
- const payment = address?.asBase()?.getPaymentCredential();
447
- if (!address || payment?.type !== _laceio_cardano_core.Cardano.CredentialType.ScriptHash) return require_hex.fail(malformed$1("expected a script base address", "address"));
448
- return require_hex.ok({
449
- networkMagic: selectedNetworkMagic,
450
- accountValidatorHash: payment.hash,
451
- creatorKeyHash: value.owner,
452
- stakeScriptHash: value.stakeScriptHash,
453
- address,
454
- addressText: value.address
455
- });
456
- };
457
- const readClaims = (input, selectedNetworkMagic) => {
458
- const value = parseJson(input);
459
- if (!isPlainObject$1(value)) return require_hex.fail(malformed$1("expected a JSON object"));
460
- return "owner" in value ? readUpstreamRecordClaims(value, selectedNetworkMagic) : readRecordClaims(value);
461
- };
462
- /**
463
- * Validates an imported record, in order: (1) known type, version and field
464
- * shapes; (2) a supported contract version; (3) the stake script hash
465
- * re-derived from the creator key hash; (4) the address re-derived for the
466
- * record's network, byte for byte; (5) the record's network is the selected
467
- * one. Step 3 is what proves the record names a genuine custody account: the
468
- * mint accepts any stake script.
469
- *
470
- * Also accepts the upstream `{ owner, stakeScriptHash, address }` record,
471
- * taking the network from `selectedNetworkMagic`.
472
- *
473
- * @param input - the record as JSON text or as an already parsed value
474
- */
475
- const parseCustodyAccountRecord = (input, selectedNetworkMagic) => {
476
- const claims = readClaims(input, selectedNetworkMagic);
477
- if (!claims.ok) return claims;
478
- const { networkMagic, accountValidatorHash, creatorKeyHash, stakeScriptHash, address, addressText } = claims.value;
479
- const contractVersion = require_hex.findCustodyContractVersion(accountValidatorHash);
480
- if (!contractVersion.ok) return require_hex.fail({
481
- step: 2,
482
- ...contractVersion.error
483
- });
484
- const identity = require_identity.identityOfValidatedCreator({
485
- contractVersion: contractVersion.value,
486
- creatorKeyHash,
487
- chainId: require_identity.chainIdFromNetworkMagic(networkMagic)
488
- });
489
- if (identity.stakeScriptHash !== stakeScriptHash) return require_hex.fail({
490
- step: 3,
491
- code: "stake-script-hash-mismatch",
492
- expected: identity.stakeScriptHash,
493
- actual: stakeScriptHash
494
- });
495
- const derivedAddress = identity.address;
496
- if (_laceio_cardano_core.Cardano.Address.fromBech32(derivedAddress).toBytes() !== address.toBytes()) return require_hex.fail({
497
- step: 4,
498
- code: "address-mismatch",
499
- expected: derivedAddress,
500
- actual: addressText
501
- });
502
- if (networkMagic !== selectedNetworkMagic) return require_hex.fail({
503
- step: 5,
504
- code: "network-mismatch",
505
- expected: selectedNetworkMagic,
506
- actual: networkMagic
507
- });
508
- return require_hex.ok({
509
- record: createCustodyAccountRecord(identity),
510
- identity
511
- });
512
- };
513
- //#endregion
514
- //#region ../../packages/lib/cardano-custody/src/codecs/redeemers.ts
515
- /** Constructor order of `AccountRedeemer`; no constructor has fields. */
516
- const ACCOUNT_REDEEMER_KINDS = [
517
- "device",
518
- "spendWithGrant",
519
- "sweepGrant",
520
- "fund"
521
- ];
522
- /** Constructor order of `MintRedeemer`; no constructor has fields. */
523
- const MINT_REDEEMER_KINDS = [
524
- "createAccount",
525
- "issueGrants",
526
- "burnGrants"
527
- ];
528
- /** Decodes a union whose constructors all have no fields. */
529
- const decodeFieldless = (data, kinds, what) => require_outputs.decodeResult(() => {
530
- const index = require_outputs.expectConstrIndex(data, "", what);
531
- const kind = kinds[Number(index)];
532
- if (kind === void 0) return require_outputs.unknownAlternative("", `${what} of ${kinds.join(", ")}`, index);
533
- require_outputs.expectConstr(data, {
534
- index: Number(index),
535
- arity: 0
536
- }, {
537
- path: "",
538
- what
539
- });
540
- return { kind };
541
- });
542
- /** `Device` is constructor 0, `SpendWithGrant` 1, `SweepGrant` 2 and `Fund` 3. */
543
- const encodeCustodyAccountRedeemer = ({ kind }) => require_outputs.constr(ACCOUNT_REDEEMER_KINDS.indexOf(kind));
544
- const decodeCustodyAccountRedeemer = (data) => decodeFieldless(data, ACCOUNT_REDEEMER_KINDS, "an account redeemer");
545
- /** `CreateAccount` is constructor 0, `IssueGrants` 1 and `BurnGrants` 2. */
546
- const encodeCustodyMintRedeemer = ({ kind }) => require_outputs.constr(MINT_REDEEMER_KINDS.indexOf(kind));
547
- const decodeCustodyMintRedeemer = (data) => decodeFieldless(data, MINT_REDEEMER_KINDS, "a mint redeemer");
548
- /** `Operate`: constructor 0 with no fields. */
549
- const encodeCustodyStakeRedeemer = () => require_outputs.constr(0);
550
- /**
551
- * `Run`, the redeemer of a logic script's withdraw and publish handlers:
552
- * constructor 0 with no fields.
553
- */
554
- const encodeCustodyLogicRedeemer = () => require_outputs.constr(0);
555
- //#endregion
556
- //#region ../../packages/lib/cardano-custody/src/codecs/reserve.ts
557
- /** The datum the upstream library writes on a reserve: constructor 0 with no fields. */
558
- const RESERVE_DATUM_CBOR = "d87980";
559
- /**
560
- * Whether an inline datum is, byte for byte, the reserve datum `d87980`.
561
- * Any datum makes a deposit an owner-only reserve; only this one has a known
562
- * size, so a builder can spend such a reserve without a surprise in fee or
563
- * minimum lovelace.
564
- */
565
- const isCustodyReserveDatum = (datum) => _laceio_cardano_core.Serialization.PlutusData.fromCore(datum).toCbor() === RESERVE_DATUM_CBOR;
566
- //#endregion
567
- //#region ../../packages/lib/cardano-custody/src/analysis/fee-shares.ts
568
- const lovelaceOf = (items) => items.reduce((total, { value }) => total + value.coins, 0n);
569
- const positivePart = (quantity) => quantity > 0n ? quantity : 0n;
570
- /** How the fee splits between the external inputs and the operated accounts. */
571
- const feeSharesOf = ({ externalInputs, externalOutputs, fee }) => {
572
- const payers = new Set(externalInputs.map(({ address }) => address));
573
- const beyondChange = lovelaceOf(externalInputs) - lovelaceOf(externalOutputs.filter(({ address }) => payers.has(address)));
574
- const externalShare = beyondChange < fee ? positivePart(beyondChange) : fee;
575
- return {
576
- payers: [...payers],
577
- externalShare,
578
- accountShare: fee - externalShare
579
- };
580
- };
581
- /**
582
- * The part of the accounts' fee share each operated account pays, from the
583
- * lovelace each gives up. Each pays only what the others cannot cover, so an
584
- * account is named as paying someone whenever some split of the fee has it
585
- * do so. A single account pays the whole share.
586
- */
587
- const accountFeeSharesOf = (givenUp, accountShare) => {
588
- const total = givenUp.reduce((sum, lovelace) => sum + positivePart(lovelace), 0n);
589
- return givenUp.map((lovelace) => positivePart(accountShare - (total - positivePart(lovelace))));
590
- };
591
- //#endregion
592
- //#region ../../packages/lib/cardano-custody/src/analysis/state-change.ts
593
- const isSameOrder = (before, after) => {
594
- const kept = before.filter((item) => after.includes(item));
595
- const keptAfter = after.filter((item) => before.includes(item));
596
- return kept.every((item, index) => item === keptAfter[index]);
597
- };
598
- const counterChange = (before, after) => before === after ? void 0 : {
599
- before,
600
- after
601
- };
602
- const diffCustodyStates = (before, after) => {
603
- const generation = counterChange(before.grantGeneration, after.grantGeneration);
604
- const nextSlot = counterChange(before.nextSlot, after.nextSlot);
605
- const outstanding = counterChange(before.outstanding, after.outstanding);
606
- return {
607
- before,
608
- after,
609
- ...before.logic === after.logic ? {} : { logic: {
610
- before: before.logic,
611
- after: after.logic
612
- } },
613
- devicesAdded: after.devices.filter((device) => !before.devices.includes(device)),
614
- devicesRemoved: before.devices.filter((device) => !after.devices.includes(device)),
615
- revokedAdded: after.revoked.filter((slot) => !before.revoked.includes(slot)),
616
- revokedRemoved: before.revoked.filter((slot) => !after.revoked.includes(slot)),
617
- ...generation ? { grantGeneration: generation } : {},
618
- ...nextSlot ? { nextSlot } : {},
619
- ...outstanding ? { outstanding } : {},
620
- isReordered: !isSameOrder(before.devices, after.devices) || !isSameOrder(before.revoked, after.revoked)
621
- };
622
- };
623
- //#endregion
624
- //#region ../../packages/lib/cardano-custody/src/analysis/account-inspection.ts
625
- const accountKey = (account) => `${account.accountValidatorHash}${account.stakeScriptHash}`;
626
- const hashesOf = (account) => ({
627
- accountValidatorHash: account.accountValidatorHash,
628
- stakeScriptHash: account.stakeScriptHash
629
- });
630
- const versionOfPolicy = (policyId) => require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS.find((version) => version.accountValidatorHash === policyId);
631
- /**
632
- * Every custody account the transaction names: by an input, reference input
633
- * or output at its address, by a mint of one of its account tokens, or by
634
- * the record.
635
- */
636
- const findCandidates = (facts, record) => {
637
- const candidates = /* @__PURE__ */ new Map();
638
- const add = (candidate) => candidates.set(accountKey(candidate), candidate);
639
- const views = [
640
- ...facts.inputs.map(({ output }) => output),
641
- ...facts.referenceInputs.map(({ output }) => output),
642
- ...facts.outputs
643
- ];
644
- for (const { addressClass } of views) if (addressClass.kind === "account") add({
645
- contractVersion: addressClass.contractVersion,
646
- accountValidatorHash: addressClass.contractVersion.accountValidatorHash,
647
- stakeScriptHash: addressClass.stakeScriptHash
648
- });
649
- for (const assetId of facts.mint.keys()) {
650
- const contractVersion = versionOfPolicy(require_outputs.policyIdOf(assetId));
651
- const name = require_tokens.parseCustodyAccountTokenName(require_outputs.assetNameOf(assetId));
652
- if (contractVersion && name) add({
653
- contractVersion,
654
- accountValidatorHash: contractVersion.accountValidatorHash,
655
- stakeScriptHash: name.stakeScriptHash
656
- });
657
- }
658
- if (record) add({
659
- contractVersion: record.contractVersion,
660
- accountValidatorHash: record.contractVersion.accountValidatorHash,
661
- stakeScriptHash: record.stakeScriptHash
662
- });
663
- return [...candidates.values()];
664
- };
665
- const VOTE_DELEGATIONS = new Set([
666
- _laceio_cardano_core.Cardano.CertificateType.StakeRegistrationDelegation,
667
- _laceio_cardano_core.Cardano.CertificateType.StakeVoteDelegation,
668
- _laceio_cardano_core.Cardano.CertificateType.StakeVoteRegistrationDelegation,
669
- _laceio_cardano_core.Cardano.CertificateType.VoteDelegation,
670
- _laceio_cardano_core.Cardano.CertificateType.VoteRegistrationDelegation
671
- ]);
672
- /**
673
- * The certificates on the account's stake credential and the withdrawal
674
- * from its reward account, apart from the logic run, which withdraws from
675
- * the logic's credential. Registration with a deposit and pool delegation
676
- * are what Lace builds; vote and combined delegations are valid on chain
677
- * but unsupported, and every other certificate is refused.
678
- */
679
- const readStakeEffects = (account, facts) => {
680
- const refusals = [];
681
- const certificates = [];
682
- let isStaked = false;
683
- for (const [certificateIndex, certificate] of (facts.body.certificates ?? []).entries()) {
684
- const credential = stakeCredentialOf(certificate);
685
- if (credential?.type !== _laceio_cardano_core.Cardano.CredentialType.ScriptHash || credential.hash !== account.stakeScriptHash) continue;
686
- isStaked = true;
687
- if (certificate.__typename === _laceio_cardano_core.Cardano.CertificateType.Registration) certificates.push({
688
- certificateIndex,
689
- kind: "registration",
690
- deposit: certificate.deposit
691
- });
692
- else if (certificate.__typename === _laceio_cardano_core.Cardano.CertificateType.StakeDelegation) certificates.push({
693
- certificateIndex,
694
- kind: "pool-delegation",
695
- poolId: certificate.poolId
696
- });
697
- else refusals.push({
698
- code: "custody-unsupported-operation",
699
- reason: VOTE_DELEGATIONS.has(certificate.__typename) ? "unsupported-certificate" : "refused-certificate",
700
- certificateIndex,
701
- certificateType: certificate.__typename
702
- });
703
- }
704
- const withdrawal = (facts.body.withdrawals ?? []).find(({ stakeAddress }) => scriptHashOfRewardAccount(stakeAddress) === account.stakeScriptHash);
705
- return {
706
- certificates,
707
- withdrawal: withdrawal?.quantity,
708
- refusals,
709
- isStaked: isStaked || withdrawal !== void 0
710
- };
711
- };
712
- /**
713
- * The run of the logic the account operates under: the logic its spent
714
- * control UTxO names, the referenced one for a grant spend, or the control
715
- * output's at creation. The proxy requires a withdrawal from that logic's
716
- * stake credential, which makes the ledger run it. The decoded body keeps
717
- * one withdrawal per reward account; a repeat in the bytes is refused.
718
- */
719
- const readLogicRun = (account, facts, logic) => {
720
- if (logic === void 0) return { refusals: [] };
721
- const withdrawal = (facts.body.withdrawals ?? []).find(({ stakeAddress }) => scriptHashOfRewardAccount(stakeAddress) === logic);
722
- return withdrawal ? {
723
- run: {
724
- logic,
725
- draw: withdrawal.quantity
726
- },
727
- refusals: []
728
- } : { refusals: [{
729
- code: "custody-unsupported-operation",
730
- reason: "logic-withdrawal-missing",
731
- account,
732
- logic
733
- }] };
734
- };
735
- /**
736
- * A device whose key hash is not 28 bytes can never sign, and the contract
737
- * does not check key lengths, so an on-chain state may keep one. Lace never
738
- * adds one, but a device can still remove it in a rewrite that keeps it
739
- * meanwhile.
740
- */
741
- const isCarriedOver = (violation, before) => before !== void 0 && violation.code === "invalid-device-key-hash" && before.devices.includes(violation.device);
742
- /** The authoring checks a state written by a device or a creation must pass. */
743
- const checkWrittenState = ({ account, before, after }) => require_outputs.findCustodyStateViolations(after).filter((violation) => !isCarriedOver(violation, before)).map((violation) => ({
744
- code: "custody-malformed-datum",
745
- reason: "state-violation",
746
- account,
747
- violation
748
- }));
749
- const isSameAccount = (account, record) => record !== void 0 && record.contractVersion.accountValidatorHash === account.accountValidatorHash && record.stakeScriptHash === account.stakeScriptHash;
750
- /**
751
- * Lace creates only the creator-only state: the current logic of the
752
- * account's contract version, the creator as the single device, generation
753
- * zero and no grant issued, revoked or outstanding. The logic also accepts
754
- * any generation at creation.
755
- */
756
- 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;
757
- /**
758
- * At creation the stake validator needs the creator's signature, and Lace
759
- * creates only the creator-only state; otherwise a device listed in the
760
- * spent state must sign.
761
- */
762
- const checkSigner = ({ account, before, after }, facts, scope) => {
763
- const { deviceKeyHash } = scope;
764
- const refusals = [];
765
- const notSigning = (keyHash) => facts.requiredSigners.has(keyHash) ? [] : [{
766
- code: "custody-device-not-listed",
767
- reason: "not-required-signer",
768
- account,
769
- keyHash
770
- }];
771
- if (!before) {
772
- if (!isSameAccount(account, scope.record)) return scope.record ? [] : [{
773
- code: "custody-context-mismatch",
774
- reason: "creation-without-record",
775
- account
776
- }];
777
- const creator = scope.record.creatorKeyHash;
778
- if (deviceKeyHash !== void 0 && deviceKeyHash !== creator) refusals.push({
779
- code: "custody-device-not-listed",
780
- reason: "not-creator",
781
- account,
782
- keyHash: deviceKeyHash
783
- });
784
- refusals.push(...notSigning(creator));
785
- if (!isCreatorOnly(after, creator, scope.record.contractVersion.currentLogicHash)) refusals.push({
786
- code: "custody-context-mismatch",
787
- reason: "initial-state",
788
- account
789
- });
790
- return refusals;
791
- }
792
- if (deviceKeyHash === void 0) return before.devices.some((device) => facts.requiredSigners.has(device)) ? [] : [{
793
- code: "custody-device-not-listed",
794
- reason: "no-device-signer",
795
- account
796
- }];
797
- if (!before.devices.includes(deviceKeyHash)) refusals.push({
798
- code: "custody-device-not-listed",
799
- reason: "not-listed",
800
- account,
801
- keyHash: deviceKeyHash
802
- });
803
- return [...refusals, ...notSigning(deviceKeyHash)];
804
- };
805
- /** The redeemers the contract lets spend each kind of account UTxO. */
806
- const REDEEMERS_OF = {
807
- control: ["device"],
808
- grant: ["spendWithGrant", "sweepGrant"],
809
- reserve: ["fund"],
810
- fund: ["fund"]
811
- };
812
- /**
813
- * The account inputs by kind, each with its spend redeemer, which must fit
814
- * the UTxO it spends. A plain deposit rides on any account token spent
815
- * beside it; a reserve needs the control UTxO, so only a device spends it.
816
- */
817
- const readAccountInputs = (account, facts) => {
818
- const refusals = [];
819
- const spent = facts.inputs.flatMap((view, redeemerIndex) => {
820
- if (!require_outputs.isAtAccount(view.output, account)) return [];
821
- const kind = require_outputs.accountUtxoKindOf(view.output, account);
822
- const data = redeemerData(facts, _laceio_cardano_core.Cardano.RedeemerPurpose.spend, redeemerIndex);
823
- const decoded = data === void 0 ? void 0 : decodeCustodyAccountRedeemer(data);
824
- if (!decoded?.ok) {
825
- refusals.push({
826
- code: "custody-unsupported-operation",
827
- reason: decoded ? "unknown-redeemer" : "missing-redeemer",
828
- purpose: "spend",
829
- index: redeemerIndex
830
- });
831
- return [{
832
- ...view,
833
- kind,
834
- redeemer: void 0
835
- }];
836
- }
837
- if (!REDEEMERS_OF[kind].includes(decoded.value.kind)) refusals.push({
838
- code: "custody-context-mismatch",
839
- reason: "redeemer-mismatch",
840
- input: view.input,
841
- redeemer: decoded.value.kind
842
- });
843
- return [{
844
- ...view,
845
- kind,
846
- redeemer: decoded.value
847
- }];
848
- });
849
- const ofKind = (kind) => spent.filter((input) => input.kind === kind);
850
- const controls = ofKind("control");
851
- const grants = ofKind("grant");
852
- const reserves = ofKind("reserve");
853
- const funds = ofKind("fund");
854
- if (controls.length === 0) refusals.push(...reserves.map(({ input }) => ({
855
- code: "custody-unsupported-operation",
856
- reason: "reserve-without-control",
857
- input
858
- })));
859
- if (funds.length > 0 && controls.length + grants.length === 0) refusals.push({
860
- code: "custody-unsupported-operation",
861
- reason: "fund-without-account-token",
862
- account
863
- });
864
- return {
865
- controls,
866
- spends: grants.filter(({ redeemer }) => redeemer?.kind === "spendWithGrant"),
867
- sweeps: grants.filter(({ redeemer }) => redeemer?.kind === "sweepGrant"),
868
- reserves,
869
- funds,
870
- isSpent: spent.length > 0,
871
- refusals
872
- };
873
- };
874
- const bySlot$1 = (a, b) => a.slot < b.slot ? -1 : a.slot > b.slot ? 1 : 0;
875
- const mintShapeOf = (entries) => {
876
- const names = entries.map(([assetId, quantity]) => ({
877
- name: require_tokens.parseCustodyAccountTokenName(require_outputs.assetNameOf(assetId)),
878
- quantity
879
- }));
880
- const [first] = names;
881
- if (names.length === 1 && first.name?.kind === "state-nft") return {
882
- kind: "creation",
883
- stakeScriptHash: first.name.stakeScriptHash
884
- };
885
- const stakeScriptHash = first?.name?.stakeScriptHash;
886
- if (!(stakeScriptHash !== void 0 && names.every(({ name, quantity }) => name?.kind === "grant" && name.stakeScriptHash === stakeScriptHash && (quantity === 1n || quantity === -1n)))) return { kind: "invalid" };
887
- const issues = names.filter(({ quantity }) => quantity === 1n).length;
888
- return {
889
- kind: issues === names.length ? "issue" : issues === 0 ? "burn" : "mixed",
890
- stakeScriptHash
891
- };
892
- };
893
- /**
894
- * The account's mint: its state NFT at creation, grant tokens issued at +1
895
- * or burned at -1, under the one mint redeemer of the policy. A device
896
- * spend of the control UTxO counts only the account's own grant tokens, so
897
- * any other name under the policy beside it is refused. A mint of no valid
898
- * shape is the transaction checks' to refuse.
899
- */
900
- const readMint = (account, facts, hasControlInput) => {
901
- const refusals = [];
902
- const policy = account.accountValidatorHash;
903
- const nftAssetId = require_outputs.stateNftAssetIdOf(account);
904
- const entries = [...facts.mint].filter(([assetId]) => require_outputs.policyIdOf(assetId) === policy);
905
- const shape = mintShapeOf(entries);
906
- const isOwnGrantMint = shape.kind !== "invalid" && shape.kind !== "creation" && shape.stakeScriptHash === account.stakeScriptHash;
907
- const issued = [];
908
- const burned = [];
909
- for (const [assetId, quantity] of entries) {
910
- const name = require_tokens.parseCustodyAccountTokenName(require_outputs.assetNameOf(assetId));
911
- if (isOwnGrantMint && name?.kind === "grant") (quantity === 1n ? issued : burned).push({
912
- slot: name.slot,
913
- assetId
914
- });
915
- else if (shape.kind !== "invalid" && shape.kind !== "mixed" && (hasControlInput || assetId === nftAssetId && quantity !== 1n)) refusals.push({
916
- code: "custody-unsupported-operation",
917
- reason: "unsupported-mint",
918
- assetId,
919
- quantity
920
- });
921
- }
922
- issued.sort(bySlot$1);
923
- burned.sort(bySlot$1);
924
- const isCreation = facts.mint.has(nftAssetId);
925
- if (isCreation || isOwnGrantMint) {
926
- const index = facts.mintPolicies.indexOf(policy);
927
- const data = redeemerData(facts, _laceio_cardano_core.Cardano.RedeemerPurpose.mint, index);
928
- const decoded = data === void 0 ? void 0 : decodeCustodyMintRedeemer(data);
929
- const expected = {
930
- creation: "createAccount",
931
- issue: "issueGrants",
932
- burn: "burnGrants"
933
- };
934
- if (!decoded?.ok) refusals.push({
935
- code: "custody-unsupported-operation",
936
- reason: decoded ? "unknown-redeemer" : "missing-redeemer",
937
- purpose: "mint",
938
- index
939
- });
940
- else if (expected[shape.kind] !== void 0 && decoded.value.kind !== expected[shape.kind]) refusals.push({
941
- code: "custody-unsupported-operation",
942
- reason: "mint-redeemer-mismatch",
943
- account,
944
- redeemer: decoded.value.kind
945
- });
946
- }
947
- if (isOwnGrantMint && !hasControlInput) refusals.push({
948
- code: "custody-unsupported-operation",
949
- reason: "grant-mint-without-control",
950
- account
951
- });
952
- for (const count of [issued.length, burned.length]) if (count > 8) refusals.push({
953
- code: "custody-unsupported-operation",
954
- reason: "grant-batch-limit",
955
- account,
956
- count,
957
- limit: 8
958
- });
959
- return {
960
- isCreation,
961
- issued,
962
- burned,
963
- refusals
964
- };
965
- };
966
- /** The control input's checks: one control UTxO whose resolution holds a state. */
967
- const readControlInput = (account, inputs) => {
968
- const refusals = [];
969
- const [control] = inputs.controls;
970
- if (inputs.controls.length > 1) refusals.push({
971
- code: "custody-context-mismatch",
972
- reason: "multiple-control-inputs",
973
- account
974
- });
975
- const before = control && require_outputs.decodedInlineState(control.output);
976
- if (control) {
977
- if (!before) refusals.push({
978
- code: "custody-malformed-datum",
979
- reason: "control-input-datum",
980
- input: control.input
981
- });
982
- if (!require_outputs.holdsOnlyLovelaceAndStateNft(control.output.value, account)) refusals.push({
983
- code: "custody-malformed-datum",
984
- reason: "control-input-value",
985
- input: control.input
986
- });
987
- }
988
- return {
989
- control,
990
- before,
991
- refusals
992
- };
993
- };
994
- /**
995
- * The account's control UTxO among the reference inputs. Only a grant spend
996
- * reads the control UTxO without spending it: Lace spends it for every
997
- * owner operation, delegation and withdrawal included.
998
- */
999
- const readReferencedControls = (account, facts, isGrantSpend) => {
1000
- const references = facts.referenceInputs.filter(({ output }) => require_outputs.isControlOutput(output, account));
1001
- return {
1002
- references,
1003
- refusals: isGrantSpend ? [] : references.map(({ input }) => ({
1004
- code: "custody-unsupported-operation",
1005
- reason: "reference-control",
1006
- input
1007
- }))
1008
- };
1009
- };
1010
- /**
1011
- * Registration belongs to creation and creation needs it; delegation and
1012
- * withdrawal need the control UTxO spent, since Lace never references it
1013
- * for them.
1014
- */
1015
- const checkStakeOperations = (account, { certificates, withdrawal }, { isCreation, hasControlInput }) => {
1016
- const refusals = [];
1017
- const isRegistered = certificates.some(({ kind }) => kind === "registration");
1018
- if (isRegistered && !isCreation) refusals.push({
1019
- code: "custody-unsupported-operation",
1020
- reason: "registration-without-creation",
1021
- account
1022
- });
1023
- if (isCreation && !isRegistered) refusals.push({
1024
- code: "custody-unsupported-operation",
1025
- reason: "creation-without-registration",
1026
- account
1027
- });
1028
- if ((withdrawal !== void 0 || certificates.some(({ kind }) => kind === "pool-delegation")) && !hasControlInput) refusals.push({
1029
- code: "custody-unsupported-operation",
1030
- reason: "stake-operation-without-control",
1031
- account
1032
- });
1033
- return refusals;
1034
- };
1035
- /**
1036
- * The control output's checks. With a control input or a creation there is
1037
- * exactly one, holding only lovelace and the NFT and an account state, with
1038
- * no reference script, which the proxy refuses there; without either there
1039
- * is none.
1040
- */
1041
- const readControlOutput = (account, facts, hasStateSource) => {
1042
- const refusals = [];
1043
- const controls = facts.outputs.flatMap((view, outputIndex) => require_outputs.isControlOutput(view, account) ? [{
1044
- view,
1045
- outputIndex
1046
- }] : []);
1047
- if (!hasStateSource && controls.length > 0) refusals.push({
1048
- code: "custody-context-mismatch",
1049
- reason: "control-output-without-control-input",
1050
- account
1051
- });
1052
- if (hasStateSource && controls.length !== 1) refusals.push({
1053
- code: "custody-malformed-datum",
1054
- reason: "control-output-count",
1055
- account,
1056
- count: controls.length
1057
- });
1058
- const control = controls.length === 1 ? controls[0] : void 0;
1059
- const after = control && require_outputs.decodedInlineState(control.view);
1060
- if (hasStateSource && control) {
1061
- if (!require_outputs.holdsOnlyLovelaceAndStateNft(control.view.value, account)) refusals.push({
1062
- code: "custody-malformed-datum",
1063
- reason: "control-output-value",
1064
- outputIndex: control.outputIndex
1065
- });
1066
- if (!after) refusals.push({
1067
- code: "custody-malformed-datum",
1068
- reason: "control-output-datum",
1069
- outputIndex: control.outputIndex
1070
- });
1071
- if (control.view.hasReferenceScript) refusals.push({
1072
- code: "custody-unsupported-operation",
1073
- reason: "control-output-reference-script",
1074
- outputIndex: control.outputIndex
1075
- });
1076
- }
1077
- return {
1078
- count: controls.length,
1079
- control,
1080
- after,
1081
- refusals
1082
- };
1083
- };
1084
- /** What one issued grant breaks of `issues_grant`, or its output and grant. */
1085
- const readIssuedGrant = (account, facts, { assetId }) => {
1086
- const holders = facts.outputs.flatMap((view, outputIndex) => view.value.assets.get(assetId) === 1n ? [{
1087
- view,
1088
- outputIndex
1089
- }] : []);
1090
- if (holders.length !== 1) return { problem: {
1091
- code: "output-count",
1092
- count: holders.length
1093
- } };
1094
- const [{ view, outputIndex }] = holders;
1095
- if (!require_outputs.isAtAccount(view, account)) return { problem: {
1096
- code: "address",
1097
- outputIndex
1098
- } };
1099
- if (view.value.assets.size !== 1) return { problem: {
1100
- code: "value",
1101
- outputIndex
1102
- } };
1103
- if (view.hasReferenceScript) return { problem: {
1104
- code: "reference-script",
1105
- outputIndex
1106
- } };
1107
- if (view.inlineDatum === void 0) return { problem: {
1108
- code: "datum-not-inline",
1109
- outputIndex
1110
- } };
1111
- const decoded = require_outputs.decodeCustodyGrant(view.inlineDatum);
1112
- return decoded.ok ? {
1113
- view,
1114
- outputIndex,
1115
- grant: decoded.value
1116
- } : { problem: {
1117
- code: "datum-malformed",
1118
- outputIndex
1119
- } };
1120
- };
1121
- /**
1122
- * The grants a device rewrite issues: `issues_grant` for each minted token,
1123
- * in slot order from the spent state's next slot, each under the generation
1124
- * of the control output, plus the rules Lace applies to every grant it
1125
- * authors and an expiry after `now`.
1126
- */
1127
- const readIssuedGrants = (account, facts, { issued, before, after, now }) => {
1128
- const refusals = [];
1129
- const reports = [];
1130
- const refuse = (slot, problem) => refusals.push({
1131
- code: "custody-malformed-datum",
1132
- reason: "grant-output-invalid",
1133
- account,
1134
- slot,
1135
- problem
1136
- });
1137
- for (const [index, mint] of issued.entries()) {
1138
- const { slot } = mint;
1139
- if (before) {
1140
- const expected = before.nextSlot + BigInt(index);
1141
- if (slot !== expected) refuse(slot, {
1142
- code: "not-next-slot",
1143
- expected
1144
- });
1145
- }
1146
- const read = readIssuedGrant(account, facts, mint);
1147
- if ("problem" in read) {
1148
- refuse(slot, read.problem);
1149
- continue;
1150
- }
1151
- const { view, outputIndex, grant } = read;
1152
- if (grant.slot !== slot) refuse(slot, {
1153
- code: "slot-mismatch",
1154
- outputIndex,
1155
- datumSlot: grant.slot
1156
- });
1157
- if (after && grant.generation !== after.grantGeneration) refuse(slot, {
1158
- code: "generation-mismatch",
1159
- outputIndex,
1160
- generation: grant.generation,
1161
- expected: after.grantGeneration
1162
- });
1163
- refusals.push(...require_outputs.findCustodyGrantViolations(grant).map((violation) => ({
1164
- code: "custody-malformed-datum",
1165
- reason: "grant-violation",
1166
- account,
1167
- slot,
1168
- violation
1169
- })));
1170
- if (require_outputs.isCustodyGrantExpired(grant, now)) refusals.push({
1171
- code: "custody-malformed-datum",
1172
- reason: "expired-grant",
1173
- account,
1174
- slot,
1175
- expiresAt: grant.scope.expiresAt
1176
- });
1177
- reports.push({
1178
- outputIndex,
1179
- assetId: mint.assetId,
1180
- grant,
1181
- lovelace: view.value.lovelace
1182
- });
1183
- }
1184
- return {
1185
- reports,
1186
- refusals
1187
- };
1188
- };
1189
- /**
1190
- * `is_dead` against the spent state: issued under an older generation, its
1191
- * slot revoked, or the validity interval starting strictly after its expiry.
1192
- */
1193
- const deathOf = (grant, state, lowerBoundMs) => {
1194
- if (grant.generation < state.grantGeneration) return "superseded";
1195
- if (state.revoked.includes(grant.slot)) return "revoked";
1196
- return lowerBoundMs !== void 0 && Number.isSafeInteger(lowerBoundMs) && BigInt(lowerBoundMs) > grant.scope.expiresAt ? "expired" : void 0;
1197
- };
1198
- /**
1199
- * The grants a device rewrite sweeps: each grant UTxO spent with
1200
- * `SweepGrant` carries its grant inline beside only its token, is dead
1201
- * against the spent state, and has its token burned; every burned token is
1202
- * a swept grant's.
1203
- */
1204
- const readSweptGrants = (account, facts, { sweeps, burned, before, hasControlInput }) => {
1205
- const refusals = [];
1206
- const reports = [];
1207
- if (sweeps.length > 0 && !hasControlInput) refusals.push({
1208
- code: "custody-unsupported-operation",
1209
- reason: "sweep-without-control",
1210
- account
1211
- });
1212
- for (const { input, output } of sweeps) {
1213
- const grant = require_outputs.decodedInlineGrant(output);
1214
- if (!grant) {
1215
- refusals.push({
1216
- code: "custody-malformed-datum",
1217
- reason: "grant-input-datum",
1218
- input
1219
- });
1220
- continue;
1221
- }
1222
- const assetId = require_outputs.grantAssetIdOf(account, grant.slot);
1223
- if (!assetId || !require_outputs.holdsOnlyLovelaceAndGrantToken(output.value, account, grant.slot)) {
1224
- refusals.push({
1225
- code: "custody-malformed-datum",
1226
- reason: "grant-input-value",
1227
- input
1228
- });
1229
- continue;
1230
- }
1231
- if (facts.mint.get(assetId) !== -1n) refusals.push({
1232
- code: "custody-unsupported-operation",
1233
- reason: "sweep-burn-mismatch",
1234
- account,
1235
- slot: grant.slot
1236
- });
1237
- if (!before) continue;
1238
- const death = deathOf(grant, before, facts.lowerBoundMs);
1239
- if (!death) {
1240
- refusals.push({
1241
- code: "custody-unsupported-operation",
1242
- reason: "sweep-not-dead",
1243
- account,
1244
- input,
1245
- slot: grant.slot
1246
- });
1247
- continue;
1248
- }
1249
- reports.push({
1250
- input,
1251
- assetId,
1252
- grant,
1253
- lovelace: output.value.lovelace,
1254
- death
1255
- });
1256
- }
1257
- const sweptSlots = sweeps.flatMap(({ output }) => {
1258
- const grant = require_outputs.decodedInlineGrant(output);
1259
- return grant ? [grant.slot] : [];
1260
- });
1261
- for (const { slot } of burned) if (!sweptSlots.includes(slot)) refusals.push({
1262
- code: "custody-unsupported-operation",
1263
- reason: "sweep-burn-mismatch",
1264
- account,
1265
- slot
1266
- });
1267
- return {
1268
- reports,
1269
- refusals
1270
- };
1271
- };
1272
- /**
1273
- * Every control state the transaction spends, references or writes for the
1274
- * account names a logic of its contract version's catalogue: the logic is
1275
- * the account's rule set, and any registered script may be named there, so
1276
- * an unknown one runs rules Lace has not reviewed. Each unknown logic is
1277
- * refused once.
1278
- */
1279
- const checkKnownLogics = (account, version, states) => {
1280
- const unknown = /* @__PURE__ */ new Set();
1281
- for (const state of states) if (state && !require_hex.findCustodyLogic(version, state.logic).ok) unknown.add(state.logic);
1282
- return [...unknown].map((logic) => ({
1283
- code: "custody-unsupported-version",
1284
- reason: "unknown-logic",
1285
- account,
1286
- logic
1287
- }));
1288
- };
1289
- /**
1290
- * The counter rules of `recreates_control_output`, and the rules Lace adds:
1291
- * the logic stays, since Lace does not sign an upgrade; a slot leaves the
1292
- * revoked list only with its grant token burned in the same rewrite, or
1293
- * with a generation step that supersedes every grant; a slot joins it only
1294
- * once, and only after it was issued.
1295
- */
1296
- const checkStateChange = (account, change, { issued, burned }) => {
1297
- const { before, after } = change;
1298
- const refusals = [];
1299
- if (change.logic) refusals.push({
1300
- code: "custody-unsupported-operation",
1301
- reason: "logic-changed",
1302
- account,
1303
- before: change.logic.before,
1304
- after: change.logic.after
1305
- });
1306
- if (after.grantGeneration < before.grantGeneration) refusals.push({
1307
- code: "custody-malformed-datum",
1308
- reason: "generation-decreased",
1309
- account,
1310
- before: before.grantGeneration,
1311
- after: after.grantGeneration
1312
- });
1313
- const minted = BigInt(issued.length);
1314
- const counters = [[
1315
- "nextSlot",
1316
- before.nextSlot + minted,
1317
- after.nextSlot
1318
- ], [
1319
- "outstanding",
1320
- before.outstanding + minted - BigInt(burned.length),
1321
- after.outstanding
1322
- ]];
1323
- for (const [counter, expected, actual] of counters) if (actual !== expected) refusals.push({
1324
- code: "custody-malformed-datum",
1325
- reason: "counter-mismatch",
1326
- account,
1327
- counter,
1328
- expected,
1329
- actual
1330
- });
1331
- if (after.grantGeneration === before.grantGeneration) {
1332
- for (const slot of change.revokedRemoved) if (!burned.some((mint) => mint.slot === slot)) refusals.push({
1333
- code: "custody-unsupported-operation",
1334
- reason: "revoked-slot-restored",
1335
- account,
1336
- slot
1337
- });
1338
- }
1339
- const invalid = change.revokedAdded.filter((slot, index) => slot < 0n || slot >= before.nextSlot || change.revokedAdded.indexOf(slot) !== index);
1340
- for (const slot of new Set(after.revoked)) {
1341
- const previousCount = before.revoked.filter((value) => value === slot).length;
1342
- if (after.revoked.filter((value) => value === slot).length > Math.max(1, previousCount)) invalid.push(slot);
1343
- }
1344
- for (const slot of new Set(invalid)) refusals.push({
1345
- code: "custody-unsupported-operation",
1346
- reason: "revoked-slot-invalid",
1347
- account,
1348
- slot
1349
- });
1350
- return refusals;
1351
- };
1352
- /**
1353
- * Grant spends are refused unless the caller allows them. An allowed one is
1354
- * refused for every rule of the logic's grant spend rule it breaks, so an
1355
- * accepted grant spend is one the contract accepts, and for any input
1356
- * outside the account address: an agent spends from the account alone.
1357
- */
1358
- const checkGrantSpends = ({ account, spends }, facts, scope) => [...spends.flatMap(({ input, output }) => scope.options.allowGrantSpend ? grantSpendViolationsOf({
1359
- grantInput: output,
1360
- inputs: facts.inputs.map((view) => view.output),
1361
- referenceInputs: facts.referenceInputs.map((view) => view.output),
1362
- outputs: facts.outputs,
1363
- mint: facts.mint,
1364
- requiredSigners: facts.requiredSigners,
1365
- upperBoundMs: facts.upperBoundMs,
1366
- account
1367
- }).map((violation) => ({
1368
- code: "custody-unsupported-operation",
1369
- reason: "grant-spend-violation",
1370
- account,
1371
- input,
1372
- violation
1373
- })) : [{
1374
- code: "custody-unsupported-operation",
1375
- reason: "grant-spend",
1376
- account,
1377
- input
1378
- }]), ...scope.options.allowGrantSpend && spends.length > 0 ? facts.inputs.filter(({ output }) => !require_outputs.isAtAccount(output, account)).map(({ input }) => ({
1379
- code: "custody-unsupported-operation",
1380
- reason: "grant-spend-foreign-input",
1381
- account,
1382
- input
1383
- })) : []];
1384
- /** The grant a single grant spend uses and the output that recreates it. */
1385
- const grantSpendReportOf = (account, spends, facts) => {
1386
- if (spends.length !== 1) return void 0;
1387
- const [{ input, output }] = spends;
1388
- const grant = require_outputs.decodedInlineGrant(output);
1389
- const assetId = grant && require_outputs.grantAssetIdOf(account, grant.slot);
1390
- const outputIndex = facts.outputs.findIndex((view) => assetId !== void 0 && view.value.assets.get(assetId) === 1n);
1391
- const after = outputIndex === -1 ? void 0 : require_outputs.decodedInlineGrant(facts.outputs[outputIndex]);
1392
- return grant && after ? {
1393
- input,
1394
- grant,
1395
- outputIndex,
1396
- after
1397
- } : void 0;
1398
- };
1399
- /**
1400
- * The operations a device rewrite performs, from its state change and the
1401
- * grants it issues and sweeps. One revoked slot beside one issued grant is a
1402
- * replacement; a generation step of one that empties the revoked list
1403
- * under the same logic revokes every grant, where an upgrade has the same
1404
- * counters.
1405
- */
1406
- const stateOperationsOf = (change, issuedSlots, sweptSlots) => {
1407
- const { before, after, revokedAdded } = change;
1408
- const isRevokeAll = !change.logic && after.grantGeneration === before.grantGeneration + 1n && after.revoked.length === 0;
1409
- const isReplace = !change.grantGeneration && revokedAdded.length === 1 && issuedSlots.length === 1;
1410
- return [
1411
- ...isReplace ? [{
1412
- kind: "replace-grant",
1413
- slot: revokedAdded[0],
1414
- issuedSlot: issuedSlots[0]
1415
- }] : issuedSlots.map((slot) => ({
1416
- kind: "issue-grant",
1417
- slot
1418
- })),
1419
- ...isRevokeAll ? [{ kind: "revoke-all-grants" }] : [...isReplace ? [] : revokedAdded.map((slot) => ({
1420
- kind: "revoke-grant",
1421
- slot
1422
- })), ...change.grantGeneration ? [{ kind: "change-grant-generation" }] : []],
1423
- ...sweptSlots.length > 0 ? [{
1424
- kind: "sweep-grants",
1425
- slots: sweptSlots
1426
- }] : [],
1427
- ...change.devicesAdded.map((device) => ({
1428
- kind: "add-device",
1429
- device
1430
- })),
1431
- ...change.devicesRemoved.map((device) => ({
1432
- kind: "remove-device",
1433
- device
1434
- })),
1435
- ...change.isReordered ? [{ kind: "reorder-state" }] : []
1436
- ];
1437
- };
1438
- /**
1439
- * Lovelace leaving the account's address, its reward account and its logic
1440
- * run's draw; negative is a net inflow. Lovelace is fungible, so what the
1441
- * logic run draws is credited to the account: any of it that leaves counts
1442
- * as the account's.
1443
- */
1444
- const lovelaceGivenUp = ({ netOutflow, stake, logicRun }) => netOutflow.lovelace + (stake.withdrawal ?? 0n) + (logicRun?.draw ?? 0n);
1445
- /**
1446
- * Whether an owner operation pays value out of the account: any asset that
1447
- * leaves it, or lovelace given up beyond its share of the fee. An account
1448
- * that pays only its own fee spends nothing. Account tokens are minted and
1449
- * burned, never paid out: placement keeps them at their accounts.
1450
- */
1451
- const isOwnerSpend = (facts, feeShare) => [...facts.netOutflow.assets].some(([assetId, quantity]) => quantity > 0n && require_outputs.policyIdOf(assetId) !== facts.account.accountValidatorHash) || lovelaceGivenUp(facts) > feeShare;
1452
- const operationsOf = (facts, feeShare) => {
1453
- const { isCreation, grantSpend, stateChange, stake } = facts;
1454
- return [
1455
- ...isCreation ? [{ kind: "create" }] : [],
1456
- ...grantSpend ? [{
1457
- kind: "grant-spend",
1458
- slot: grantSpend.grant.slot
1459
- }] : [],
1460
- ...stateChange ? stateOperationsOf(stateChange, facts.issued.map(({ grant }) => grant.slot), facts.swept.map(({ grant }) => grant.slot)) : [],
1461
- ...!facts.isGrantSpend && isOwnerSpend(facts, feeShare) ? [{ kind: "owner-spend" }] : [],
1462
- ...stake.certificates.flatMap((certificate) => certificate.kind === "pool-delegation" ? [{
1463
- kind: "delegate",
1464
- poolId: certificate.poolId
1465
- }] : []),
1466
- ...stake.withdrawal === void 0 ? [] : [{
1467
- kind: "withdraw-rewards",
1468
- quantity: stake.withdrawal
1469
- }]
1470
- ];
1471
- };
1472
- /** Each operated account's part of the accounts' fee share, in order. */
1473
- const feeSharesOfAccounts = (operated, { accountShare }) => accountFeeSharesOf(operated.map(({ operationFacts }) => lovelaceGivenUp(operationFacts)), accountShare);
1474
- /** The reports of the operated accounts, each with its operations named. */
1475
- const withOperations = (operated, feeShares) => {
1476
- const shares = feeSharesOfAccounts(operated, feeShares);
1477
- return operated.map(({ report, operationFacts }, index) => ({
1478
- ...report,
1479
- operations: operationsOf(operationFacts, shares[index])
1480
- }));
1481
- };
1482
- /**
1483
- * A logic run that draws lovelace must leave it at the account. Lovelace
1484
- * is fungible, so the draw is credited to the account, and it stays there
1485
- * when the account gives up no more lovelace than its part of the fee. A
1486
- * fee payer that takes change back could have paid the whole fee, so the
1487
- * accounts' part is then nothing: otherwise the payer's change could hold
1488
- * the draw. A grant spend pays out of the account by definition, so it may
1489
- * draw nothing.
1490
- */
1491
- const checkLogicDraws = (operated, feeShares, isChangeToPayers) => {
1492
- const shares = isChangeToPayers ? operated.map(() => 0n) : feeSharesOfAccounts(operated, feeShares);
1493
- return operated.flatMap(({ operationFacts }, index) => {
1494
- const { account, logicRun, isGrantSpend } = operationFacts;
1495
- if (!logicRun || logicRun.draw === 0n) return [];
1496
- return isGrantSpend || lovelaceGivenUp(operationFacts) > shares[index] ? [{
1497
- code: "custody-unsupported-operation",
1498
- reason: "logic-draw-leaves-account",
1499
- account,
1500
- logic: logicRun.logic,
1501
- quantity: logicRun.draw
1502
- }] : [];
1503
- });
1504
- };
1505
- const isOwnReserve = (view) => view.inlineDatum !== void 0 && !view.hasReferenceScript && isCustodyReserveDatum(view.inlineDatum);
1506
- const inspectAccount = (candidate, facts, scope) => {
1507
- const account = hashesOf(candidate);
1508
- const { networkId } = scope.options.chainId;
1509
- const inputs = readAccountInputs(account, facts);
1510
- const { control, before, ...controlInput } = readControlInput(account, inputs);
1511
- const hasControlInput = control !== void 0;
1512
- const isGrantSpend = inputs.spends.length > 0;
1513
- const mint = readMint(account, facts, hasControlInput);
1514
- const stake = readStakeEffects(candidate, facts);
1515
- const controlOutput = readControlOutput(account, facts, hasControlInput || mint.isCreation);
1516
- const { after } = controlOutput;
1517
- const referenced = readReferencedControls(account, facts, isGrantSpend);
1518
- const referencedStates = referenced.references.map(({ output }) => require_outputs.decodedInlineState(output));
1519
- const states = [
1520
- before,
1521
- after,
1522
- ...referencedStates
1523
- ];
1524
- const logicRun = readLogicRun(account, facts, before?.logic ?? (isGrantSpend ? referencedStates[0]?.logic : void 0) ?? (mint.isCreation ? after?.logic : void 0));
1525
- const issued = readIssuedGrants(account, facts, {
1526
- issued: mint.issued,
1527
- before,
1528
- after,
1529
- now: scope.options.now
1530
- });
1531
- const swept = readSweptGrants(account, facts, {
1532
- sweeps: inputs.sweeps,
1533
- burned: mint.burned,
1534
- before,
1535
- hasControlInput
1536
- });
1537
- const refusals = [
1538
- ...inputs.refusals,
1539
- ...controlInput.refusals,
1540
- ...mint.refusals,
1541
- ...referenced.refusals,
1542
- ...checkKnownLogics(account, candidate.contractVersion, states),
1543
- ...logicRun.refusals,
1544
- ...stake.refusals,
1545
- ...checkStakeOperations(account, stake, {
1546
- isCreation: mint.isCreation,
1547
- hasControlInput
1548
- }),
1549
- ...controlOutput.refusals,
1550
- ...issued.refusals,
1551
- ...swept.refusals,
1552
- ...checkGrantSpends({
1553
- account,
1554
- spends: inputs.spends
1555
- }, facts, scope)
1556
- ];
1557
- const netOutflow = leavingValueAt(facts.inputs.map(({ output }) => output), facts.outputs, require_outputs.accountPlutusAddressKey(account));
1558
- let stateChange;
1559
- if (after && (before || mint.isCreation)) {
1560
- const written = {
1561
- account,
1562
- before,
1563
- after
1564
- };
1565
- refusals.push(...checkWrittenState(written), ...checkSigner(written, facts, scope));
1566
- if (before) {
1567
- stateChange = diffCustodyStates(before, after);
1568
- refusals.push(...checkStateChange(account, stateChange, mint));
1569
- }
1570
- }
1571
- const [reference] = referenced.references;
1572
- const [referencedState] = referencedStates;
1573
- const grantSpend = grantSpendReportOf(account, inputs.spends, facts);
1574
- return {
1575
- isOperated: inputs.isSpent || mint.isCreation || mint.issued.length + mint.burned.length > 0 || stake.isStaked || controlOutput.count > 0,
1576
- refusals,
1577
- logics: states.flatMap((state) => state ? [state.logic] : []),
1578
- report: {
1579
- contractVersion: candidate.contractVersion,
1580
- ...account,
1581
- address: require_outputs.accountAddressOf(account, networkId),
1582
- rewardAccount: require_outputs.accountRewardAccountOf(account, networkId),
1583
- ...control && before ? { controlInput: {
1584
- input: control.input,
1585
- state: before,
1586
- lovelace: control.output.value.lovelace
1587
- } } : {},
1588
- ...controlOutput.control && after ? { controlOutput: {
1589
- outputIndex: controlOutput.control.outputIndex,
1590
- state: after,
1591
- lovelace: controlOutput.control.view.value.lovelace
1592
- } } : {},
1593
- ...isGrantSpend && reference && referencedState ? { referencedControl: {
1594
- input: reference.input,
1595
- state: referencedState,
1596
- lovelace: reference.output.value.lovelace
1597
- } } : {},
1598
- fundInputs: inputs.funds.map(({ input, output }) => ({
1599
- input,
1600
- value: output.txOut.value,
1601
- hasReferenceScript: output.hasReferenceScript
1602
- })),
1603
- reserveInputs: inputs.reserves.map(({ input, output }) => ({
1604
- input,
1605
- value: output.txOut.value,
1606
- datum: output.datumKind === "hash" ? "hash" : "inline",
1607
- isOwnFormat: isOwnReserve(output),
1608
- hasReferenceScript: output.hasReferenceScript
1609
- })),
1610
- accountOutputs: facts.outputs.flatMap((view, outputIndex) => require_outputs.isAtAccount(view, account) ? [{
1611
- outputIndex,
1612
- value: view.txOut.value,
1613
- datum: view.datumKind,
1614
- kind: require_outputs.accountUtxoKindOf(view, account)
1615
- }] : []),
1616
- issuedGrants: issued.reports,
1617
- sweptGrants: swept.reports,
1618
- ...grantSpend ? { grantSpend } : {},
1619
- netOutflow,
1620
- certificates: stake.certificates,
1621
- ...stake.withdrawal === void 0 ? {} : { withdrawal: stake.withdrawal },
1622
- ...logicRun.run && logicRun.run.draw > 0n ? { logicDraw: logicRun.run.draw } : {},
1623
- ...stateChange ? { stateChange } : {}
1624
- },
1625
- operationFacts: {
1626
- account,
1627
- isCreation: mint.isCreation,
1628
- isGrantSpend,
1629
- grantSpend,
1630
- stateChange,
1631
- issued: issued.reports,
1632
- swept: swept.reports,
1633
- netOutflow,
1634
- stake,
1635
- logicRun: logicRun.run
1636
- }
1637
- };
1638
- };
1639
- //#endregion
1640
- //#region ../../packages/lib/cardano-custody/src/analysis/auxiliary-data.ts
1641
- /**
1642
- * The metadata a transaction carries, read from its auxiliary data and
1643
- * bound to the signed body by the auxiliary data hash. Undefined without
1644
- * any. A body that commits to auxiliary data the transaction does not
1645
- * carry, or auxiliary data the body does not commit to by that hash, is
1646
- * refused: a signature over the body would then authorise metadata consent
1647
- * never saw. So is auxiliary data holding anything but metadata.
1648
- */
1649
- const readCustodyMetadata = (transaction, body) => {
1650
- let auxiliaryData;
1651
- let carried;
1652
- try {
1653
- const serialized = transaction.auxiliaryData();
1654
- auxiliaryData = serialized?.toCore();
1655
- carried = serialized ? _laceio_cardano_crypto.blake2b.hash(serialized.toCbor(), 32) : void 0;
1656
- } catch {
1657
- return require_hex.fail({
1658
- code: "custody-unsupported-operation",
1659
- reason: "auxiliary-data-unreadable"
1660
- });
1661
- }
1662
- if (body.auxiliaryDataHash?.toLowerCase() !== carried) return require_hex.fail({
1663
- code: "custody-unsupported-operation",
1664
- reason: "auxiliary-data-hash-mismatch"
1665
- });
1666
- if ((auxiliaryData?.scripts?.length ?? 0) > 0) return require_hex.fail({
1667
- code: "custody-unsupported-operation",
1668
- reason: "auxiliary-data-scripts"
1669
- });
1670
- const metadata = auxiliaryData?.blob;
1671
- return require_hex.ok(metadata && metadata.size > 0 ? metadata : void 0);
1672
- };
1673
- /**
1674
- * A grant spend carrying metadata. An agent spends only what its grant
1675
- * allows, and attaching data to the account's transactions is not among it.
1676
- */
1677
- const checkGrantSpendMetadata = (operated, metadata) => metadata === void 0 ? [] : operated.flatMap(({ report }) => report.grantSpend ? [{
1678
- code: "custody-unsupported-operation",
1679
- reason: "grant-spend-metadata",
1680
- account: hashesOf(report),
1681
- input: report.grantSpend.input
1682
- }] : []);
1683
- //#endregion
1684
- //#region ../../packages/lib/cardano-custody/src/analysis/refusals.ts
1685
- /**
1686
- * The order checks run in: the record, the caller's context, the account
1687
- * UTxOs it resolves, a grant spend, unsupported shapes, the written datums,
1688
- * the signing device, the initial state at creation and locked outputs.
1689
- */
1690
- const STAGE = {
1691
- "unsupported-contract-version": 0,
1692
- "invalid-record": 0,
1693
- "unresolved-input": 1,
1694
- "resolution-not-in-body": 1,
1695
- "duplicate-resolution": 1,
1696
- "invalid-resolution": 1,
1697
- "network-mismatch": 1,
1698
- "redeemer-mismatch": 1,
1699
- "redeemer-on-key-input": 1,
1700
- "reference-script-mismatch": 1,
1701
- "unused-account-validator": 1,
1702
- "unused-logic": 1,
1703
- "multiple-control-inputs": 1,
1704
- "foreign-account": 1,
1705
- "account-not-operated": 1,
1706
- "creation-without-record": 1,
1707
- "control-output-without-control-input": 1,
1708
- "control-input-datum": 2,
1709
- "unknown-logic": 2,
1710
- "control-input-value": 2,
1711
- "grant-input-datum": 2,
1712
- "grant-input-value": 2,
1713
- "grant-spend": 3,
1714
- "grant-spend-violation": 3,
1715
- "grant-spend-foreign-input": 3,
1716
- "grant-spend-metadata": 3,
1717
- "unsupported-body-field": 4,
1718
- "auxiliary-data-hash-mismatch": 4,
1719
- "auxiliary-data-scripts": 4,
1720
- "auxiliary-data-unreadable": 4,
1721
- "missing-redeemer": 4,
1722
- "unknown-redeemer": 4,
1723
- "duplicate-redeemer": 4,
1724
- "mint-redeemer-mismatch": 4,
1725
- "foreign-certificate": 4,
1726
- "refused-certificate": 4,
1727
- "unsupported-certificate": 4,
1728
- "foreign-withdrawal": 4,
1729
- "duplicate-withdrawal": 4,
1730
- "logic-draw-leaves-account": 4,
1731
- "logic-withdrawal-missing": 4,
1732
- "foreign-mint": 4,
1733
- "unsupported-mint": 4,
1734
- "issue-sweep-mixed": 4,
1735
- "grant-batch-limit": 4,
1736
- "grant-mint-without-control": 4,
1737
- "reference-control": 4,
1738
- "foreign-reference-script": 4,
1739
- "creation-without-registration": 4,
1740
- "fund-without-account-token": 4,
1741
- "reserve-without-control": 4,
1742
- "sweep-without-control": 4,
1743
- "sweep-burn-mismatch": 4,
1744
- "registration-without-creation": 4,
1745
- "stake-operation-without-control": 4,
1746
- "account-token-misplaced": 4,
1747
- "foreign-reserve": 4,
1748
- "deposit-reference-script": 4,
1749
- "control-output-count": 5,
1750
- "control-output-datum": 5,
1751
- "control-output-value": 5,
1752
- "control-output-reference-script": 5,
1753
- "state-violation": 5,
1754
- "counter-mismatch": 5,
1755
- "generation-decreased": 5,
1756
- "logic-changed": 5,
1757
- "revoked-slot-restored": 5,
1758
- "revoked-slot-invalid": 5,
1759
- "grant-output-invalid": 5,
1760
- "grant-violation": 5,
1761
- "expired-grant": 5,
1762
- "sweep-not-dead": 5,
1763
- "no-device-signer": 6,
1764
- "not-creator": 6,
1765
- "not-listed": 6,
1766
- "not-required-signer": 6,
1767
- "initial-state": 7,
1768
- "datum-hash": 8,
1769
- "unspendable-address": 8
1770
- };
1771
- /** Refusals in check order; refusals of one stage keep the order found. */
1772
- const inCheckOrder = (refusals) => refusals.map((refusal, position) => ({
1773
- refusal,
1774
- position
1775
- })).sort((a, b) => STAGE[a.refusal.reason] - STAGE[b.refusal.reason] || a.position - b.position).map(({ refusal }) => refusal);
1776
- //#endregion
1777
- //#region ../../packages/lib/cardano-custody/src/analysis/transaction-checks.ts
1778
- const UNSUPPORTED_BODY_FIELDS = [
1779
- "accountBalanceIntervals",
1780
- "directDeposits",
1781
- "guards",
1782
- "proposalProcedures",
1783
- "subTransactions",
1784
- "update",
1785
- "votingProcedures"
1786
- ];
1787
- /**
1788
- * A redeemer pointer named twice has no single reading, so the redeemer a
1789
- * check reads might not be the one a script runs with.
1790
- */
1791
- const checkRedeemerPointers = (facts) => {
1792
- const seen = /* @__PURE__ */ new Set();
1793
- const refusals = [];
1794
- for (const { purpose, index } of facts.redeemers) {
1795
- const pointer = `${purpose}:${index}`;
1796
- if (seen.has(pointer)) refusals.push({
1797
- code: "custody-unsupported-operation",
1798
- reason: "duplicate-redeemer",
1799
- purpose,
1800
- index
1801
- });
1802
- seen.add(pointer);
1803
- }
1804
- return refusals;
1805
- };
1806
- /**
1807
- * Consistency checks that catch resolutions hiding a custody spend. The
1808
- * ledger refuses a spend redeemer for an input no script locks, and an
1809
- * account validator witness that no input or mint needs, so either means a
1810
- * resolution misstates what the transaction spends.
1811
- */
1812
- const checkSpendWitnesses = (facts) => {
1813
- const refusals = [];
1814
- for (const [index, { input, output }] of facts.inputs.entries()) if (redeemerData(facts, _laceio_cardano_core.Cardano.RedeemerPurpose.spend, index) !== void 0 && paymentCredentialOf(output)?.type !== _laceio_cardano_core.Cardano.CredentialType.ScriptHash) refusals.push({
1815
- code: "custody-context-mismatch",
1816
- reason: "redeemer-on-key-input",
1817
- input
1818
- });
1819
- for (const version of require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS) {
1820
- const { accountValidatorHash } = version;
1821
- const isNeeded = facts.inputs.some(({ output: { addressClass } }) => addressClass.kind === "account" && addressClass.contractVersion === version) || facts.mintPolicies.includes(accountValidatorHash);
1822
- if (facts.witnessScriptHashes.has(accountValidatorHash) && !isNeeded) refusals.push({
1823
- code: "custody-context-mismatch",
1824
- reason: "unused-account-validator",
1825
- accountValidatorHash
1826
- });
1827
- }
1828
- return refusals;
1829
- };
1830
- /**
1831
- * Consistency checks on the known logics a transaction attaches or runs: a
1832
- * logic runs through a withdrawal from its stake credential, and only over
1833
- * a control UTxO that names it, spent, referenced or written. The ledger
1834
- * refuses a witness nothing needs, and a run with no such control UTxO
1835
- * fails, so a logic attached without a run, or run with no control UTxO
1836
- * naming it among `logics`, means a resolution hides that control UTxO or
1837
- * the transaction cannot succeed.
1838
- */
1839
- const checkLogicUse = (facts, logics) => {
1840
- const named = new Set(logics);
1841
- const withdrawn = new Set((facts.body.withdrawals ?? []).flatMap(({ stakeAddress }) => {
1842
- const scriptHash = scriptHashOfRewardAccount(stakeAddress);
1843
- return scriptHash === void 0 ? [] : [scriptHash];
1844
- }));
1845
- return [...new Set([...withdrawn, ...facts.witnessScriptHashes].filter(require_outputs.isKnownCustodyLogic))].filter((logic) => !withdrawn.has(logic) || !named.has(logic)).map((logic) => ({
1846
- code: "custody-context-mismatch",
1847
- reason: "unused-logic",
1848
- logic
1849
- }));
1850
- };
1851
- /**
1852
- * A reward account the body's withdrawal map names more than once. The
1853
- * decoded body keeps one of its withdrawals, so what inspection reads might
1854
- * not be what the bytes say.
1855
- */
1856
- const checkWithdrawalAccounts = (facts) => {
1857
- const seen = /* @__PURE__ */ new Set();
1858
- const repeated = /* @__PURE__ */ new Set();
1859
- for (const rewardAccount of facts.withdrawalAccounts) {
1860
- if (seen.has(rewardAccount)) repeated.add(rewardAccount);
1861
- seen.add(rewardAccount);
1862
- }
1863
- return [...repeated].map((rewardAccount) => ({
1864
- code: "custody-unsupported-operation",
1865
- reason: "duplicate-withdrawal",
1866
- rewardAccount
1867
- }));
1868
- };
1869
- const isSameOutpoint = (a, b) => a.txId.toLowerCase() === b.txId.toLowerCase() && a.index === b.index;
1870
- /**
1871
- * Reference inputs that carry a reference script must be the contract's
1872
- * parked scripts on the inspected network: the output reference a supported
1873
- * version pins, resolved with exactly the script it pins there. Any other
1874
- * reference script is code Lace has not pinned.
1875
- */
1876
- const checkReferenceScripts = (facts, networkMagic) => {
1877
- const parked = require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS.flatMap((version) => version.referenceScripts[networkMagic] ?? []);
1878
- return facts.referenceInputs.flatMap(({ input, output }) => {
1879
- const pinned = parked.find(({ txIn }) => isSameOutpoint(txIn, input));
1880
- if (pinned) return output.referenceScriptHash === pinned.scriptHash ? [] : [{
1881
- code: "custody-context-mismatch",
1882
- reason: "reference-script-mismatch",
1883
- input
1884
- }];
1885
- return output.hasReferenceScript ? [{
1886
- code: "custody-unsupported-operation",
1887
- reason: "foreign-reference-script",
1888
- input
1889
- }] : [];
1890
- });
1891
- };
1892
- /**
1893
- * Certificates and withdrawals on credentials of no custody account the
1894
- * transaction names. A withdrawal may also run a known logic, which
1895
- * `checkLogicUse` ties to the control UTxOs naming it.
1896
- */
1897
- const checkForeignStakeEffects = (facts, candidates, networkId) => {
1898
- const refusals = [];
1899
- const stakeScriptHashes = new Set(candidates.map(({ stakeScriptHash }) => stakeScriptHash));
1900
- for (const [certificateIndex, certificate] of (facts.body.certificates ?? []).entries()) {
1901
- const credential = stakeCredentialOf(certificate);
1902
- if (credential?.type !== _laceio_cardano_core.Cardano.CredentialType.ScriptHash || !stakeScriptHashes.has(credential.hash)) refusals.push({
1903
- code: "custody-unsupported-operation",
1904
- reason: "foreign-certificate",
1905
- certificateIndex,
1906
- certificateType: certificate.__typename
1907
- });
1908
- }
1909
- for (const { stakeAddress } of facts.body.withdrawals ?? []) {
1910
- const scriptHash = scriptHashOfRewardAccount(stakeAddress);
1911
- if (scriptHash === void 0 || !(stakeScriptHashes.has(scriptHash) || require_outputs.isKnownCustodyLogic(scriptHash))) refusals.push({
1912
- code: "custody-unsupported-operation",
1913
- reason: "foreign-withdrawal",
1914
- rewardAccount: stakeAddress
1915
- });
1916
- else if (_laceio_cardano_core.Cardano.Address.fromString(stakeAddress)?.getNetworkId() !== networkId) refusals.push({
1917
- code: "custody-context-mismatch",
1918
- reason: "network-mismatch",
1919
- address: stakeAddress
1920
- });
1921
- }
1922
- return refusals;
1923
- };
1924
- /**
1925
- * Mints of other policies, and custody mints of a shape no mint redeemer
1926
- * accepts: one state NFT name, or grant names of one account that all issue
1927
- * or all burn.
1928
- */
1929
- const checkMint = (facts) => {
1930
- const byPolicy = /* @__PURE__ */ new Map();
1931
- for (const entry of facts.mint) {
1932
- const policyId = require_outputs.policyIdOf(entry[0]);
1933
- byPolicy.set(policyId, [...byPolicy.get(policyId) ?? [], entry]);
1934
- }
1935
- return [...byPolicy].flatMap(([policyId, entries]) => {
1936
- const refuseEach = (reason) => entries.map(([assetId, quantity]) => ({
1937
- code: "custody-unsupported-operation",
1938
- reason,
1939
- assetId,
1940
- quantity
1941
- }));
1942
- if (!versionOfPolicy(policyId)) return refuseEach("foreign-mint");
1943
- const shape = mintShapeOf(entries);
1944
- if (shape.kind === "invalid") return refuseEach("unsupported-mint");
1945
- return shape.kind === "mixed" ? [{
1946
- code: "custody-unsupported-operation",
1947
- reason: "issue-sweep-mixed",
1948
- account: {
1949
- accountValidatorHash: policyId,
1950
- stakeScriptHash: shape.stakeScriptHash
1951
- }
1952
- }] : [];
1953
- });
1954
- };
1955
- /**
1956
- * What an output at a custody account that holds no account token must be:
1957
- * a plain deposit, or a reserve in the format Lace writes, `d87980` with no
1958
- * reference script. A datum hash locks value behind a preimage no account
1959
- * builder holds; another datum or a reference script raises the fee and
1960
- * minimum of every later spend.
1961
- */
1962
- const findAccountOutputRefusal = (view, location) => {
1963
- if (view.datumKind === "hash") return {
1964
- code: "custody-locked-output",
1965
- reason: "datum-hash",
1966
- location
1967
- };
1968
- if (view.inlineDatum !== void 0) return view.hasReferenceScript || !isCustodyReserveDatum(view.inlineDatum) ? {
1969
- code: "custody-unsupported-operation",
1970
- reason: "foreign-reserve",
1971
- location
1972
- } : void 0;
1973
- return view.hasReferenceScript ? {
1974
- code: "custody-unsupported-operation",
1975
- reason: "deposit-reference-script",
1976
- location
1977
- } : void 0;
1978
- };
1979
- /**
1980
- * Rules over every output and resolution: custody addresses on the named
1981
- * network, account tokens at their own account addresses, no value locked
1982
- * where no redeemer can reach it, and account outputs without an account
1983
- * token in a shape Lace writes. Outputs holding an account token are the
1984
- * control and grant outputs, which their account's checks read.
1985
- */
1986
- const checkOutputs = (facts, networkId) => {
1987
- const refusals = [];
1988
- const custodyViews = [
1989
- ...[
1990
- ...facts.inputs,
1991
- ...facts.referenceInputs,
1992
- ...facts.collaterals
1993
- ].map(({ output }) => output),
1994
- ...facts.outputs,
1995
- ...facts.collateralReturn ? [facts.collateralReturn] : []
1996
- ].filter(({ addressClass }) => addressClass.kind !== "other");
1997
- for (const view of custodyViews) if (view.address?.getNetworkId() !== networkId) refusals.push({
1998
- code: "custody-context-mismatch",
1999
- reason: "network-mismatch",
2000
- address: view.txOut.address
2001
- });
2002
- const accountValidatorHashes = require_hex.SUPPORTED_CUSTODY_CONTRACT_VERSIONS.map((version) => version.accountValidatorHash);
2003
- for (const { input, output } of facts.inputs) if (require_outputs.holdsMisplacedAccountToken(output, accountValidatorHashes)) refusals.push({
2004
- code: "custody-unsupported-operation",
2005
- reason: "account-token-misplaced",
2006
- location: {
2007
- kind: "input",
2008
- input
2009
- }
2010
- });
2011
- const locatedOutputs = [...facts.outputs.map((view, index) => ({
2012
- view,
2013
- location: {
2014
- kind: "output",
2015
- index
2016
- }
2017
- })), ...facts.collateralReturn ? [{
2018
- view: facts.collateralReturn,
2019
- location: { kind: "collateral-return" }
2020
- }] : []];
2021
- for (const { view, location } of locatedOutputs) {
2022
- if (location.kind === "output" && require_outputs.holdsMisplacedAccountToken(view, accountValidatorHashes)) refusals.push({
2023
- code: "custody-unsupported-operation",
2024
- reason: "account-token-misplaced",
2025
- location
2026
- });
2027
- const { addressClass } = view;
2028
- if (addressClass.kind === "unspendable") refusals.push({
2029
- code: "custody-locked-output",
2030
- reason: "unspendable-address",
2031
- location
2032
- });
2033
- else if (addressClass.kind === "account" && require_outputs.holdsNoAccountToken(view.value, addressClass.contractVersion.accountValidatorHash)) {
2034
- const refusal = findAccountOutputRefusal(view, location);
2035
- if (refusal) refusals.push(refusal);
2036
- }
2037
- }
2038
- return refusals;
2039
- };
2040
- const checkBodyFields = (facts) => {
2041
- const body = facts.body;
2042
- return UNSUPPORTED_BODY_FIELDS.filter((field) => body[field] !== void 0).map((field) => ({
2043
- code: "custody-unsupported-operation",
2044
- reason: "unsupported-body-field",
2045
- field
2046
- }));
2047
- };
2048
- /** With a record, the transaction operates that account and no other. */
2049
- const checkRecordAccount = (operated, record) => {
2050
- if (!record) return [];
2051
- const refusals = operated.filter((account) => !isSameAccount(account, record)).map((account) => ({
2052
- code: "custody-context-mismatch",
2053
- reason: "foreign-account",
2054
- account: hashesOf(account)
2055
- }));
2056
- if (!operated.some((account) => isSameAccount(account, record))) refusals.push({
2057
- code: "custody-context-mismatch",
2058
- reason: "account-not-operated",
2059
- account: {
2060
- accountValidatorHash: record.contractVersion.accountValidatorHash,
2061
- stakeScriptHash: record.stakeScriptHash
2062
- }
2063
- });
2064
- return refusals;
2065
- };
2066
- //#endregion
2067
- //#region ../../packages/lib/cardano-custody/src/analysis/inspect.ts
2068
- const readRecord = (options) => {
2069
- if (options.accountRecord === void 0) return require_hex.ok(void 0);
2070
- const parsed = parseCustodyAccountRecord(options.accountRecord, options.chainId.networkMagic);
2071
- if (parsed.ok) return require_hex.ok(parsed.value.identity);
2072
- return require_hex.fail(parsed.error.code === "unsupported-contract-version" ? {
2073
- code: "custody-unsupported-version",
2074
- reason: "unsupported-contract-version",
2075
- accountValidatorHash: parsed.error.accountValidatorHash
2076
- } : {
2077
- code: "custody-context-mismatch",
2078
- reason: "invalid-record",
2079
- recordError: parsed.error
2080
- });
2081
- };
2082
- const paymentKeyHashOf = (view) => {
2083
- const credential = paymentCredentialOf(view);
2084
- return credential?.type === _laceio_cardano_core.Cardano.CredentialType.KeyHash ? credential.hash : void 0;
2085
- };
2086
- const externalInputOf = ({ input, output }, deviceKeyHash) => ({
2087
- input,
2088
- address: output.txOut.address,
2089
- value: output.txOut.value,
2090
- isDeviceKey: deviceKeyHash !== void 0 && paymentKeyHashOf(output) === deviceKeyHash
2091
- });
2092
- const validityOf = ({ validityInterval }, slotToPosixMs) => {
2093
- const bounds = {};
2094
- if (validityInterval?.invalidBefore !== void 0) {
2095
- const slot = validityInterval.invalidBefore;
2096
- bounds.invalidBefore = {
2097
- slot,
2098
- posixMs: slotToPosixMs(slot)
2099
- };
2100
- }
2101
- if (validityInterval?.invalidHereafter !== void 0) {
2102
- const slot = validityInterval.invalidHereafter;
2103
- bounds.invalidHereafter = {
2104
- slot,
2105
- posixMs: slotToPosixMs(slot)
2106
- };
2107
- }
2108
- return bounds;
2109
- };
2110
- /**
2111
- * Reads every custody effect of a transaction from its bytes and the
2112
- * caller's resolutions of its inputs, and refuses what Lace never signs.
2113
- * The SDK checks its own builds with it before asking for a signature, and
2114
- * the hosted signer derives its consent and its refusals from it, so both
2115
- * apply one set of rules.
2116
- *
2117
- * It takes the `Serialization.Transaction` whose body hash is signed, so
2118
- * what it reads is what a signature commits to. Resolutions come from the
2119
- * caller and are not checked against the chain; an inline datum in a
2120
- * resolution wins over a datum hash reported beside it. A custody account
2121
- * is any address whose payment credential is a supported account validator
2122
- * and whose stake credential is an inline script.
2123
- *
2124
- * Every operation on an account runs the logic its control datum names,
2125
- * through a withdrawal from the logic's stake credential, and that logic
2126
- * must be one of the contract version's catalogue. A reference input may
2127
- * carry a reference script only as a contract script parked at the output
2128
- * reference the version pins on the inspected network.
2129
- *
2130
- * Witness-set redeemers are bound to the signed body only through its
2131
- * script data hash, which inspection does not recompute. A spend redeemer
2132
- * must fit the UTxO it spends: `Device` the control UTxO, `SpendWithGrant`
2133
- * or `SweepGrant` a grant UTxO, `Fund` a deposit. `SpendWithGrant` is
2134
- * refused unless `allowGrantSpend` is set, and the mint redeemer must fit
2135
- * the mint: `CreateAccount` a state NFT, `IssueGrants` grant tokens minted,
2136
- * `BurnGrants` grant tokens burned. Another redeemer set under the same body
2137
- * cannot change what the body does: it can only run a contract branch that
2138
- * the device's signature does not authorise.
2139
- */
2140
- const inspectCustodyTransaction = (transaction, resolvedInputs, options) => {
2141
- const record = readRecord(options);
2142
- if (!record.ok) return require_hex.fail({ refusals: [record.error] });
2143
- const serializedBody = transaction.body();
2144
- const body = serializedBody.toCore();
2145
- const resolved = resolveBody(body, resolvedInputs);
2146
- if (!resolved.ok) return require_hex.fail({ refusals: resolved.error });
2147
- const metadata = readCustodyMetadata(transaction, body);
2148
- if (!metadata.ok) return require_hex.fail({ refusals: [metadata.error] });
2149
- const facts = readTransaction({
2150
- body,
2151
- bodyCbor: serializedBody.toCbor(),
2152
- witness: transaction.witnessSet().toCore()
2153
- }, resolved.value, options);
2154
- const { networkId } = options.chainId;
2155
- const scope = {
2156
- options,
2157
- record: record.value,
2158
- deviceKeyHash: options.deviceKeyHash?.toLowerCase()
2159
- };
2160
- const candidates = findCandidates(facts, record.value);
2161
- const outcomes = candidates.map((candidate) => inspectAccount(candidate, facts, scope));
2162
- const operated = outcomes.filter(({ isOperated }) => isOperated);
2163
- const isOperatedAccount = (view) => operated.some(({ report }) => require_outputs.isAtAccount(view, report));
2164
- const externalInputs = facts.inputs.filter(({ output }) => !isOperatedAccount(output)).map((view) => externalInputOf(view, scope.deviceKeyHash));
2165
- const externalOutputs = facts.outputs.flatMap((view, outputIndex) => view.addressClass.kind === "other" ? [{
2166
- outputIndex,
2167
- address: view.txOut.address,
2168
- value: view.txOut.value
2169
- }] : []);
2170
- const feeShares = feeSharesOf({
2171
- externalInputs,
2172
- externalOutputs,
2173
- fee: body.fee
2174
- });
2175
- const refusals = inCheckOrder([
2176
- ...checkBodyFields(facts),
2177
- ...checkWithdrawalAccounts(facts),
2178
- ...checkForeignStakeEffects(facts, candidates, networkId),
2179
- ...checkMint(facts),
2180
- ...checkRedeemerPointers(facts),
2181
- ...checkSpendWitnesses(facts),
2182
- ...checkLogicUse(facts, outcomes.flatMap(({ logics }) => logics)),
2183
- ...checkReferenceScripts(facts, options.chainId.networkMagic),
2184
- ...checkOutputs(facts, networkId),
2185
- ...checkRecordAccount(operated.map(({ report }) => report), record.value),
2186
- ...outcomes.flatMap((outcome) => outcome.refusals),
2187
- ...checkGrantSpendMetadata(operated, metadata.value),
2188
- ...checkLogicDraws(operated, feeShares, externalOutputs.some(({ address }) => feeShares.payers.includes(address)))
2189
- ]);
2190
- if (refusals.length > 0) return require_hex.fail({ refusals });
2191
- return require_hex.ok({
2192
- accounts: withOperations(operated, feeShares),
2193
- deposits: facts.outputs.flatMap((view, outputIndex) => view.addressClass.kind === "account" && view.datumKind !== "hash" && !isOperatedAccount(view) ? [{
2194
- outputIndex,
2195
- address: view.txOut.address,
2196
- value: view.txOut.value,
2197
- accountValidatorHash: view.addressClass.contractVersion.accountValidatorHash,
2198
- stakeScriptHash: view.addressClass.stakeScriptHash,
2199
- datum: view.datumKind
2200
- }] : []),
2201
- externalOutputs,
2202
- externalInputs,
2203
- collateralInputs: facts.collaterals.map((view) => externalInputOf(view, scope.deviceKeyHash)),
2204
- ...body.collateralReturn ? { collateralReturn: {
2205
- address: body.collateralReturn.address,
2206
- value: body.collateralReturn.value
2207
- } } : {},
2208
- requiredSigners: [...facts.requiredSigners],
2209
- validity: validityOf(body, options.slotToPosixMs),
2210
- fee: body.fee,
2211
- feeShares,
2212
- ...body.donation === void 0 ? {} : { donation: body.donation },
2213
- ...metadata.value === void 0 ? {} : { metadata: metadata.value }
2214
- });
2215
- };
2216
- //#endregion
2217
- //#region ../../packages/lib/cardano-custody/src/analysis/requested-scope.ts
2218
- /**
2219
- * The recipient a bech32 Shelley payment address names, when the text is
2220
- * spelled exactly as `toBech32` writes it: lower case, with the prefix of the
2221
- * network its header names. Bech32 also decodes an upper-case spelling and
2222
- * ignores the prefix, so a mainnet-looking address could otherwise stand for
2223
- * a testnet recipient.
2224
- */
2225
- const canonicalRecipientOf = (value) => {
2226
- if (typeof value !== "string" || !_laceio_cardano_core.Cardano.Address.isValidBech32(value)) return;
2227
- const address = _laceio_cardano_core.Cardano.Address.fromBech32(value);
2228
- const recipient = require_outputs.custodyRecipientFromAddress(address);
2229
- return recipient.ok && address.toBech32() === value ? {
2230
- address,
2231
- recipient: recipient.value
2232
- } : void 0;
2233
- };
2234
- const recipientKeysOn = (recipients, networkId) => {
2235
- const keys = [];
2236
- for (const text of recipients) {
2237
- const canonical = canonicalRecipientOf(text);
2238
- if (canonical?.address.getNetworkId() !== networkId) return void 0;
2239
- keys.push(require_outputs.recipientKey(canonical.recipient));
2240
- }
2241
- return keys;
2242
- };
2243
- const hasSameRecipients = (requested, actual, networkId) => {
2244
- const requestedKeys = recipientKeysOn(requested, networkId);
2245
- const { recipients } = actual.scope;
2246
- return requestedKeys !== void 0 && requestedKeys.length === recipients.length && requestedKeys.every((key, index) => key === require_outputs.recipientKey(recipients[index]));
2247
- };
2248
- /**
2249
- * The fields where a requested scope differs from a grant a transaction
2250
- * issues, in declaration order; empty when they match. Every field must be
2251
- * equal exactly. Recipients must be the same Plutus addresses in the same
2252
- * order, and every requested recipient must be on the account network,
2253
- * because the datum carries no network and the grant would otherwise send
2254
- * to another network's address with the same credentials.
2255
- */
2256
- const compareRequestedGrantScope = (requested, actual, chainId) => {
2257
- const { scope } = actual;
2258
- const isEqual = {
2259
- slot: requested.slot === actual.slot,
2260
- grantee: requested.grantee === actual.grantee,
2261
- asset: requested.asset.policyId === scope.asset.policyId && requested.asset.assetName === scope.asset.assetName,
2262
- perCallCap: requested.perCallCap === scope.perCallCap,
2263
- cap: requested.cap === scope.cap,
2264
- lovelacePerCallCap: requested.lovelacePerCallCap === scope.lovelacePerCallCap,
2265
- lovelaceCap: requested.lovelaceCap === scope.lovelaceCap,
2266
- expiresAt: requested.expiresAt === scope.expiresAt,
2267
- recipients: hasSameRecipients(requested.recipients, actual, chainId.networkId)
2268
- };
2269
- return Object.keys(isEqual).filter((field) => !isEqual[field]);
2270
- };
2271
- const serializeCustodyRequestedGrantScope = (scope) => ({
2272
- slot: scope.slot.toString(),
2273
- grantee: scope.grantee,
2274
- asset: {
2275
- policyId: scope.asset.policyId,
2276
- assetName: scope.asset.assetName
2277
- },
2278
- perCallCap: scope.perCallCap.toString(),
2279
- cap: scope.cap.toString(),
2280
- lovelacePerCallCap: scope.lovelacePerCallCap.toString(),
2281
- lovelaceCap: scope.lovelaceCap.toString(),
2282
- expiresAt: scope.expiresAt.toString(),
2283
- recipients: [...scope.recipients]
2284
- });
2285
- const SCOPE_FIELDS = [
2286
- "slot",
2287
- "grantee",
2288
- "asset",
2289
- "perCallCap",
2290
- "cap",
2291
- "lovelacePerCallCap",
2292
- "lovelaceCap",
2293
- "expiresAt",
2294
- "recipients"
2295
- ];
2296
- const ASSET_FIELDS = ["policyId", "assetName"];
2297
- const INTEGER_FIELDS = [
2298
- "slot",
2299
- "perCallCap",
2300
- "cap",
2301
- "lovelacePerCallCap",
2302
- "lovelaceCap",
2303
- "expiresAt"
2304
- ];
2305
- const MAX_ASSET_NAME_BYTES = 32;
2306
- /** Canonical decimal: no sign, no leading zero, no exponent. */
2307
- const NON_NEGATIVE_DECIMAL = /^(?:0|[1-9]\d*)$/;
2308
- const isNonNegativeDecimal = (value) => typeof value === "string" && NON_NEGATIVE_DECIMAL.test(value);
2309
- const malformed = (reason, field) => require_hex.fail({
2310
- code: "malformed-requested-scope",
2311
- reason,
2312
- ...field === void 0 ? {} : { field }
2313
- });
2314
- const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2315
- const hasExactFields = (value, fields) => {
2316
- const keys = Object.keys(value);
2317
- return keys.length === fields.length && fields.every((field) => keys.includes(field));
2318
- };
2319
- const isShelleyPaymentAddress = (value) => canonicalRecipientOf(value) !== void 0;
2320
- const isWellFormedAsset = (asset) => {
2321
- const { policyId, assetName } = asset;
2322
- if (policyId === "") return assetName === "";
2323
- return require_hex.isHash28Hex(policyId) && require_hex.isLowerCaseHex(assetName) && assetName.length <= MAX_ASSET_NAME_BYTES * 2;
2324
- };
2325
- /**
2326
- * Reads a requested scope from protocol JSON, refusing anything but the
2327
- * exact shape `serializeCustodyRequestedGrantScope` writes: no missing or
2328
- * extra field, canonical non-negative decimal integers, a positive expiry,
2329
- * a 28-byte grantee, a lovelace or 28-byte policy asset with a name of at
2330
- * most 32 bytes, and at most 8 Shelley payment addresses in canonical
2331
- * bech32. Network membership of the recipients is checked when comparing.
2332
- *
2333
- * @param input - the scope as JSON text or as an already parsed value
2334
- */
2335
- const parseCustodyRequestedGrantScope = (input) => {
2336
- let value = input;
2337
- if (typeof input === "string") try {
2338
- value = JSON.parse(input);
2339
- } catch {
2340
- return malformed("expected JSON");
2341
- }
2342
- if (!isPlainObject(value) || !hasExactFields(value, SCOPE_FIELDS)) return malformed(`expected exactly ${SCOPE_FIELDS.join(", ")}`);
2343
- const integerField = INTEGER_FIELDS.find((field) => !isNonNegativeDecimal(value[field]));
2344
- if (integerField) return malformed("expected a non-negative decimal string", integerField);
2345
- if (value.expiresAt === "0") return malformed("expected a positive expiry", "expiresAt");
2346
- if (!require_hex.isHash28Hex(value.grantee)) return malformed("expected 28 bytes of lower-case hex", "grantee");
2347
- const { asset, recipients } = value;
2348
- if (!isPlainObject(asset) || !hasExactFields(asset, ASSET_FIELDS) || !isWellFormedAsset(asset)) return malformed("expected lovelace or a policy id and asset name", "asset");
2349
- if (!Array.isArray(recipients) || recipients.length > 8 || !recipients.every(isShelleyPaymentAddress)) return malformed(`expected at most 8 bech32 payment addresses`, "recipients");
2350
- const integers = value;
2351
- return require_hex.ok({
2352
- slot: BigInt(integers.slot),
2353
- grantee: value.grantee,
2354
- asset: {
2355
- policyId: asset.policyId,
2356
- assetName: asset.assetName
2357
- },
2358
- perCallCap: BigInt(integers.perCallCap),
2359
- cap: BigInt(integers.cap),
2360
- lovelacePerCallCap: BigInt(integers.lovelacePerCallCap),
2361
- lovelaceCap: BigInt(integers.lovelaceCap),
2362
- expiresAt: BigInt(integers.expiresAt),
2363
- recipients
2364
- });
2365
- };
2366
- //#endregion
2367
- //#region ../../packages/lib/cardano-custody/src/classify.ts
2368
- /** The tokens of a value under a policy. */
2369
- const tokensUnder = (value, policyId) => [...value.assets?.entries() ?? []].filter(([assetId, quantity]) => quantity !== 0n && _laceio_cardano_core.Cardano.AssetId.getPolicyId(assetId) === policyId).map(([assetId]) => assetId);
2370
- /** Reads a UTxO that holds exactly one grant token of the account, in quantity one. */
2371
- const classifyGrant = (utxo, token, slot) => {
2372
- const [, output] = utxo;
2373
- if (output.value.assets?.size !== 1) return { anomaly: {
2374
- code: "grant-value-not-strict",
2375
- utxo,
2376
- slot
2377
- } };
2378
- if (output.datum === void 0) return { anomaly: {
2379
- code: "grant-datum-not-inline",
2380
- utxo,
2381
- slot
2382
- } };
2383
- const decoded = require_outputs.decodeCustodyGrant(output.datum);
2384
- if (!decoded.ok) return { anomaly: {
2385
- code: "grant-datum-malformed",
2386
- utxo,
2387
- slot,
2388
- error: decoded.error
2389
- } };
2390
- if (decoded.value.slot !== slot) return { anomaly: {
2391
- code: "grant-slot-mismatch",
2392
- utxo,
2393
- slot,
2394
- datumSlot: decoded.value.slot
2395
- } };
2396
- return { grant: {
2397
- utxo,
2398
- grant: decoded.value,
2399
- token
2400
- } };
2401
- };
2402
- /** The grant token and its slot, when the tokens are exactly one own grant token held once. */
2403
- const ownGrantToken = (account, value, tokens) => {
2404
- const [token] = tokens;
2405
- if (tokens.length !== 1 || value.assets?.get(token) !== 1n) return void 0;
2406
- const name = require_tokens.parseCustodyAccountTokenName(_laceio_cardano_core.Cardano.AssetId.getAssetName(token));
2407
- return name?.kind === "grant" && name.stakeScriptHash === account.stakeScriptHash ? {
2408
- token,
2409
- slot: name.slot
2410
- } : void 0;
2411
- };
2412
- /**
2413
- * Sorts an account's UTxOs by kind. UTxOs not at the exact account address
2414
- * are ignored. It never throws: a UTxO that holds an account token in a
2415
- * shape the contract rules out is listed in `anomalies`.
2416
- */
2417
- const classifyCustodyAccountUtxos = (account, utxos) => {
2418
- const policyId = _laceio_cardano_core.Cardano.PolicyId(account.accountValidatorHash);
2419
- const stateNft = _laceio_cardano_core.Cardano.AssetId.fromParts(policyId, _laceio_cardano_core.Cardano.AssetName(account.stakeScriptHash));
2420
- const controls = [];
2421
- const grants = [];
2422
- const own = [];
2423
- const foreign = [];
2424
- const funds = [];
2425
- const stranded = [];
2426
- const anomalies = [];
2427
- for (const utxo of utxos) {
2428
- const [, output] = utxo;
2429
- if (output.address !== account.address) continue;
2430
- const tokens = tokensUnder(output.value, policyId);
2431
- if (tokens.includes(stateNft)) controls.push(utxo);
2432
- else if (tokens.length > 0) {
2433
- const grantToken = ownGrantToken(account, output.value, tokens);
2434
- if (!grantToken) {
2435
- anomalies.push({
2436
- code: "unexpected-account-tokens",
2437
- utxo,
2438
- assetIds: tokens
2439
- });
2440
- continue;
2441
- }
2442
- const classified = classifyGrant(utxo, grantToken.token, grantToken.slot);
2443
- if ("grant" in classified) grants.push(classified.grant);
2444
- else anomalies.push(classified.anomaly);
2445
- } else if (output.datum !== void 0) (output.scriptReference === void 0 && isCustodyReserveDatum(output.datum) ? own : foreign).push(utxo);
2446
- else if (output.datumHash === void 0) funds.push(utxo);
2447
- else stranded.push(utxo);
2448
- }
2449
- grants.sort((a, b) => a.grant.slot < b.grant.slot ? -1 : a.grant.slot > b.grant.slot ? 1 : 0);
2450
- return {
2451
- controls,
2452
- grants,
2453
- reserves: {
2454
- own,
2455
- foreign
2456
- },
2457
- funds,
2458
- stranded,
2459
- anomalies
2460
- };
2461
- };
2462
- /**
2463
- * The liveness of a grant against a control state at `now`. A grant dead for
2464
- * several reasons takes the first of superseded, revoked and expired, so
2465
- * `expired` alone means a sweep needs a validity interval that starts after
2466
- * the expiry.
2467
- *
2468
- * @param now - POSIX milliseconds; a non-finite value counts as past every expiry
2469
- */
2470
- const custodyGrantLiveness = (grant, state, now) => {
2471
- if (grant.generation < state.grantGeneration) return "superseded";
2472
- if (grant.generation > state.grantGeneration) return "future-generation";
2473
- if (!require_outputs.isCustodyGrantCurrent(grant, state)) return "revoked";
2474
- return require_outputs.isCustodyGrantExpired(grant, now) ? "expired" : "current";
2475
- };
2476
- //#endregion
2477
- //#region ../../packages/lib/cardano-custody/src/device-key.ts
2478
- /**
2479
- * The CIP-1854 path `m/1854'/1815'/a'/0/i` of custody device and creator
2480
- * keys. The account index `a` belongs to the contract version and network:
2481
- * `custodyDeviceAccountIndex` gives it.
2482
- */
2483
- const CUSTODY_DEVICE_KEY_PATH = {
2484
- purpose: 1854,
2485
- coinType: 1815,
2486
- role: 0
2487
- };
2488
- /** First index that is a hardened derivation; device keys use soft ones only. */
2489
- const HARDENED_INDEX = 2147483648;
2490
- /**
2491
- * The device key at `m/1854'/1815'/a'/0/index`, derived from the CIP-1854
2492
- * account extended public key of account `a` by soft derivation, so no
2493
- * secret is needed.
2494
- */
2495
- const deriveCustodyDeviceKey = async (accountPublicKey, index) => {
2496
- if (!Number.isInteger(index) || index < 0 || index >= HARDENED_INDEX) return require_hex.fail({
2497
- code: "invalid-device-index",
2498
- index
2499
- });
2500
- await _laceio_cardano_crypto.ready();
2501
- try {
2502
- const publicKey = _laceio_cardano_crypto.Bip32PublicKey.fromHex(accountPublicKey).derive([CUSTODY_DEVICE_KEY_PATH.role, index]).toRawKey();
2503
- return require_hex.ok({
2504
- index,
2505
- publicKey: publicKey.hex(),
2506
- keyHash: publicKey.hash().hex()
2507
- });
2508
- } catch {
2509
- return require_hex.fail({ code: "invalid-account-public-key" });
2510
- }
2511
- };
2512
- /**
2513
- * A short form of a device key hash for people to compare across devices:
2514
- * its first and last 8 hex characters.
2515
- */
2516
- const custodyDeviceFingerprint = (keyHash) => `${keyHash.slice(0, 8)}…${keyHash.slice(-8)}`;
2517
- //#endregion
2518
- //#region ../../packages/contract/cardano-context/src/custody/account-state.ts
2519
- /** Whether the account's contract version lists the logic in its catalogue. */
2520
- const isKnownLogic = (accountValidatorHash, logic) => {
2521
- const version = require_hex.findCustodyContractVersion(accountValidatorHash);
2522
- return version.ok && require_hex.findCustodyLogic(version.value, logic).ok;
2523
- };
2524
- /**
2525
- * Classifies a custody account's UTxOs by the rules of the custody contract
2526
- * and reads the control state.
2527
- */
2528
- const classifyCardanoCustodyAccount = ({ account, utxos, withdrawableRewards, protocolParameters }) => {
2529
- const { controls, grants, reserves, funds, stranded, anomalies } = classifyCustodyAccountUtxos(account, utxos);
2530
- const holdings = {
2531
- funds,
2532
- reserves,
2533
- grants,
2534
- grantLockedLovelace: grants.reduce((total, { utxo: [, output] }) => total + output.value.coins, 0n),
2535
- stranded,
2536
- anomalies,
2537
- withdrawableRewards
2538
- };
2539
- if (controls.length === 0) return {
2540
- ...holdings,
2541
- status: "notCreated"
2542
- };
2543
- const invalid = (reason) => ({
2544
- ...holdings,
2545
- status: "invalid",
2546
- reason,
2547
- controlUtxos: controls
2548
- });
2549
- if (controls.length > 1) return invalid({ code: "multiple-control-utxos" });
2550
- const [controlUtxo] = controls;
2551
- const [, controlOutput] = controlUtxo;
2552
- if (controlOutput.datum === void 0) return invalid({ code: "missing-inline-datum" });
2553
- const logic = require_outputs.decodeCustodyStateLogic(controlOutput.datum);
2554
- if (!logic.ok) return invalid({
2555
- code: "undecodable-state",
2556
- error: logic.error
2557
- });
2558
- if (!isKnownLogic(account.accountValidatorHash, logic.value)) return invalid({
2559
- code: "unknown-logic",
2560
- logic: logic.value
2561
- });
2562
- const decoded = require_outputs.decodeCustodyAccountState(controlOutput.datum);
2563
- if (!decoded.ok) return invalid({
2564
- code: "undecodable-state",
2565
- error: decoded.error
2566
- });
2567
- const controlMinimumLovelace = (0, _laceio_cardano_tx_construction.minAdaRequired)(controlOutput, BigInt(protocolParameters.coinsPerUtxoByte));
2568
- const controlLovelace = controlOutput.value.coins;
2569
- const deviceKeyHash = account.device?.keyHash;
2570
- return {
2571
- ...holdings,
2572
- status: "live",
2573
- controlUtxo,
2574
- state: decoded.value,
2575
- controlMinimumLovelace,
2576
- controlHeadroomLovelace: controlLovelace > controlMinimumLovelace ? controlLovelace - controlMinimumLovelace : 0n,
2577
- ...deviceKeyHash !== void 0 && { currentDeviceListed: decoded.value.devices.some((device) => device.toLowerCase() === deviceKeyHash.toLowerCase()) }
2578
- };
2579
- };
2580
- //#endregion
2581
- //#region ../../packages/contract/cardano-context/src/custody/build-transaction.ts
2582
- /**
2583
- * The most lovelace a grant spend's fee may take when the operation names
2584
- * no `feeBound`: 1,500,000 lovelace. The spend lowers the grant's remaining caps by its outputs
2585
- * and fee before the fee is final, so the bound is what the caps must leave
2586
- * room for.
2587
- */
2588
- const CARDANO_CUSTODY_DEFAULT_GRANT_FEE_BOUND = 1500000n;
2589
- //#endregion
2590
- //#region ../../packages/contract/cardano-context/src/custody/grant-status.ts
2591
- /**
2592
- * Any key hash: an unrestricted grant pays out to any address, and an
2593
- * enterprise key address makes the smallest output.
2594
- */
2595
- const ANY_KEY_HASH = (0, _laceio_cardano_crypto.Hash28ByteBase16)("00".repeat(28));
2596
- /** The addresses a grant may pay out to, or one standing for any address. */
2597
- const payoutAddressesOf = ({ recipients }, networkId) => {
2598
- if (recipients.length === 0) return [_laceio_cardano_core.Cardano.EnterpriseAddress.fromCredentials(networkId, {
2599
- type: _laceio_cardano_core.Cardano.CredentialType.KeyHash,
2600
- hash: ANY_KEY_HASH
2601
- }).toAddress().toBech32()];
2602
- return recipients.flatMap((recipient) => {
2603
- const address = require_outputs.custodyRecipientToAddress(recipient, networkId);
2604
- return address.ok ? [address.value.toBech32()] : [];
2605
- });
2606
- };
2607
- /** The scope's token, or undefined when its policy id or name is malformed. */
2608
- const tokenOf = ({ asset }) => {
2609
- try {
2610
- return _laceio_cardano_core.Cardano.AssetId.fromParts(_laceio_cardano_core.Cardano.PolicyId(asset.policyId), _laceio_cardano_core.Cardano.AssetName(asset.assetName));
2611
- } catch {
2612
- return;
2613
- }
2614
- };
2615
- const smallerOf = (a, b) => a < b ? a : b;
2616
- /**
2617
- * Whether a grant scope can no longer pay the smallest spend. That spend
2618
- * is one output to the cheapest address the grant allows, holding the
2619
- * ledger minimum lovelace, plus the fee bound. A lovelace grant draws both
2620
- * from its lovelace caps (`cap`, `perCallCap`). A token grant draws one
2621
- * token from its token caps and the lovelace from `lovelaceCap` and
2622
- * `lovelacePerCallCap`. A grant with no address it can pay is exhausted.
2623
- */
2624
- const isCardanoCustodyGrantExhausted = (scope, { protocolParameters, networkId, feeBound = CARDANO_CUSTODY_DEFAULT_GRANT_FEE_BOUND }) => {
2625
- const isLovelace = require_outputs.isCustodyLovelaceAsset(scope.asset);
2626
- const token = isLovelace ? void 0 : tokenOf(scope);
2627
- if (!isLovelace && !token) return true;
2628
- const addresses = payoutAddressesOf(scope, networkId);
2629
- if (addresses.length === 0) return true;
2630
- const coinsPerUtxoByte = BigInt(protocolParameters.coinsPerUtxoByte);
2631
- const smallestLovelaceSpend = addresses.map((address) => (0, _laceio_cardano_tx_construction.minAdaRequired)({
2632
- address,
2633
- value: {
2634
- coins: 0n,
2635
- ...token && { assets: new Map([[token, 1n]]) }
2636
- }
2637
- }, coinsPerUtxoByte)).reduce(smallerOf) + feeBound;
2638
- if (isLovelace) return smallerOf(scope.cap, scope.perCallCap) < smallestLovelaceSpend;
2639
- return smallerOf(scope.cap, scope.perCallCap) < 1n || smallerOf(scope.lovelaceCap, scope.lovelacePerCallCap) < smallestLovelaceSpend;
2640
- };
2641
- const bySlot = ({ grant: a }, { grant: b }) => a.slot < b.slot ? -1 : a.slot > b.slot ? 1 : 0;
2642
- /**
2643
- * The status of a grant the confirmed view holds and the in-flight view
2644
- * keeps. `grant` is its in-flight version: an unsettled grant spend lowers
2645
- * its caps and changes nothing else.
2646
- */
2647
- const confirmedGrantStatus = (grant, { confirmed, inFlight, now, ...pricing }) => {
2648
- const liveness = custodyGrantLiveness(grant, confirmed, now);
2649
- if (liveness === "revoked" || liveness === "superseded") return liveness;
2650
- if (liveness === "future-generation") return "pending";
2651
- const next = custodyGrantLiveness(grant, inFlight, now);
2652
- if (next === "revoked" || next === "superseded") return "revoking";
2653
- if (liveness === "expired") return "expired";
2654
- return isCardanoCustodyGrantExhausted(grant.scope, pricing) ? "exhausted" : "live";
2655
- };
2656
- /**
2657
- * The status of every grant UTxO of a custody account, in slot order.
2658
- * `inFlight` is the account after the submitted operations that have not
2659
- * settled; `confirmed` is the account on chain, absent while it has no
2660
- * confirmed control state. A grant is known by its slot, which no grant
2661
- * ever reuses. `now` is POSIX milliseconds: the time of the chain tip.
2662
- *
2663
- * A grant only the in-flight view holds is `pending`, and one only the
2664
- * confirmed view holds is `sweeping`. A grant both hold is judged against
2665
- * the confirmed state, and is `revoking` when the in-flight state kills it.
2666
- * Its listed version is the in-flight one, so an unsettled grant spend
2667
- * shows its lowered caps at once.
2668
- */
2669
- const classifyCardanoCustodyGrants = ({ confirmed, inFlight, now, protocolParameters, networkId }) => {
2670
- const confirmedSlots = new Set(confirmed?.grants.map(({ grant }) => grant.slot));
2671
- const inFlightSlots = new Set(inFlight.grants.map(({ grant }) => grant.slot));
2672
- const kept = inFlight.grants.map((grantUtxo) => ({
2673
- ...grantUtxo,
2674
- status: confirmed && confirmedSlots.has(grantUtxo.grant.slot) ? confirmedGrantStatus(grantUtxo.grant, {
2675
- confirmed: confirmed.state,
2676
- inFlight: inFlight.state,
2677
- now,
2678
- protocolParameters,
2679
- networkId
2680
- }) : "pending"
2681
- }));
2682
- const swept = (confirmed?.grants ?? []).filter(({ grant }) => !inFlightSlots.has(grant.slot)).map((grantUtxo) => ({
2683
- ...grantUtxo,
2684
- status: "sweeping"
2685
- }));
2686
- return [...kept, ...swept].sort(bySlot);
2687
- };
2688
- //#endregion
2689
- Object.defineProperty(exports, "CARDANO_CUSTODY_DEFAULT_GRANT_FEE_BOUND", {
2690
- enumerable: true,
2691
- get: function() {
2692
- return CARDANO_CUSTODY_DEFAULT_GRANT_FEE_BOUND;
2693
- }
2694
- });
2695
- Object.defineProperty(exports, "CUSTODY_ACCOUNT_RECORD_TYPE", {
2696
- enumerable: true,
2697
- get: function() {
2698
- return CUSTODY_ACCOUNT_RECORD_TYPE;
2699
- }
2700
- });
2701
- Object.defineProperty(exports, "canonicalCustodyAccountRecord", {
2702
- enumerable: true,
2703
- get: function() {
2704
- return canonicalCustodyAccountRecord;
2705
- }
2706
- });
2707
- Object.defineProperty(exports, "classifyCardanoCustodyAccount", {
2708
- enumerable: true,
2709
- get: function() {
2710
- return classifyCardanoCustodyAccount;
2711
- }
2712
- });
2713
- Object.defineProperty(exports, "classifyCardanoCustodyGrants", {
2714
- enumerable: true,
2715
- get: function() {
2716
- return classifyCardanoCustodyGrants;
2717
- }
2718
- });
2719
- Object.defineProperty(exports, "classifyCustodyAccountUtxos", {
2720
- enumerable: true,
2721
- get: function() {
2722
- return classifyCustodyAccountUtxos;
2723
- }
2724
- });
2725
- Object.defineProperty(exports, "compareRequestedGrantScope", {
2726
- enumerable: true,
2727
- get: function() {
2728
- return compareRequestedGrantScope;
2729
- }
2730
- });
2731
- Object.defineProperty(exports, "createCustodyAccountRecord", {
2732
- enumerable: true,
2733
- get: function() {
2734
- return createCustodyAccountRecord;
2735
- }
2736
- });
2737
- Object.defineProperty(exports, "custodyDeviceFingerprint", {
2738
- enumerable: true,
2739
- get: function() {
2740
- return custodyDeviceFingerprint;
2741
- }
2742
- });
2743
- Object.defineProperty(exports, "custodyGrantAfterSpend", {
2744
- enumerable: true,
2745
- get: function() {
2746
- return custodyGrantAfterSpend;
2747
- }
2748
- });
2749
- Object.defineProperty(exports, "custodyGrantLiveness", {
2750
- enumerable: true,
2751
- get: function() {
2752
- return custodyGrantLiveness;
2753
- }
2754
- });
2755
- Object.defineProperty(exports, "deriveCustodyDeviceKey", {
2756
- enumerable: true,
2757
- get: function() {
2758
- return deriveCustodyDeviceKey;
2759
- }
2760
- });
2761
- Object.defineProperty(exports, "encodeCustodyAccountRedeemer", {
2762
- enumerable: true,
2763
- get: function() {
2764
- return encodeCustodyAccountRedeemer;
2765
- }
2766
- });
2767
- Object.defineProperty(exports, "encodeCustodyLogicRedeemer", {
2768
- enumerable: true,
2769
- get: function() {
2770
- return encodeCustodyLogicRedeemer;
2771
- }
2772
- });
2773
- Object.defineProperty(exports, "encodeCustodyMintRedeemer", {
2774
- enumerable: true,
2775
- get: function() {
2776
- return encodeCustodyMintRedeemer;
2777
- }
2778
- });
2779
- Object.defineProperty(exports, "encodeCustodyStakeRedeemer", {
2780
- enumerable: true,
2781
- get: function() {
2782
- return encodeCustodyStakeRedeemer;
2783
- }
2784
- });
2785
- Object.defineProperty(exports, "findCustodyScopeViolations", {
2786
- enumerable: true,
2787
- get: function() {
2788
- return findCustodyScopeViolations;
2789
- }
2790
- });
2791
- Object.defineProperty(exports, "inspectCustodyTransaction", {
2792
- enumerable: true,
2793
- get: function() {
2794
- return inspectCustodyTransaction;
2795
- }
2796
- });
2797
- Object.defineProperty(exports, "isCardanoCustodyGrantExhausted", {
2798
- enumerable: true,
2799
- get: function() {
2800
- return isCardanoCustodyGrantExhausted;
2801
- }
2802
- });
2803
- Object.defineProperty(exports, "parseCustodyAccountRecord", {
2804
- enumerable: true,
2805
- get: function() {
2806
- return parseCustodyAccountRecord;
2807
- }
2808
- });
2809
- Object.defineProperty(exports, "parseCustodyRequestedGrantScope", {
2810
- enumerable: true,
2811
- get: function() {
2812
- return parseCustodyRequestedGrantScope;
2813
- }
2814
- });
2815
- Object.defineProperty(exports, "serializeCustodyRequestedGrantScope", {
2816
- enumerable: true,
2817
- get: function() {
2818
- return serializeCustodyRequestedGrantScope;
2819
- }
2820
- });