@helios-lang/effect 0.1.2 → 0.1.4
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.
- package/dist/Ledger/Address.js +14 -12
- package/dist/Ledger/Address.js.map +1 -1
- package/dist/Ledger/AssetClass.js +69 -0
- package/dist/Ledger/AssetClass.js.map +1 -0
- package/dist/Ledger/Assets.js +120 -0
- package/dist/Ledger/Assets.js.map +1 -0
- package/dist/Ledger/Credential.js +1 -1
- package/dist/Ledger/Credential.js.map +1 -1
- package/dist/Ledger/DatumHash.js +21 -0
- package/dist/Ledger/DatumHash.js.map +1 -0
- package/dist/Ledger/IsMainnet.js +4 -0
- package/dist/Ledger/IsMainnet.js.map +1 -0
- package/dist/Ledger/MintingPolicy.js +45 -0
- package/dist/Ledger/MintingPolicy.js.map +1 -0
- package/dist/Ledger/PubKeyHash.js +2 -2
- package/dist/Ledger/PubKeyHash.js.map +1 -1
- package/dist/Ledger/TxId.js +15 -0
- package/dist/Ledger/TxId.js.map +1 -0
- package/dist/Ledger/TxInput.js +51 -0
- package/dist/Ledger/TxInput.js.map +1 -0
- package/dist/Ledger/TxOutput.js +119 -0
- package/dist/Ledger/TxOutput.js.map +1 -0
- package/dist/Ledger/TxOutputDatum.js +41 -0
- package/dist/Ledger/TxOutputDatum.js.map +1 -0
- package/dist/Ledger/TxOutputId.js +39 -0
- package/dist/Ledger/TxOutputId.js.map +1 -0
- package/dist/Ledger/ValidatorHash.js +2 -2
- package/dist/Ledger/ValidatorHash.js.map +1 -1
- package/dist/Ledger/index.js +10 -0
- package/dist/Ledger/index.js.map +1 -1
- package/dist/Uplc/Data.js +24 -4
- package/dist/Uplc/Data.js.map +1 -1
- package/package.json +2 -1
- package/src/Ledger/Address.ts +27 -17
- package/src/Ledger/AssetClass.ts +90 -0
- package/src/Ledger/Assets.ts +164 -0
- package/src/Ledger/Credential.ts +1 -1
- package/src/Ledger/DatumHash.ts +36 -0
- package/src/Ledger/IsMainnet.ts +6 -0
- package/src/Ledger/MintingPolicy.ts +57 -0
- package/src/Ledger/PubKeyHash.ts +2 -2
- package/src/Ledger/TxId.ts +31 -0
- package/src/Ledger/TxInput.test.ts +21 -0
- package/src/Ledger/TxInput.ts +66 -0
- package/src/Ledger/TxOutput.ts +166 -0
- package/src/Ledger/TxOutputDatum.ts +64 -0
- package/src/Ledger/TxOutputId.ts +63 -0
- package/src/Ledger/ValidatorHash.ts +1 -1
- package/src/Ledger/index.ts +10 -0
- package/src/Uplc/Data.ts +28 -1
|
@@ -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 (const 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
|
+
const assets: Record<string, bigint> = {}
|
|
35
|
+
|
|
36
|
+
for (const [policy, inner] of outer) {
|
|
37
|
+
for (const [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 (const [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 (const [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
|
+
}
|
package/src/Ledger/Credential.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect"
|
|
2
|
+
import { Data } from "../Uplc"
|
|
2
3
|
import { PubKeyHash } from "./PubKeyHash.js"
|
|
3
4
|
import { ValidatorHash } from "./ValidatorHash.js"
|
|
4
|
-
import { Data } from "../Uplc/index.js"
|
|
5
5
|
|
|
6
6
|
export const Credential = Schema.Union(
|
|
7
7
|
Schema.TaggedStruct("PubKey", { hash: PubKeyHash }),
|
|
@@ -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
|
+
}
|
package/src/Ledger/PubKeyHash.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Effect, Encoding, Schema } from "effect"
|
|
2
2
|
import * as Bytes from "../internal/Bytes.js"
|
|
3
3
|
import { decodeBytes, DecodeEffect, encodeBytes } from "../Cbor.js"
|
|
4
|
-
import { Data } from "../Uplc
|
|
4
|
+
import { Data } from "../Uplc"
|
|
5
5
|
|
|
6
|
-
export function isValid(pkh: string):
|
|
6
|
+
export function isValid(pkh: string): pkh is PubKeyHash {
|
|
7
7
|
return /^[0-9a-fA-F]+$/.test(pkh) && pkh.length == 56
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect"
|
|
2
|
+
import * as Bytes from "../internal/Bytes.js"
|
|
3
|
+
import * as Cbor from "../Cbor.js"
|
|
4
|
+
|
|
5
|
+
export function isValid(txId: string): txId is TxId {
|
|
6
|
+
return txId.length == 64 && /^[0-9a-fA-F]+$/.test(txId)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const TxId = Schema.String.pipe(
|
|
10
|
+
Schema.filter((id: string) => isValid(id) || "Invalid Cardano TxId"),
|
|
11
|
+
Schema.brand("TxId")
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export type TxId = Schema.Schema.Type<typeof TxId>
|
|
15
|
+
|
|
16
|
+
export function make(txId: Bytes.BytesLike) {
|
|
17
|
+
return Schema.decode(TxId)(Bytes.toHex(txId))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const decode = (bytes: Bytes.BytesLike): Cbor.DecodeEffect<TxId> =>
|
|
21
|
+
Cbor.decodeBytes(bytes).pipe(
|
|
22
|
+
Effect.flatMap(make),
|
|
23
|
+
Effect.catchTag(
|
|
24
|
+
"ParseError",
|
|
25
|
+
(e) => new Cbor.DecodeError(Bytes.makeStream(bytes), e.message)
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
export function encode(txId: TxId): number[] {
|
|
30
|
+
return Cbor.encodeBytes(txId)
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it } from "bun:test"
|
|
2
|
+
import { Effect } from "effect"
|
|
3
|
+
import { decode } from "./TxInput.js"
|
|
4
|
+
|
|
5
|
+
describe("TxInput.decode()", () => {
|
|
6
|
+
it("works for UTxO returned from Cip30 wallet", () => {
|
|
7
|
+
Effect.runSync(
|
|
8
|
+
decode(
|
|
9
|
+
"828258204cb4e9f79554fb3b572b19f68c8cce0dba929fcee2f6ab6cc390419a8d703bd8181882581d604988cad9aa1ebd733b165695cfef965fda2ee42dab2d8584c43b039c1a49da0141"
|
|
10
|
+
)
|
|
11
|
+
)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("works for UTXO with V3 ref script", () => {
|
|
15
|
+
Effect.runSync(
|
|
16
|
+
decode(
|
|
17
|
+
"828258203d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e4600a4011a01393d92028201d8185853d8799f581c3babcffc6102ec25ced40e1a24fba20371925c46f0299b2b9456360ed8799f581cdb0d968cda2cc636b28c0f377e66691a065b8004e57be5129aeef8224461757468ff1a000f42401832183205ff03d818591174820259116f59116c010100333333323232323232323223222322322322322533300d3232323232533233013300130143754004264664464646464a66603460060022a66603a60386ea80280085854ccc068c02000454ccc074c070dd50050010b0a99980d18020008a99980e980e1baa00a0021616301a37540122646464646464646464a666042601460446ea80304c8c8c8c8c8c8c8c94ccc0a4c048c0a8dd50008991919191919191919191929919981a980f00409919299981d181e80109919191919299981e1813181e9baa001132533303d302b303e375400826601600226464646464a66608a60900042646464646464a666090606c60926ea8c134c138c128dd518268030a999824181918249baa00213253330493037304a37540042a666092606460946ea80044c8c8c8c8c8c8c8c8c8c94ccc158c1640084c8c8c8c8c8c8c8c8c8c8c8c8c94ccc18008454ccc18008c54ccc18000c54ccc18000840045280a5014a02940cdd79ba6041374c666605c666605c666062076022900101d807a40040ba9101044655454c0005432533333306600114a02646464a646660cc0042a6660c6600200a266e25200000314a026644a6660ca66ebcc19800930101400015333065337126eb4c19cc1a8dd59833801002898019ba600114a02a6660ca66e2520000051300300714a060d000460d20046ebcc19cc1a000cdd5801992999830982518311baa0011375a60c660cc6eacc198c18cdd50008a400060ca0026466600200203844660cc004660cc6e980052f5c0444a6660ca00426600298103d87a80004bd6f7b630099199911299983319b9148900003153330663371e91010000313300530403306a374c00497ae00011330054c103d87a8000006133300700700122330070023306c337606ea4014dd30020009bae3064001375660ca00260d200660ce00429405280a50374c666605a666060074020900102e245044655454c00053533305d337120a86466e00c004050c00404894ccc178cdc4000a40002605400220022a6660ba0062a6660ba004200229405280a5032533305d304b305e3754002266e24dd69831182f9baa00100b163037305e3754606060bc6ea80f4cdc780599b8a48810550494c4f54003730600407066e3c030cdc52450453484950003730600206e46e64cc008dd4000a450030010012225333233330620031323232323300a0013371491101280053330620021337149101035b5d2900006133714911035b5f2000333300900233714911025d29000062233300a00a00230070012233300a00a002001533305e337100049007099b80483c80400854ccc178cdc40012410004266e00cdc0241002800490068b1bac305f002375a60ba0026466ec0dd4182e8009ba7305e0013754006264a6660be002266e292201027b7d00003133714911037b5f20003232330010010032253330620011003132533306300113233300a00a3062001337149101023a200033300a00a3063001005306500213233300a00a3062001337149101023a200033300a00a30630013007330040040023065002306500133714910102207d000033756006264a6660be002266e29221025b5d00003133714911035b5f2000333300600133714911015d000032233300700700230040012233300700700200137580066e292201022c2000133005375a00400226466e2922010268270032333001001337006e340092001337149101012700003222533305e3371000490000800899191919980300319b8000548004cdc599b80002533306133710004900a0a40c02903719b8b33700002a6660c266e2000520141481805206e0043370c004901019b8300148080cdc70020011bae0022222323300100100522533305f00110051325333060001133004306200200613300530620023300300300130620012232330010010032253330593042001133714910101300000315333059337100029000099b8a489012d0033002002302500113300533708002900a19b8b3370066e1400520144818000cc0040048894ccc158cdc4801240002002266600600666e1000920143371666e00cdc28012402890300008b1bad30570013057002375c60aa00260aa0046eb8c14c004c14c008dd6982880098288011bad304f001304b37540022c2c609a60946ea80085858c130c134008c12c004c12c008dd59824800982480098221baa0011630460013300d0232325333042303030433754002266e3c078dd7182398221baa00114a0603860866ea8c070c10cdd5000a99981f99baf374c00a6e98cccc038dd5980998209baa30133041375402c9110048810003a100114a066e212000333013004375c603260806ea80ecdd7180918201baa03b3375e602e660826ea0cdc000d24004660826ea40652f5c00022c6082607c6ea800458c10000cdd5981f801981f181f800981f000981e981c9baa00116303b0013300201823375e602260706ea8004c044c0e0dd51805181c1baa00d15333035302300813232533303a303d00213232323232533303c3026303d3754002264a66607a6056607c6ea80104c8c8c8c8c94ccc114c1200084c8c8c8c8c94ccc11cc0c4c120dd50008992999824181b18249baa00413232533304a3033304b3754006264646464646464646464a6660ae60b40042646464a6660ae0342a6660ae0322a6660ae0302a6660ae01e2a6660ae01c2a6660ae0062a6660ae004200229405280a5014a029405280a503375e6e980e0dd31999812999814019003a40020a89101044655454c00302233302b0110544881044655454c00325333056304430573754002266e2400cdd6982d982c1baa00116303030573754606060ae6ea80d8cdd79ba7330583750012660b06ea001d2f5c0981049f0000ff0016375a60b000260b00046eb8c158004c158008dd7182a000982a0011bad30520013052002375a60a000260986ea800c58cdd79ba732323300100102b22533304f00114bd70099199911299982819baf00700313305400233006006001133006006001304e001304f001305300230510013374a900119826981218259baa0084bd701ba73304d4c0103d87b80004bd70191929998269828001099299982599b88371a00400229404cdc7999b8c480000040092201045348495000371a910104534849500016375c609c002646600200264a666094607060966ea800452f5bded8c026eacc13cc130dd50009980f8020129129998268008a5eb804cc138c12cc13c004cc008008c14000458c130c124dd50008b18258019bab304a0033049304a0013049001304830443754602c60886ea800458c118004cc0340908c8c8c94ccc110c0c8c114dd50008a999822181918229baa3049304a00213371e6eb8c124c118dd5000810001801982400098221baa301d30443754602c60886ea80085281919baf374c600200c6e98c004dd5980a18211baa30143042375402e4646600200200444a66608a002297adef6c60132333222533304633722910100003153330463371e9101000031001100513304a337606ea400cdd3001198030030009bae30440013756608a0026092004608e00264646464a666084605660866ea80044c8c8c8cdc499b82375a60940026eb4c128c12c008cdc11bad304a002375a60946096002608c6ea8c8c8c080cc128dd419b82375a60960046eb4c12c004cc128dd419b82375a609660980046eb4c12cc1300052f5c0608e6ea8c8c8c8c8c08ccc134dd419b81337046eb4c138010004cdc11bad304e0020033304d375066e0800c0052f5c06eb4c134c138004c124dd5182618249baa006375a60966098002608e6ea8c00d200230463754600400a608a6ea8c00400c8c074cc11cdd400099823a610101004bd700b2999820a5014c0103d87a8000153330414a0260366608a60366608a6ea0c0340e8cc1153001023863004bd7025eb804c06ccc114c06ccc114dd401d19822a61021864004bd7025eb80c008018c004dd5980998209baa30133041375402c46660280029101004881003300b00113375e0380022c6082607c6ea800458c10000cdd5981f801981f181f800981f000981e981c9baa00116303b0013300201823375e602260706ea8004c044c0e0dd51805181c1baa00d15333035301e303637546601c0324a66606c66606ca66606c603e606e6ea8c044c0e0dd51808981c1baa300a303837540022940528a504a2266e21200033300b3756601460706ea8c028c0e0dd50009bae3011303837540666eb8c028c0e0dd50198a5014a22940dc0a400044646600200200644a666074002297ae0133225333039300500213303d00233004004001133004004001303c001303d001225333034301d30353754004264646464a666076607c00400a2c6eb8c0f0004c0f0008dd6981d000981b1baa0021622225333035301e0011004132323300100100622533303b00113303c337606ea4018dd3001a5eb7bdb1804c8ccc8894ccc0f0cdc800500189982019bb037520146e9801c01454ccc0f0cdc7805001899299981e9813181f1baa001133041337606ea402cc108c0fcdd5000801080119299981ea9998200008a5114a02980103d87a80001301733041374c00297ae03233001001003225333041001133042337606ea402cdd400525eb7bdb1804c8ccc8894ccc108cdc800780189982319bb0375201e6ea003801454ccc108cdc78078018992999821981618221baa001133047337606ea4040c120c114dd5000801080119299982198160008a6103d87a80001301d33047375000297ae03370000401c26608c66ec0dd48019ba800233006006001375c60800026eb4c104004c114008c10c0044cc100cdd81ba9003374c0046600c00c0026eb8c0e8004dd5981d800981f801181e800991900118030009981c99bb037520046ea00052f5bded8c0600200244a666068002293099299981a8008a4c264666444a66606c66e4000c0084cc01c01ccc0e801000458dd7181a181c0019bae30340013039002303700130370012225333031301a00114bd6f7b6300991919800800a5eb7bdb180894ccc0dc0044cc0e0cdd81ba9006374c00697adef6c6013233322253330383372001400626607866ec0dd48051ba6007005153330383371e01400626607866ec0dd48051ba600700113303c337606ea400cdd3001198030030009bae30360013756606e00260760046072002646600200297adef6c60225333036001133037337606ea4010dd4001a5eb7bdb1804c8ccc8894ccc0dccdc800400189981d99bb037520106ea001c01454ccc0dccdc780400189981d99bb037520106ea001c0044cc0eccdd81ba900337500046600c00c0026eb8c0d4004dd6981b000981d001181c00098171baa01d23032303300122232533302f301d303037540022900009bad30343031375400264a66605e603a60606ea80045300103d87a8000132330010013756606a60646ea8008894ccc0d0004530103d87a80001323332225333035337220100062a66606a66e3c02000c4c03ccc0e4dd400125eb80530103d87a8000133006006001375c60660026eb4c0d0004c0e0008c0d8004cc01000c00888c8cc00400400c894ccc0c4004530103d87a800013233322253330323372200e0062a66606466e3c01c00c4c030cc0d8dd300125eb80530103d87a8000133006006001375c60600026eacc0c4004c0d4008c0cc004c0b8c0acdd50008b19800806119baf3004302b375400202a44646600200200644a66605c0022980103d87a800013322533302d300500213007330310024bd70099802002000981800098188009ba5480008c0ac004dd7181498150011bad302800130243754002604c60466ea803058dd598129813181300298120021bab3023004375860440086eb0c084010c084c084c084004c080c080004c07cc07c004c068dd50049b8748000dc3a4008603200260326034002602a6ea8008dc3a40042c602c602e006602a00460280046028002601e6ea800452613656375a0026eb4004dd68009bad001375c002ae6955ceaab9e5573eae815d0aba2574898011e581c3babcffc6102ec25ced40e1a24fba20371925c46f0299b2b9456360e004c0127d8799f581cdb0d968cda2cc636b28c0f377e66691a065b8004e57be5129aeef8224461757468ff004c01051a000f4240004c01021832004c01021832004c010105000100581d7177799e0f07f377c76676fe9232b9b0bc57ca92c56132817c1ba06d0b"
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect"
|
|
2
|
+
import * as Bytes from "../internal/Bytes.js"
|
|
3
|
+
import * as Cbor from "../Cbor.js"
|
|
4
|
+
import * as TxOutput from "./TxOutput.js"
|
|
5
|
+
import * as TxOutputId from "./TxOutputId.js"
|
|
6
|
+
|
|
7
|
+
export const TxInput = Schema.TaggedStruct("TxInput", {
|
|
8
|
+
id: TxOutputId.TxOutputId,
|
|
9
|
+
output: Schema.optional(TxOutput.TxOutput)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export type TxInput = Schema.Schema.Type<typeof TxInput>
|
|
13
|
+
|
|
14
|
+
export function make(
|
|
15
|
+
id: TxOutputId.TxOutputId,
|
|
16
|
+
output: TxOutput.TxOutput | undefined = undefined
|
|
17
|
+
): TxInput {
|
|
18
|
+
return {
|
|
19
|
+
_tag: "TxInput",
|
|
20
|
+
id,
|
|
21
|
+
output
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const decode = (bytes: Bytes.BytesLike): Cbor.DecodeEffect<TxInput> =>
|
|
26
|
+
Effect.gen(function* () {
|
|
27
|
+
const stream = Bytes.makeStream(bytes)
|
|
28
|
+
|
|
29
|
+
if (yield* (yield* Cbor.decodeTupleLazy(stream.copy()))(Cbor.isBytes)) {
|
|
30
|
+
const id = yield* TxOutputId.decode(stream)
|
|
31
|
+
|
|
32
|
+
return make(id)
|
|
33
|
+
} else if (
|
|
34
|
+
yield* (yield* Cbor.decodeTupleLazy(stream.copy()))(Cbor.isTuple)
|
|
35
|
+
) {
|
|
36
|
+
const [id, output] = yield* Cbor.decodeTuple([
|
|
37
|
+
TxOutputId.decode,
|
|
38
|
+
TxOutput.decode
|
|
39
|
+
])(stream)
|
|
40
|
+
|
|
41
|
+
return make(id, output)
|
|
42
|
+
} else {
|
|
43
|
+
return yield* Effect.fail(
|
|
44
|
+
new Cbor.DecodeError(stream, "unhandled TxInput encoding")
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export function encode(txInput: TxInput, full: boolean = false) {
|
|
50
|
+
if (full) {
|
|
51
|
+
return Cbor.encodeTuple([
|
|
52
|
+
TxOutputId.encode(txInput.id),
|
|
53
|
+
TxOutput.encode(output(txInput))
|
|
54
|
+
])
|
|
55
|
+
} else {
|
|
56
|
+
return TxOutputId.encode(txInput.id)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function output(txInput: TxInput): TxOutput.TxOutput {
|
|
61
|
+
if (!txInput.output) {
|
|
62
|
+
throw new Error("txInput.output not available")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return txInput.output
|
|
66
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect"
|
|
2
|
+
import * as Bytes from "../internal/Bytes.js"
|
|
3
|
+
import * as Cbor from "../Cbor.js"
|
|
4
|
+
import * as Address from "./Address.js"
|
|
5
|
+
import * as Assets from "./Assets.js"
|
|
6
|
+
import * as DatumHash from "./DatumHash.js"
|
|
7
|
+
import * as TxOutputDatum from "./TxOutputDatum.js"
|
|
8
|
+
|
|
9
|
+
export const TxOutputEncodingConfig = Schema.Struct({
|
|
10
|
+
strictBabbage: Schema.optional(Schema.Boolean)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
export type TxOutputEncodingConfig = Schema.Schema.Type<
|
|
14
|
+
typeof TxOutputEncodingConfig
|
|
15
|
+
>
|
|
16
|
+
|
|
17
|
+
export const DEFAULT_TX_OUTPUT_ENCODING_CONFIG: TxOutputEncodingConfig = {
|
|
18
|
+
strictBabbage: true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// TODO: add ref script
|
|
22
|
+
export const TxOutput = Schema.TaggedStruct("TxOutput", {
|
|
23
|
+
address: Address.Address,
|
|
24
|
+
assets: Assets.Assets,
|
|
25
|
+
datum: Schema.optional(TxOutputDatum.TxOutputDatum),
|
|
26
|
+
refScript: Schema.optional(Schema.Uint8ArrayFromSelf),
|
|
27
|
+
encodingConfig: Schema.Struct({
|
|
28
|
+
strictBabbage: Schema.optional(Schema.Boolean)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export type TxOutput = Schema.Schema.Type<typeof TxOutput>
|
|
33
|
+
|
|
34
|
+
export function make({
|
|
35
|
+
address,
|
|
36
|
+
assets,
|
|
37
|
+
datum = undefined,
|
|
38
|
+
refScript = undefined,
|
|
39
|
+
encodingConfig = DEFAULT_TX_OUTPUT_ENCODING_CONFIG
|
|
40
|
+
}: {
|
|
41
|
+
address: Address.Address
|
|
42
|
+
assets: Assets.Assets
|
|
43
|
+
datum?: TxOutputDatum.TxOutputDatum
|
|
44
|
+
refScript?: Uint8Array
|
|
45
|
+
encodingConfig?: TxOutputEncodingConfig
|
|
46
|
+
}): TxOutput {
|
|
47
|
+
return {
|
|
48
|
+
_tag: "TxOutput",
|
|
49
|
+
address,
|
|
50
|
+
assets,
|
|
51
|
+
datum,
|
|
52
|
+
refScript,
|
|
53
|
+
encodingConfig
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const decode = (bytes: Bytes.BytesLike): Cbor.DecodeEffect<TxOutput> =>
|
|
58
|
+
Effect.gen(function* () {
|
|
59
|
+
const stream = Bytes.makeStream(bytes)
|
|
60
|
+
if (yield* Cbor.isObject(bytes)) {
|
|
61
|
+
const {
|
|
62
|
+
0: address,
|
|
63
|
+
1: assets,
|
|
64
|
+
2: datum,
|
|
65
|
+
3: refScript
|
|
66
|
+
} = yield* Cbor.decodeObjectIKey({
|
|
67
|
+
0: Address.decode,
|
|
68
|
+
1: Assets.decode,
|
|
69
|
+
2: TxOutputDatum.decode,
|
|
70
|
+
3: (stream): Cbor.DecodeEffect<number[]> =>
|
|
71
|
+
Effect.gen(function* () {
|
|
72
|
+
if ((yield* Cbor.decodeTag(stream)) != 24n) {
|
|
73
|
+
return yield* new Cbor.DecodeError(
|
|
74
|
+
stream,
|
|
75
|
+
"unexpected reference script tag"
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return yield* Cbor.decodeBytes(stream)
|
|
80
|
+
})
|
|
81
|
+
})(stream)
|
|
82
|
+
|
|
83
|
+
if (!address) {
|
|
84
|
+
return yield* new Cbor.DecodeError(stream, "address field missing")
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!assets) {
|
|
88
|
+
return yield* new Cbor.DecodeError(stream, "assets field missing")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return make({
|
|
92
|
+
address,
|
|
93
|
+
assets,
|
|
94
|
+
...(datum ? { datum } : {}),
|
|
95
|
+
...(refScript ? { refScript: new Uint8Array(refScript) } : {}),
|
|
96
|
+
encodingConfig: { strictBabbage: true }
|
|
97
|
+
})
|
|
98
|
+
} else if (yield* Cbor.isTuple(bytes)) {
|
|
99
|
+
const [address, assets, datumHash] = yield* Cbor.decodeTuple(
|
|
100
|
+
[Address.decode, Assets.decode],
|
|
101
|
+
[DatumHash.decode]
|
|
102
|
+
)(stream)
|
|
103
|
+
|
|
104
|
+
return make({
|
|
105
|
+
address,
|
|
106
|
+
assets,
|
|
107
|
+
...(datumHash ? { _tag: "Hash", hash: datumHash } : {}),
|
|
108
|
+
encodingConfig: {
|
|
109
|
+
strictBabbage: false
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
} else {
|
|
113
|
+
return yield* new Cbor.DecodeError(stream, "unexpected TxOutput encoding")
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
export function encode(txOutput: TxOutput): number[] {
|
|
118
|
+
if (
|
|
119
|
+
(!txOutput.datum || txOutput.datum._tag == "Hash") &&
|
|
120
|
+
!txOutput.refScript &&
|
|
121
|
+
(txOutput.encodingConfig.strictBabbage == null ||
|
|
122
|
+
!txOutput.encodingConfig.strictBabbage)
|
|
123
|
+
) {
|
|
124
|
+
// this is needed to match eternl wallet (de)serialization (annoyingly eternl deserializes the tx and then signs its own serialization)
|
|
125
|
+
// hopefully cardano-cli signs whatever serialization we choose (so we use the eternl variant in order to be compatible with both)
|
|
126
|
+
|
|
127
|
+
const fields = [
|
|
128
|
+
Address.encode(txOutput.address),
|
|
129
|
+
Assets.encode(txOutput.assets)
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
if (txOutput.datum && txOutput.datum._tag == "Hash") {
|
|
133
|
+
fields.push(DatumHash.encode(txOutput.datum.hash))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return Cbor.encodeTuple(fields)
|
|
137
|
+
} else {
|
|
138
|
+
const object: Map<number, number[]> = new Map()
|
|
139
|
+
|
|
140
|
+
object.set(0, Address.encode(txOutput.address))
|
|
141
|
+
object.set(1, Assets.encode(txOutput.assets))
|
|
142
|
+
|
|
143
|
+
if (txOutput.datum) {
|
|
144
|
+
object.set(2, TxOutputDatum.encode(txOutput.datum))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (txOutput.refScript) {
|
|
148
|
+
throw new Error("not yet implemented")
|
|
149
|
+
//object.set(
|
|
150
|
+
// 3,
|
|
151
|
+
// Cbor.encodeTag(24n).concat(
|
|
152
|
+
// Cbor.encodeBytes(
|
|
153
|
+
// Cbor.encodeTuple([
|
|
154
|
+
// Cbor.encodeInt(
|
|
155
|
+
// BigInt(this.refScript.plutusVersionTag)
|
|
156
|
+
// ),
|
|
157
|
+
// txOutput.refScript
|
|
158
|
+
// ])
|
|
159
|
+
// )
|
|
160
|
+
// )
|
|
161
|
+
//)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return Cbor.encodeObjectIKey(object)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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 DatumHash from "./DatumHash.js"
|
|
6
|
+
|
|
7
|
+
export const TxOutputDatum = Schema.Union(
|
|
8
|
+
Schema.TaggedStruct("Inline", { data: Data.Data }),
|
|
9
|
+
Schema.TaggedStruct("Hash", { hash: DatumHash.DatumHash })
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
export type TxOutputDatum = Schema.Schema.Type<typeof TxOutputDatum>
|
|
13
|
+
|
|
14
|
+
export const decode = (
|
|
15
|
+
bytes: Bytes.BytesLike
|
|
16
|
+
): Cbor.DecodeEffect<TxOutputDatum> =>
|
|
17
|
+
Effect.gen(function* () {
|
|
18
|
+
const [type, decodeItem] = yield* Cbor.decodeTagged(bytes)
|
|
19
|
+
|
|
20
|
+
switch (type) {
|
|
21
|
+
case 0:
|
|
22
|
+
return { _tag: "Hash", hash: yield* decodeItem(DatumHash.decode) }
|
|
23
|
+
case 1:
|
|
24
|
+
return {
|
|
25
|
+
_tag: "Inline",
|
|
26
|
+
data: yield* decodeItem((stream: Bytes.Stream) =>
|
|
27
|
+
Effect.gen(function* () {
|
|
28
|
+
const tag = yield* Cbor.decodeTag(stream)
|
|
29
|
+
if (tag != 24n) {
|
|
30
|
+
return yield* Effect.fail(
|
|
31
|
+
new Cbor.DecodeError(stream, `expected 24 as tag, got ${tag}`)
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return yield* Data.decode(yield* Cbor.decodeBytes(stream))
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
default:
|
|
40
|
+
return yield* Effect.fail(
|
|
41
|
+
new Cbor.DecodeError(
|
|
42
|
+
Bytes.makeStream(bytes),
|
|
43
|
+
`unhandled TxOutputDatum type ${type}`
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export function encode(txOutputDatum: TxOutputDatum): number[] {
|
|
50
|
+
switch (txOutputDatum._tag) {
|
|
51
|
+
case "Hash":
|
|
52
|
+
return Cbor.encodeTuple([
|
|
53
|
+
Cbor.encodeInt(0n),
|
|
54
|
+
DatumHash.encode(txOutputDatum.hash)
|
|
55
|
+
])
|
|
56
|
+
case "Inline":
|
|
57
|
+
return Cbor.encodeTuple([
|
|
58
|
+
Cbor.encodeInt(1n),
|
|
59
|
+
Cbor.encodeTag(24n).concat(
|
|
60
|
+
Cbor.encodeBytes(Data.encode(txOutputDatum.data))
|
|
61
|
+
)
|
|
62
|
+
])
|
|
63
|
+
}
|
|
64
|
+
}
|