@helios-lang/effect 0.1.1 → 0.1.3

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 (57) hide show
  1. package/dist/Ledger/Address.js +156 -0
  2. package/dist/Ledger/Address.js.map +1 -0
  3. package/dist/Ledger/AssetClass.js +74 -0
  4. package/dist/Ledger/AssetClass.js.map +1 -0
  5. package/dist/Ledger/Assets.js +122 -0
  6. package/dist/Ledger/Assets.js.map +1 -0
  7. package/dist/Ledger/Credential.js +17 -0
  8. package/dist/Ledger/Credential.js.map +1 -0
  9. package/dist/Ledger/DatumHash.js +21 -0
  10. package/dist/Ledger/DatumHash.js.map +1 -0
  11. package/dist/Ledger/MintingPolicy.js +45 -0
  12. package/dist/Ledger/MintingPolicy.js.map +1 -0
  13. package/dist/Ledger/PubKeyHash.js +21 -0
  14. package/dist/Ledger/PubKeyHash.js.map +1 -0
  15. package/dist/Ledger/TxId.js +15 -0
  16. package/dist/Ledger/TxId.js.map +1 -0
  17. package/dist/Ledger/TxInput.js +51 -0
  18. package/dist/Ledger/TxInput.js.map +1 -0
  19. package/dist/Ledger/TxOutput.js +118 -0
  20. package/dist/Ledger/TxOutput.js.map +1 -0
  21. package/dist/Ledger/TxOutputDatum.js +41 -0
  22. package/dist/Ledger/TxOutputDatum.js.map +1 -0
  23. package/dist/Ledger/TxOutputId.js +39 -0
  24. package/dist/Ledger/TxOutputId.js.map +1 -0
  25. package/dist/Ledger/ValidatorHash.js +21 -0
  26. package/dist/Ledger/ValidatorHash.js.map +1 -0
  27. package/dist/Ledger/index.js +14 -0
  28. package/dist/Ledger/index.js.map +1 -0
  29. package/dist/Uplc/Data.js +229 -21
  30. package/dist/Uplc/Data.js.map +1 -1
  31. package/dist/Uplc/index.js +0 -1
  32. package/dist/Uplc/index.js.map +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/package.json +14 -10
  36. package/src/Ledger/Address.ts +252 -0
  37. package/src/Ledger/AssetClass.ts +96 -0
  38. package/src/Ledger/Assets.ts +164 -0
  39. package/src/Ledger/Credential.ts +29 -0
  40. package/src/Ledger/DatumHash.ts +36 -0
  41. package/src/Ledger/MintingPolicy.ts +57 -0
  42. package/src/Ledger/PubKeyHash.ts +36 -0
  43. package/src/Ledger/TxId.ts +31 -0
  44. package/src/Ledger/TxInput.test.ts +21 -0
  45. package/src/Ledger/TxInput.ts +66 -0
  46. package/src/Ledger/TxOutput.ts +165 -0
  47. package/src/Ledger/TxOutputDatum.ts +64 -0
  48. package/src/Ledger/TxOutputId.ts +63 -0
  49. package/src/Ledger/ValidatorHash.ts +36 -0
  50. package/src/Ledger/index.ts +13 -0
  51. package/src/Uplc/Data.test.ts +321 -0
  52. package/src/Uplc/Data.ts +445 -47
  53. package/src/Uplc/index.ts +0 -1
  54. package/src/index.ts +1 -1
  55. package/src/Address.ts +0 -20
  56. package/src/Uplc/DataSchema.test.ts +0 -207
  57. package/src/Uplc/DataSchema.ts +0 -181
package/package.json CHANGED
@@ -1,21 +1,17 @@
1
1
  {
2
2
  "name": "@helios-lang/effect",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "types/index.d.ts",
7
- "scripts": {
8
- "build": "tsc -p ./tsconfig.build.json",
9
- "lint": "eslint",
10
- "prettify": "prettier . --write",
11
- "test": "pnpm run test:types && pnpm run lint && pnpm run test:suite",
12
- "test:suite": "bun test .",
13
- "test:types": "tsc -p ./tsconfig.json"
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./Ledger": "./dist/Ledger/index.js",
10
+ "./Uplc": "./dist/Uplc/index.js"
14
11
  },
15
12
  "keywords": [],
16
13
  "author": "Christian Schmitz",
17
14
  "license": "BSD-3-Clause",
18
- "packageManager": "pnpm@10.20.0",
19
15
  "prettier": {
20
16
  "trailingComma": "none",
21
17
  "tabWidth": 2,
@@ -32,5 +28,13 @@
32
28
  },
33
29
  "dependencies": {
34
30
  "effect": "^3.19.12"
31
+ },
32
+ "scripts": {
33
+ "build": "tsc -p ./tsconfig.build.json",
34
+ "lint": "eslint",
35
+ "prettify": "prettier . --write",
36
+ "test": "pnpm run test:types && pnpm run lint && pnpm run test:suite",
37
+ "test:suite": "bun test .",
38
+ "test:types": "tsc -p ./tsconfig.json"
35
39
  }
36
- }
40
+ }
@@ -0,0 +1,252 @@
1
+ import { Context, Effect, Option, ParseResult, Schema } from "effect"
2
+ import * as Bech32 from "../Bech32.js"
3
+ import * as Cbor from "../Cbor.js"
4
+ import * as Bytes from "../internal/Bytes.js"
5
+ import { Data } from "../Uplc/index.js"
6
+ import * as PubKeyHash from "./PubKeyHash.js"
7
+ import * as Credential from "./Credential.js"
8
+ import * as ValidatorHash from "./ValidatorHash.js"
9
+
10
+ export function isValid(addr: string): boolean {
11
+ if (addr.startsWith("addr1") || addr.startsWith("addr_test1")) {
12
+ return Bech32.isValid(addr) // TODO: full validation
13
+ }
14
+ // TODO: validate Byron format
15
+
16
+ return false
17
+ }
18
+
19
+ export class IsMainnet extends Context.Tag("IS_MAINNET")<
20
+ IsMainnet,
21
+ boolean
22
+ >() {}
23
+
24
+ export const Address = Schema.String.pipe(
25
+ Schema.filter((addr: string) => {
26
+ return isValid(addr) || "Invalid Cardano Address"
27
+ }),
28
+ Schema.brand("Address")
29
+ )
30
+
31
+ export type Address = Schema.Schema.Type<typeof Address>
32
+
33
+ export const FromUplcData = Schema.transformOrFail(
34
+ Data.EnumVariant(0, {
35
+ spendingCredential: Credential.FromUplcData,
36
+ stakingCredential: Data.Option(Credential.FromUplcData)
37
+ }),
38
+ Address,
39
+ {
40
+ strict: true,
41
+ decode: (data) =>
42
+ Effect.gen(function* () {
43
+ const isMainnet = yield* IsMainnet
44
+
45
+ return make(
46
+ isMainnet,
47
+ data.spendingCredential,
48
+ data.stakingCredential._tag == "Some"
49
+ ? data.stakingCredential.value
50
+ : undefined
51
+ )
52
+ }),
53
+ encode: (address) => {
54
+ const { spendingCredential, stakingCredential } = Effect.runSync(
55
+ decodeInternal(address)
56
+ )
57
+
58
+ return ParseResult.succeed({
59
+ spendingCredential,
60
+ stakingCredential: stakingCredential
61
+ ? Option.some(stakingCredential)
62
+ : Option.none()
63
+ })
64
+ }
65
+ }
66
+ )
67
+
68
+ export function make(
69
+ isMainnet: boolean,
70
+ spendingCredential: Credential.Credential,
71
+ stakingCredential?: Credential.Credential
72
+ ): Address {
73
+ const prefix = isMainnet ? "addr" : "addr_test"
74
+ const bytes: number[] = toShelleyBytes(
75
+ isMainnet,
76
+ spendingCredential,
77
+ stakingCredential
78
+ )
79
+
80
+ const s: string = Bech32.encode(prefix, bytes)
81
+
82
+ return s as Address
83
+ }
84
+
85
+ // returns the byte representation of an Address
86
+ function toShelleyBytes(
87
+ isMainnet: boolean,
88
+ spendingCredential: Credential.Credential,
89
+ stakingCredential?: Credential.Credential
90
+ ): number[] {
91
+ const spendingCredBytes = Bytes.toArray(spendingCredential.hash)
92
+
93
+ if (stakingCredential) {
94
+ const stakingCredBytes = Bytes.toArray(stakingCredential.hash)
95
+
96
+ if (spendingCredential._tag == "PubKey") {
97
+ if (stakingCredential._tag == "PubKey") {
98
+ return [isMainnet ? 0x01 : 0x00]
99
+ .concat(spendingCredBytes)
100
+ .concat(stakingCredBytes)
101
+ } else {
102
+ return [isMainnet ? 0x21 : 0x20]
103
+ .concat(spendingCredBytes)
104
+ .concat(stakingCredBytes)
105
+ }
106
+ } else {
107
+ if (stakingCredential._tag == "PubKey") {
108
+ return [isMainnet ? 0x11 : 0x10]
109
+ .concat(spendingCredBytes)
110
+ .concat(stakingCredBytes)
111
+ } else {
112
+ return [isMainnet ? 0x31 : 0x30]
113
+ .concat(spendingCredBytes)
114
+ .concat(stakingCredBytes)
115
+ }
116
+ }
117
+ } else if (spendingCredential._tag == "PubKey") {
118
+ return [isMainnet ? 0x61 : 0x60].concat(spendingCredBytes)
119
+ } else {
120
+ return [isMainnet ? 0x71 : 0x70].concat(spendingCredBytes)
121
+ }
122
+ }
123
+
124
+ const decodeInternal = (bytes: Bytes.BytesLike) =>
125
+ Effect.gen(function* () {
126
+ if (typeof bytes == "string" && bytes.startsWith("addr")) {
127
+ bytes = (yield* Bech32.decode(bytes)).bytes
128
+ }
129
+
130
+ const innerBytes = (yield* Cbor.isBytes(bytes))
131
+ ? yield* Cbor.decodeBytes(bytes)
132
+ : Bytes.toArray(bytes)
133
+
134
+ const head = innerBytes[0]
135
+
136
+ const isMainnet = (head & 0b00001111) != 0
137
+
138
+ const type = head & 0b11110000
139
+
140
+ const firstPart = () => {
141
+ return innerBytes.slice(1, 29)
142
+ }
143
+
144
+ const secondPart = () => {
145
+ return innerBytes.slice(29, 57)
146
+ }
147
+
148
+ switch (type) {
149
+ case 0x00:
150
+ return {
151
+ isMainnet,
152
+ spendingCredential: Credential.makePubKey(
153
+ yield* PubKeyHash.make(firstPart())
154
+ ),
155
+ stakingCredential: Credential.makePubKey(
156
+ yield* PubKeyHash.make(secondPart())
157
+ )
158
+ }
159
+ case 0x10:
160
+ return {
161
+ isMainnet,
162
+ spendingCredential: Credential.makeValidator(
163
+ yield* ValidatorHash.make(firstPart())
164
+ ),
165
+ stakingCredential: Credential.makePubKey(
166
+ yield* PubKeyHash.make(secondPart())
167
+ )
168
+ }
169
+ case 0x20:
170
+ return {
171
+ isMainnet,
172
+ spendingCredential: Credential.makePubKey(
173
+ yield* PubKeyHash.make(firstPart())
174
+ ),
175
+ stakingCredential: Credential.makeValidator(
176
+ yield* ValidatorHash.make(secondPart())
177
+ )
178
+ }
179
+ case 0x30:
180
+ return {
181
+ isMainnet,
182
+ spendingCredential: Credential.makeValidator(
183
+ yield* ValidatorHash.make(firstPart())
184
+ ),
185
+ stakingCredential: Credential.makeValidator(
186
+ yield* ValidatorHash.make(secondPart())
187
+ )
188
+ }
189
+ case 0x60:
190
+ return {
191
+ isMainnet,
192
+ spendingCredential: Credential.makePubKey(
193
+ yield* PubKeyHash.make(firstPart())
194
+ )
195
+ }
196
+ case 0x70:
197
+ return {
198
+ isMainnet,
199
+ spendingCredential: Credential.makeValidator(
200
+ yield* ValidatorHash.make(firstPart())
201
+ )
202
+ }
203
+ default:
204
+ return yield* Effect.fail(
205
+ new Cbor.DecodeError(
206
+ Bytes.makeStream(bytes),
207
+ `invalid Shelley Address header ${head}`
208
+ )
209
+ )
210
+ }
211
+ })
212
+
213
+ export const decode = (bytes: Bytes.BytesLike): Cbor.DecodeEffect<Address> =>
214
+ decodeInternal(bytes).pipe(
215
+ Effect.map(({ isMainnet, spendingCredential, stakingCredential }) =>
216
+ make(isMainnet, spendingCredential, stakingCredential)
217
+ ),
218
+ Effect.catchTag(
219
+ "ParseError",
220
+ (e) => new Cbor.DecodeError(Bytes.makeStream(bytes), e.message)
221
+ ),
222
+ Effect.catchTag(
223
+ "DecodeException",
224
+ (e) =>
225
+ new Cbor.DecodeError(
226
+ Bytes.makeStream(bytes),
227
+ `bech32 decoding failed (${e.message})`
228
+ )
229
+ )
230
+ )
231
+
232
+ export function encode(address: Address): number[] {
233
+ return Cbor.encodeBytes(bytes(address))
234
+ }
235
+
236
+ export function bytes(address: Address): number[] {
237
+ return Effect.runSync(Bech32.decode(address)).bytes
238
+ }
239
+
240
+ export function isMainnet(address: Address): boolean {
241
+ return !address.startsWith("addr_test")
242
+ }
243
+
244
+ export function spendingCredential(address: Address): Credential.Credential {
245
+ return Effect.runSync(decodeInternal(address)).spendingCredential
246
+ }
247
+
248
+ export function stakingCredential(
249
+ address: Address
250
+ ): Credential.Credential | undefined {
251
+ return Effect.runSync(decodeInternal(address)).stakingCredential
252
+ }
@@ -0,0 +1,96 @@
1
+ import { Encoding, Option, Schema } from "effect"
2
+ //import * as Bech32 from "../Bech32.js"
3
+ import * as Bytes from "../internal/Bytes.js"
4
+ import { Data } from "../Uplc"
5
+ import * as MintingPolicy from "./MintingPolicy.js"
6
+ import * as ValidatorHash from "./ValidatorHash.js"
7
+
8
+ export function isValid(assetClass: string): assetClass is AssetClass {
9
+ const n = assetClass.length
10
+ return (
11
+ /^[0-9a-fA-F]+$/.test(assetClass) &&
12
+ n < 120 &&
13
+ (n == 0 || (n >= 56 && n % 2 == 0))
14
+ )
15
+ }
16
+
17
+ export const AssetClass = Schema.transform(
18
+ Schema.String,
19
+ Schema.String.pipe(
20
+ Schema.filter((ac: string) => isValid(ac) || "Invalid Cardano AssetClass"),
21
+ Schema.brand("AssetClass")
22
+ ),
23
+ {
24
+ strict: false,
25
+ decode: (s) => s.split(".").join(""),
26
+ encode: (s) => s
27
+ }
28
+ )
29
+
30
+ export type AssetClass = Schema.Schema.Type<typeof AssetClass>
31
+
32
+ export const ADA = "" as AssetClass
33
+
34
+ export function make(
35
+ policy: MintingPolicy.MintingPolicy,
36
+ tokenName: Bytes.BytesLike
37
+ ) {
38
+ if (policy._tag == "None") {
39
+ return ADA
40
+ } else {
41
+ return policy.value + Encoding.encodeHex(Bytes.toUint8Array(tokenName))
42
+ }
43
+ }
44
+
45
+ export const FromUplcData = Schema.transform(
46
+ Data.EnumVariant(0, {
47
+ policy: MintingPolicy.FromUplcData,
48
+ tokenName: Data.ByteArray
49
+ }),
50
+ AssetClass,
51
+ {
52
+ strict: true,
53
+ decode: ({ policy, tokenName }) => {
54
+ if (policy._tag == "None") {
55
+ return ADA
56
+ } else {
57
+ return policy.value + Encoding.encodeHex(tokenName)
58
+ }
59
+ },
60
+ encode: (assetClass) => {
61
+ return {
62
+ policy: policy(assetClass),
63
+ tokenName: Bytes.toUint8Array(tokenName(assetClass))
64
+ }
65
+ }
66
+ }
67
+ )
68
+
69
+ export function fingerprint(assetClass: string): string {
70
+ throw new Error("not yet implemented (need blake2b hashing function)")
71
+ //return Bech32.encode("asset", )
72
+ }
73
+
74
+ export function pretty(assetClass: string): string {
75
+ if (assetClass.length == 0) {
76
+ return "."
77
+ } else {
78
+ return assetClass.slice(0, 56) + "." + assetClass.slice(56)
79
+ }
80
+ }
81
+
82
+ export function policy(assetClass: string): MintingPolicy.MintingPolicy {
83
+ if (assetClass.length == 0) {
84
+ return Option.none()
85
+ } else {
86
+ return Option.some(assetClass.slice(0, 56) as ValidatorHash.ValidatorHash)
87
+ }
88
+ }
89
+
90
+ export function tokenName(assetClass: string): string {
91
+ if (assetClass.length == 0) {
92
+ return ""
93
+ } else {
94
+ return assetClass.slice(56)
95
+ }
96
+ }
@@ -0,0 +1,164 @@
1
+ import { Effect, Schema } from "effect"
2
+ import * as Bytes from "../internal/Bytes.js"
3
+ import * as Cbor from "../Cbor.js"
4
+ import { Data } from "../Uplc"
5
+ import * as AssetClass from "./AssetClass.js"
6
+ import * as MintingPolicy from "./MintingPolicy.js"
7
+
8
+ export const Assets = Schema.Record({
9
+ key: Schema.String, // can sadly not use AssetClass.AssetClass here
10
+ value: Schema.BigIntFromSelf
11
+ }).pipe(
12
+ Schema.filter((assets) => {
13
+ for (let key in assets) {
14
+ if (!AssetClass.isValid(key)) {
15
+ return `Invalid AssetClass ${key}`
16
+ }
17
+ }
18
+
19
+ return true
20
+ })
21
+ )
22
+
23
+ export type Assets = Schema.Schema.Type<typeof Assets>
24
+
25
+ export const FromUplcData = Schema.transform(
26
+ Data.PairArray(
27
+ MintingPolicy.FromUplcData,
28
+ Data.PairArray(Data.Hex, Data.BigInt)
29
+ ),
30
+ Assets,
31
+ {
32
+ strict: true,
33
+ decode: (outer) => {
34
+ let assets: Record<string, bigint> = {}
35
+
36
+ for (let [policy, inner] of outer) {
37
+ for (let [tokenName, quantity] of inner) {
38
+ assets[AssetClass.make(policy, tokenName)] = quantity
39
+ }
40
+ }
41
+
42
+ return assets as Assets
43
+ },
44
+ encode: (assets) => {
45
+ const outer = nestedRecords(assets)
46
+
47
+ return Object.entries(outer).map(
48
+ ([policy, inner]) =>
49
+ [
50
+ Effect.runSync(MintingPolicy.make(policy)),
51
+ Object.entries(inner)
52
+ ] as const
53
+ )
54
+ }
55
+ }
56
+ )
57
+
58
+ function nestedRecords(assets: Assets): Record<string, Record<string, bigint>> {
59
+ const outer: Record<string, Record<string, bigint>> = {}
60
+
61
+ Object.entries(assets).forEach(([assetClass, quantity]) => {
62
+ if (assetClass.length == 0) {
63
+ outer[assetClass] = { "": quantity }
64
+ } else {
65
+ const policy = assetClass.slice(0, 56)
66
+ const tokenName = assetClass.slice(56)
67
+
68
+ if (policy in outer) {
69
+ outer[policy] = {
70
+ ...outer[policy],
71
+ [tokenName]: quantity
72
+ }
73
+ } else {
74
+ outer[policy] = {
75
+ [tokenName]: quantity
76
+ }
77
+ }
78
+ }
79
+ })
80
+
81
+ return outer
82
+ }
83
+
84
+ export const decode = (bytes: Bytes.BytesLike): Cbor.DecodeEffect<Assets> =>
85
+ Effect.gen(function* () {
86
+ const stream = Bytes.makeStream(bytes)
87
+
88
+ if (yield* Cbor.isTuple(bytes)) {
89
+ const [lovelace, otherAssets] = yield* Cbor.decodeTuple([
90
+ Cbor.decodeInt,
91
+ Cbor.decodeMap(
92
+ MintingPolicy.decode,
93
+ Cbor.decodeMap(Cbor.decodeBytes, Cbor.decodeInt)
94
+ )
95
+ ])(stream)
96
+
97
+ const assets: Record<string, bigint> = {}
98
+
99
+ if (lovelace != 0n) {
100
+ assets[AssetClass.ADA] = lovelace
101
+ }
102
+
103
+ for (let [policy, inner] of otherAssets) {
104
+ if (policy._tag == "None") {
105
+ return yield* new Cbor.DecodeError(
106
+ stream,
107
+ "unexpected ADA assetclass in encoded non-ADA assets"
108
+ )
109
+ }
110
+
111
+ for (let [tokenName, quantity] of inner) {
112
+ const assetClass = AssetClass.make(policy, tokenName)
113
+
114
+ assets[assetClass] = quantity
115
+ }
116
+ }
117
+
118
+ return assets as Assets
119
+ } else {
120
+ return { [AssetClass.ADA]: yield* Cbor.decodeInt(stream) }
121
+ }
122
+ })
123
+
124
+ export function encode(assets: Assets): number[] {
125
+ const acs = nonAdaAssetClasses(assets)
126
+
127
+ if (acs.length == 0) {
128
+ return Cbor.encodeInt(lovelace(assets))
129
+ } else {
130
+ const obj = nestedRecords(assets)
131
+ if (AssetClass.ADA in obj) {
132
+ delete obj[AssetClass.ADA]
133
+ }
134
+
135
+ return Cbor.encodeTuple([
136
+ Cbor.encodeInt(lovelace(assets)),
137
+ Cbor.encodeMap(
138
+ Object.entries(obj).map(([mph, tokens]) => {
139
+ return [
140
+ Cbor.encodeBytes(mph),
141
+ Cbor.encodeMap(
142
+ Object.entries(tokens).map(([tokenName, qty]) => [
143
+ Cbor.encodeBytes(tokenName),
144
+ Cbor.encodeInt(qty)
145
+ ])
146
+ )
147
+ ]
148
+ })
149
+ )
150
+ ])
151
+ }
152
+ }
153
+
154
+ export function allAssetClasses(assets: Assets): AssetClass.AssetClass[] {
155
+ return Object.keys(assets) as AssetClass.AssetClass[]
156
+ }
157
+
158
+ export function nonAdaAssetClasses(assets: Assets): AssetClass.AssetClass[] {
159
+ return allAssetClasses(assets).filter((ac) => ac != AssetClass.ADA)
160
+ }
161
+
162
+ export function lovelace(assets: Assets): bigint {
163
+ return assets[AssetClass.ADA] ?? 0n
164
+ }
@@ -0,0 +1,29 @@
1
+ import { Schema } from "effect"
2
+ import { Data } from "../Uplc"
3
+ import { PubKeyHash } from "./PubKeyHash.js"
4
+ import { ValidatorHash } from "./ValidatorHash.js"
5
+
6
+ export const Credential = Schema.Union(
7
+ Schema.TaggedStruct("PubKey", { hash: PubKeyHash }),
8
+ Schema.TaggedStruct("Validator", { hash: ValidatorHash })
9
+ )
10
+
11
+ export type Credential = Schema.Schema.Type<typeof Credential>
12
+
13
+ export function makePubKey(pkh: PubKeyHash): Credential {
14
+ return { _tag: "PubKey", hash: pkh }
15
+ }
16
+
17
+ export function makeValidator(vh: ValidatorHash): Credential {
18
+ return { _tag: "Validator", hash: vh }
19
+ }
20
+
21
+ export const FromUplcData = Schema.transform(
22
+ Data.Enum({ PubKey: { hash: Data.Hex }, Validator: { hash: Data.Hex } }),
23
+ Credential,
24
+ {
25
+ strict: true,
26
+ decode: (cred) => cred,
27
+ encode: (cred) => cred
28
+ }
29
+ )
@@ -0,0 +1,36 @@
1
+ import { Effect, Encoding, Schema } from "effect"
2
+ import * as Bytes from "../internal/Bytes.js"
3
+ import { decodeBytes, DecodeEffect, encodeBytes } from "../Cbor.js"
4
+ import { Data } from "../Uplc"
5
+
6
+ export function isValid(dh: string): boolean {
7
+ return /^[0-9a-fA-F]+$/.test(dh) && dh.length == 64
8
+ }
9
+
10
+ export const DatumHash = Schema.String.pipe(
11
+ Schema.filter((dh: string) => isValid(dh) || "Invalid Cardano DatumHash"),
12
+ Schema.brand("DatumHash")
13
+ )
14
+
15
+ export type DatumHash = Schema.Schema.Type<typeof DatumHash>
16
+
17
+ export function make(bytes: Bytes.BytesLike) {
18
+ return Schema.decode(DatumHash)(Bytes.toHex(bytes))
19
+ }
20
+
21
+ export const FromUplcData = Schema.transform(Data.ByteArray, DatumHash, {
22
+ strict: true,
23
+ decode: Encoding.encodeHex,
24
+ encode: Bytes.toUint8Array
25
+ })
26
+
27
+ export const decode = (bytes: Bytes.BytesLike): DecodeEffect<DatumHash> =>
28
+ decodeBytes(bytes).pipe(
29
+ Effect.map((bytes) => new Uint8Array(bytes)),
30
+ Effect.map(Encoding.encodeHex),
31
+ Effect.map(Schema.decodeSync(DatumHash))
32
+ )
33
+
34
+ export function encode(dh: DatumHash): number[] {
35
+ return encodeBytes(dh)
36
+ }
@@ -0,0 +1,57 @@
1
+ import { Effect, Encoding, Option, Schema } from "effect"
2
+ import * as Bytes from "../internal/Bytes.js"
3
+ import * as Cbor from "../Cbor.js"
4
+ import { Data } from "../Uplc"
5
+ import * as ValidatorHash from "./ValidatorHash.js"
6
+
7
+ // None is used for ADA
8
+ export const MintingPolicy = Schema.Option(ValidatorHash.ValidatorHash)
9
+
10
+ export type MintingPolicy = Schema.Schema.Type<typeof MintingPolicy>
11
+
12
+ export const FromUplcData = Schema.transform(Data.ByteArray, MintingPolicy, {
13
+ strict: true,
14
+ decode: (bs) => {
15
+ if (bs.length == 0) {
16
+ return Option.none()
17
+ } else {
18
+ return Option.some(Encoding.encodeHex(bs))
19
+ }
20
+ },
21
+ encode: (opt) => {
22
+ if (opt._tag == "None") {
23
+ return new Uint8Array()
24
+ } else {
25
+ return Effect.runSync(Encoding.decodeHex(opt.value))
26
+ }
27
+ }
28
+ })
29
+
30
+ export function make(policy: Bytes.BytesLike) {
31
+ const p = Bytes.toHex(policy)
32
+
33
+ if (p.length == 0) {
34
+ return Effect.succeed(Option.none())
35
+ } else {
36
+ return ValidatorHash.make(p).pipe(Effect.map(Option.some))
37
+ }
38
+ }
39
+
40
+ export const decode = (
41
+ bytes: Bytes.BytesLike
42
+ ): Cbor.DecodeEffect<MintingPolicy> =>
43
+ Cbor.decodeBytes(bytes).pipe(
44
+ Effect.flatMap(make),
45
+ Effect.catchTag(
46
+ "ParseError",
47
+ (e) => new Cbor.DecodeError(Bytes.makeStream(bytes), e.message)
48
+ )
49
+ )
50
+
51
+ export function encode(policy: MintingPolicy): number[] {
52
+ if (policy._tag == "None") {
53
+ return Cbor.encodeBytes([])
54
+ } else {
55
+ return Cbor.encodeBytes(policy.value)
56
+ }
57
+ }