Skip to content

Commit 237a4df

Browse files
committed
feat: upgrade modules; add sdk directory
1 parent dee3e66 commit 237a4df

29 files changed

+2358
-65
lines changed

packages/evolution/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"typescript": "^5.9.2"
4646
},
4747
"dependencies": {
48+
"@effect/platform": "^0.90.6",
4849
"@effect/platform-node": "^0.96.0",
4950
"@noble/hashes": "^1.8.0",
5051
"@scure/base": "^1.2.6",

packages/evolution/src/AddressDetails.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Data, Effect as Eff, ParseResult, Schema } from "effect"
22

3-
import * as Address from "./Address.js"
3+
import * as AddressEras from "./AddressEras.js"
44
import * as Bytes from "./Bytes.js"
55
import * as Function from "./Function.js"
66
import * as NetworkId from "./NetworkId.js"
@@ -26,7 +26,7 @@ export class AddressDetails extends Schema.Class<AddressDetails>("AddressDetails
2626
Schema.Literal("RewardAccount"),
2727
Schema.Literal("ByronAddress")
2828
),
29-
address: Address.Address,
29+
address: AddressEras.AddressEras,
3030
bech32: Schema.String,
3131
hex: Bytes.HexSchema
3232
}) {}
@@ -36,8 +36,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, AddressDetails,
3636
encode: (_, __, ___, toA) => ParseResult.succeed(toA.bech32),
3737
decode: (_, __, ___, fromA) =>
3838
Eff.gen(function* () {
39-
const address = yield* ParseResult.decode(Address.FromBech32)(fromA)
40-
const hex = yield* ParseResult.encode(Address.FromHex)(address)
39+
const address = yield* ParseResult.decode(AddressEras.FromBech32)(fromA)
40+
const hex = yield* ParseResult.encode(AddressEras.FromHex)(address)
4141
return new AddressDetails({
4242
networkId: address.networkId,
4343
type: address._tag,
@@ -53,8 +53,8 @@ export const FromHex = Schema.transformOrFail(Bytes.HexSchema, AddressDetails, {
5353
encode: (_, __, ___, toA) => ParseResult.succeed(toA.hex),
5454
decode: (_, __, ___, fromA) =>
5555
Eff.gen(function* () {
56-
const address = yield* ParseResult.decode(Address.FromHex)(fromA)
57-
const bech32 = yield* ParseResult.encode(Address.FromBech32)(address)
56+
const address = yield* ParseResult.decode(AddressEras.FromHex)(fromA)
57+
const bech32 = yield* ParseResult.encode(AddressEras.FromBech32)(address)
5858
return new AddressDetails({
5959
networkId: address.networkId,
6060
type: address._tag,
@@ -83,7 +83,7 @@ export const equals = (self: AddressDetails, that: AddressDetails): boolean => {
8383
return (
8484
self.networkId === that.networkId &&
8585
self.type === that.type &&
86-
Address.equals(self.address, that.address) &&
86+
AddressEras.equals(self.address, that.address) &&
8787
self.bech32 === that.bech32 &&
8888
self.hex === that.hex
8989
)
@@ -95,18 +95,18 @@ export const equals = (self: AddressDetails, that: AddressDetails): boolean => {
9595
* @since 2.0.0
9696
* @category arbitrary
9797
*/
98-
export const arbitrary = Address.arbitrary.map((address) => fromAddress(address))
98+
export const arbitrary = AddressEras.arbitrary.map((address) => fromAddress(address))
9999

100100
/**
101101
* Create AddressDetails from an Address.
102102
*
103103
* @since 2.0.0
104104
* @category constructors
105105
*/
106-
export const fromAddress = (address: Address.Address): AddressDetails => {
106+
export const fromAddress = (address: AddressEras.AddressEras): AddressDetails => {
107107
// Use schema encoding to get the serialized formats
108-
const bech32 = Eff.runSync(Schema.encode(Address.FromBech32)(address))
109-
const hex = Eff.runSync(Schema.encode(Address.FromHex)(address))
108+
const bech32 = Eff.runSync(Schema.encode(AddressEras.FromBech32)(address))
109+
const hex = Eff.runSync(Schema.encode(AddressEras.FromHex)(address))
110110
return new AddressDetails({
111111
networkId: address.networkId,
112112
type: address._tag,
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
import { bech32 } from "@scure/base"
2+
import { Data, Effect as Eff, FastCheck, ParseResult, Schema } from "effect"
3+
4+
import * as BaseAddress from "./BaseAddress.js"
5+
import * as ByronAddress from "./ByronAddress.js"
6+
import * as Bytes from "./Bytes.js"
7+
import * as EnterpriseAddress from "./EnterpriseAddress.js"
8+
import * as Function from "./Function.js"
9+
import * as PointerAddress from "./PointerAddress.js"
10+
import * as RewardAccount from "./RewardAccount.js"
11+
12+
/**
13+
* CDDL specs
14+
* ```
15+
* ; address format:
16+
* ; [ 8 bit header | payload ];
17+
* ;
18+
* ; shelley payment addresses:
19+
* ; bit 7: 0
20+
* ; bit 6: base/other
21+
* ; bit 5: pointer/enterprise [for base: stake cred is keyhash/scripthash]
22+
* ; bit 4: payment cred is keyhash/scripthash
23+
* ; bits 3-0: network id
24+
* ;
25+
* ; reward addresses:
26+
* ; bits 7-5: 111
27+
* ; bit 4: credential is keyhash/scripthash
28+
* ; bits 3-0: network id
29+
* ;
30+
* ; byron addresses:
31+
* ; bits 7-4: 1000
32+
* ;
33+
* ; 0000: base address: keyhash28,keyhash28
34+
* ; 0001: base address: scripthash28,keyhash28
35+
* ; 0010: base address: keyhash28,scripthash28
36+
* ; 0011: base address: scripthash28,scripthash28
37+
* ; 0100: pointer address: keyhash28, 3 variable length uint
38+
* ; 0101: pointer address: scripthash28, 3 variable length uint
39+
* ; 0110: enterprise address: keyhash28
40+
* ; 0111: enterprise address: scripthash28
41+
* ; 1000: byron address
42+
* ; 1110: reward account: keyhash28
43+
* ; 1111: reward account: scripthash28
44+
* ; 1001-1101: future formats
45+
* ```
46+
*/
47+
48+
/**
49+
* Error thrown when address operations fail
50+
*
51+
* @since 2.0.0
52+
* @category model
53+
*/
54+
export class AddressError extends Data.TaggedError("AddressError")<{
55+
message?: string
56+
cause?: unknown
57+
}> {}
58+
59+
/**
60+
* Union type representing all possible address types.
61+
*
62+
* @since 2.0.0
63+
* @category model
64+
*/
65+
export const AddressEras = Schema.Union(
66+
BaseAddress.BaseAddress,
67+
EnterpriseAddress.EnterpriseAddress,
68+
PointerAddress.PointerAddress,
69+
RewardAccount.RewardAccount,
70+
ByronAddress.ByronAddress
71+
)
72+
73+
export const isAddress = Schema.is(AddressEras)
74+
75+
/**
76+
* Type representing an address.
77+
*
78+
* @since 2.0.0
79+
* @category model
80+
*/
81+
export type AddressEras = typeof AddressEras.Type
82+
83+
/**
84+
* Schema for encoding/decoding addresses as bytes.
85+
*
86+
* @since 2.0.0
87+
* @category schema
88+
*/
89+
export const FromBytes = Schema.transformOrFail(Schema.Uint8ArrayFromSelf, AddressEras, {
90+
strict: true,
91+
encode: (_, __, ___, toA) => {
92+
switch (toA._tag) {
93+
case "BaseAddress":
94+
return ParseResult.encode(BaseAddress.FromBytes)(toA)
95+
case "EnterpriseAddress":
96+
return ParseResult.encode(EnterpriseAddress.FromBytes)(toA)
97+
case "PointerAddress":
98+
return ParseResult.encode(PointerAddress.FromBytes)(toA)
99+
case "RewardAccount":
100+
return ParseResult.encode(RewardAccount.FromBytes)(toA)
101+
case "ByronAddress":
102+
return ParseResult.encode(ByronAddress.BytesSchema)(toA)
103+
}
104+
},
105+
decode: (_, __, ast, fromA) =>
106+
Eff.gen(function* () {
107+
const header = fromA[0]
108+
// Extract address type from the upper 4 bits (bits 4-7)
109+
const addressType = header >> 4
110+
111+
switch (addressType) {
112+
// Base address types (0000, 0001, 0010, 0011)
113+
// Format: [payment credential, stake credential]
114+
case 0b0000: // Key payment, Key stake
115+
case 0b0001: // Script payment, Key stake
116+
case 0b0010: // Key payment, Script stake
117+
case 0b0011:
118+
return yield* ParseResult.decode(BaseAddress.FromBytes)(fromA)
119+
120+
// Enterprise address types (0110, 0111)
121+
// Format: [payment credential only]
122+
case 0b0110: // Key payment
123+
case 0b0111:
124+
return yield* ParseResult.decode(EnterpriseAddress.FromBytes)(fromA)
125+
126+
// Pointer address types (0100, 0101)
127+
// Format: [payment credential, variable length integers for slot, txIndex, certIndex]
128+
case 0b0100: // Key payment with pointer
129+
case 0b0101:
130+
return yield* ParseResult.decode(PointerAddress.FromBytes)(fromA)
131+
132+
case 0b1110:
133+
case 0b1111:
134+
return yield* ParseResult.decode(RewardAccount.FromBytes)(fromA)
135+
136+
case 0b1000:
137+
return yield* ParseResult.decode(ByronAddress.BytesSchema)(fromA)
138+
139+
default:
140+
return yield* ParseResult.fail(new ParseResult.Type(ast, fromA, `Unknown address type: ${addressType}`))
141+
}
142+
})
143+
})
144+
145+
/**
146+
* Schema for encoding/decoding addresses as hex strings.
147+
*
148+
* @since 2.0.0
149+
* @category schema
150+
*/
151+
export const FromHex = Schema.compose(Bytes.FromHex, FromBytes)
152+
153+
/**
154+
* Schema for encoding/decoding addresses as Bech32 strings.
155+
*
156+
* @since 2.0.0
157+
* @category schema
158+
*/
159+
export const FromBech32 = Schema.transformOrFail(Schema.String, AddressEras, {
160+
strict: true,
161+
encode: (_, __, ast, toA) =>
162+
Eff.gen(function* () {
163+
const bytes = yield* ParseResult.encode(FromBytes)(toA)
164+
let prefix: string
165+
switch (toA._tag) {
166+
case "BaseAddress":
167+
case "EnterpriseAddress":
168+
case "PointerAddress":
169+
prefix = toA.networkId === 0 ? "addr_test" : "addr"
170+
break
171+
case "RewardAccount":
172+
prefix = toA.networkId === 0 ? "stake_test" : "stake"
173+
break
174+
case "ByronAddress":
175+
return yield* ParseResult.fail(
176+
new ParseResult.Type(ast, toA, "Byron addresses do not support Bech32 encoding")
177+
)
178+
}
179+
const result = yield* Eff.try({
180+
try: () => {
181+
const words = bech32.toWords(bytes)
182+
return bech32.encode(prefix, words, false)
183+
},
184+
catch: (error) => new ParseResult.Type(ast, toA, `Failed to encode Bech32: ${(error as Error).message}`)
185+
})
186+
return result
187+
}),
188+
decode: (fromA, _, ast) =>
189+
Eff.gen(function* () {
190+
const result = yield* Eff.try({
191+
try: () => {
192+
const decoded = bech32.decode(fromA as any, false)
193+
const bytes = bech32.fromWords(decoded.words)
194+
return new Uint8Array(bytes)
195+
},
196+
catch: (error) => new ParseResult.Type(ast, fromA, `Failed to decode Bech32: ${(error as Error).message}`)
197+
})
198+
return yield* ParseResult.decode(FromBytes)(result)
199+
})
200+
}).annotations({
201+
identifier: "Address.FromBech32",
202+
description: "Transforms Bech32 string to Address"
203+
})
204+
205+
/**
206+
* Checks if two addresses are equal.
207+
*
208+
* @since 2.0.0
209+
* @category utils
210+
*/
211+
export const equals = (a: AddressEras, b: AddressEras): boolean => {
212+
if (a._tag !== b._tag) {
213+
return false
214+
}
215+
switch (a._tag) {
216+
case "BaseAddress":
217+
return BaseAddress.equals(a, b as BaseAddress.BaseAddress)
218+
case "EnterpriseAddress":
219+
return EnterpriseAddress.equals(a, b as EnterpriseAddress.EnterpriseAddress)
220+
case "PointerAddress":
221+
return PointerAddress.equals(a, b as PointerAddress.PointerAddress)
222+
case "RewardAccount":
223+
return RewardAccount.equals(a, b as RewardAccount.RewardAccount)
224+
case "ByronAddress":
225+
return false
226+
}
227+
}
228+
229+
/**
230+
* FastCheck arbitrary for Address instances.
231+
*
232+
* @since 2.0.0
233+
* @category arbitrary
234+
*
235+
*/
236+
export const arbitrary = FastCheck.oneof(
237+
BaseAddress.arbitrary,
238+
EnterpriseAddress.arbitrary,
239+
PointerAddress.arbitrary,
240+
RewardAccount.arbitrary
241+
)
242+
243+
// ============================================================================
244+
// Parsing Functions
245+
// ============================================================================
246+
247+
/**
248+
* Parse an Address from bytes.
249+
*
250+
* @since 2.0.0
251+
* @category parsing
252+
*/
253+
export const fromBytes = Function.makeDecodeSync(FromBytes, AddressError, "Address.fromBytes")
254+
255+
/**
256+
* Parse an Address from hex string.
257+
*
258+
* @since 2.0.0
259+
* @category parsing
260+
*/
261+
export const fromHex = Function.makeDecodeSync(FromHex, AddressError, "Address.fromHex")
262+
263+
/**
264+
* Parse an Address from Bech32 string.
265+
*
266+
* @since 2.0.0
267+
* @category parsing
268+
*/
269+
export const fromBech32 = Function.makeDecodeSync(FromBech32, AddressError, "Address.fromBech32")
270+
271+
// ============================================================================
272+
// Encoding Functions
273+
// ============================================================================
274+
275+
/**
276+
* Convert an Address to bytes.
277+
*
278+
* @since 2.0.0
279+
* @category encoding
280+
*/
281+
export const toBytes = Function.makeEncodeSync(FromBytes, AddressError, "Address.toBytes")
282+
283+
/**
284+
* Convert an Address to hex string.
285+
*
286+
* @since 2.0.0
287+
* @category encoding
288+
*/
289+
export const toHex = Function.makeEncodeSync(FromHex, AddressError, "Address.toHex")
290+
291+
/**
292+
* Convert an Address to Bech32 string.
293+
*
294+
* @since 2.0.0
295+
* @category encoding
296+
*/
297+
export const toBech32 = Function.makeEncodeSync(FromBech32, AddressError, "Address.toBech32")
298+
299+
/**
300+
* Effect-based error handling variants for functions that can fail.
301+
*
302+
* @since 2.0.0
303+
* @category effect
304+
*/
305+
export namespace Either {
306+
export const fromBytes = Function.makeDecodeEither(FromBytes, AddressError)
307+
export const fromHex = Function.makeDecodeEither(FromHex, AddressError)
308+
export const fromBech32 = Function.makeDecodeEither(FromBech32, AddressError)
309+
310+
export const toBytes = Function.makeEncodeEither(FromBytes, AddressError)
311+
export const toHex = Function.makeEncodeEither(FromHex, AddressError)
312+
export const toBech32 = Function.makeEncodeEither(FromBech32, AddressError)
313+
}

0 commit comments

Comments
 (0)