1- import { Data , Effect , FastCheck , ParseResult , pipe , Schema } from "effect"
1+ import { bech32 } from "@scure/base"
2+ import { Data , Effect as Eff , FastCheck , ParseResult , Schema } from "effect"
23
34import * as BaseAddress from "./BaseAddress.js"
4- import * as Bech32 from "./Bech32.js"
55import * as ByronAddress from "./ByronAddress.js"
66import * as Bytes from "./Bytes.js"
7- import { createEncoders } from "./Codec.js"
87import * as EnterpriseAddress from "./EnterpriseAddress.js"
98import * as PointerAddress from "./PointerAddress.js"
109import * as RewardAccount from "./RewardAccount.js"
@@ -99,7 +98,7 @@ export const FromBytes = Schema.transformOrFail(Schema.Uint8ArrayFromSelf, Addre
9998 }
10099 } ,
101100 decode : ( _ , __ , ast , fromA ) =>
102- Effect . gen ( function * ( ) {
101+ Eff . gen ( function * ( ) {
103102 const header = fromA [ 0 ]
104103 // Extract address type from the upper 4 bits (bits 4-7)
105104 const addressType = header >> 4
@@ -152,10 +151,10 @@ export const FromHex = Schema.compose(Bytes.FromHex, FromBytes)
152151 * @since 2.0.0
153152 * @category schema
154153 */
155- export const FromBech32 = Schema . transformOrFail ( Schema . typeSchema ( Bech32 . Bech32Schema ) , Address , {
154+ export const FromBech32 = Schema . transformOrFail ( Schema . String , Address , {
156155 strict : true ,
157- encode : ( _ , __ , ___ , toA ) =>
158- Effect . gen ( function * ( ) {
156+ encode : ( _ , __ , ast , toA ) =>
157+ Eff . gen ( function * ( ) {
159158 const bytes = yield * ParseResult . encode ( FromBytes ) ( toA )
160159 let prefix : string
161160 switch ( toA . _tag ) {
@@ -168,13 +167,34 @@ export const FromBech32 = Schema.transformOrFail(Schema.typeSchema(Bech32.Bech32
168167 prefix = toA . networkId === 0 ? "stake_test" : "stake"
169168 break
170169 case "ByronAddress" :
171- prefix = ""
172- break
170+ return yield * ParseResult . fail (
171+ new ParseResult . Type ( ast , toA , "Byron addresses do not support Bech32 encoding" )
172+ )
173173 }
174- const b = yield * ParseResult . decode ( Bech32 . FromBytes ( prefix ) ) ( bytes )
175- return b
174+ const result = yield * Eff . try ( {
175+ try : ( ) => {
176+ const words = bech32 . toWords ( bytes )
177+ return bech32 . encode ( prefix , words , false )
178+ } ,
179+ catch : ( error ) => new ParseResult . Type ( ast , toA , `Failed to encode Bech32: ${ ( error as Error ) . message } ` )
180+ } )
181+ return result
176182 } ) ,
177- decode : ( fromI ) => pipe ( ParseResult . encode ( Bech32 . FromBytes ( ) ) ( fromI ) , Effect . flatMap ( ParseResult . decode ( FromBytes ) ) )
183+ decode : ( fromA , _ , ast ) =>
184+ Eff . gen ( function * ( ) {
185+ const result = yield * Eff . try ( {
186+ try : ( ) => {
187+ const decoded = bech32 . decode ( fromA as any , false )
188+ const bytes = bech32 . fromWords ( decoded . words )
189+ return new Uint8Array ( bytes )
190+ } ,
191+ catch : ( error ) => new ParseResult . Type ( ast , fromA , `Failed to decode Bech32: ${ ( error as Error ) . message } ` )
192+ } )
193+ return yield * ParseResult . decode ( FromBytes ) ( result )
194+ } )
195+ } ) . annotations ( {
196+ identifier : "Address.FromBech32" ,
197+ description : "Transforms Bech32 string to Address"
178198} )
179199
180200/**
@@ -202,29 +222,156 @@ export const equals = (a: Address, b: Address): boolean => {
202222}
203223
204224/**
205- * FastCheck generator for addresses .
225+ * FastCheck arbitrary for Address instances .
206226 *
207227 * @since 2.0.0
208- * @category testing
228+ * @category arbitrary
229+ *
209230 */
210- export const generator = FastCheck . oneof (
211- BaseAddress . generator ,
212- EnterpriseAddress . generator ,
213- PointerAddress . generator ,
214- RewardAccount . generator
231+ export const arbitrary = FastCheck . oneof (
232+ BaseAddress . arbitrary ,
233+ EnterpriseAddress . arbitrary ,
234+ PointerAddress . arbitrary ,
235+ RewardAccount . arbitrary
215236)
216237
238+ // ============================================================================
239+ // Parsing Functions
240+ // ============================================================================
241+
217242/**
218- * Codec utilities for addresses .
243+ * Parse an Address from bytes .
219244 *
220245 * @since 2.0.0
221- * @category encoding/decoding
246+ * @category parsing
222247 */
223- export const Codec = createEncoders (
224- {
225- bech32 : FromBech32 ,
226- hex : FromHex ,
227- bytes : FromBytes
228- } ,
229- AddressError
230- )
248+ export const fromBytes = ( bytes : Uint8Array ) : Address => Eff . runSync ( Effect . fromBytes ( bytes ) )
249+
250+ /**
251+ * Parse an Address from hex string.
252+ *
253+ * @since 2.0.0
254+ * @category parsing
255+ */
256+ export const fromHex = ( hex : string ) : Address => Eff . runSync ( Effect . fromHex ( hex ) )
257+
258+ /**
259+ * Parse an Address from Bech32 string.
260+ *
261+ * @since 2.0.0
262+ * @category parsing
263+ */
264+ export const fromBech32 = ( bech32 : string ) : Address => Eff . runSync ( Effect . fromBech32 ( bech32 ) )
265+
266+ // ============================================================================
267+ // Encoding Functions
268+ // ============================================================================
269+
270+ /**
271+ * Convert an Address to bytes.
272+ *
273+ * @since 2.0.0
274+ * @category encoding
275+ */
276+ export const toBytes = ( address : Address ) : Uint8Array => Eff . runSync ( Effect . toBytes ( address ) )
277+
278+ /**
279+ * Convert an Address to hex string.
280+ *
281+ * @since 2.0.0
282+ * @category encoding
283+ */
284+ export const toHex = ( address : Address ) : string => Eff . runSync ( Effect . toHex ( address ) )
285+
286+ /**
287+ * Convert an Address to Bech32 string.
288+ *
289+ * @since 2.0.0
290+ * @category encoding
291+ */
292+ export const toBech32 = ( address : Address ) : string => Eff . runSync ( Effect . toBech32 ( address ) )
293+
294+ // ============================================================================
295+ // Effect Namespace - Effect-based Error Handling
296+ // ============================================================================
297+
298+ /**
299+ * Effect-based error handling variants for functions that can fail.
300+ * Returns Effect<Success, Error> for composable error handling.
301+ *
302+ * @since 2.0.0
303+ * @category effect
304+ */
305+ export namespace Effect {
306+ /**
307+ * Parse an Address from bytes.
308+ *
309+ * @since 2.0.0
310+ * @category parsing
311+ */
312+ export const fromBytes = ( bytes : Uint8Array ) =>
313+ Eff . mapError (
314+ Schema . decode ( FromBytes ) ( bytes ) ,
315+ ( error ) => new AddressError ( { message : "Failed to decode Address from bytes" , cause : error } )
316+ )
317+
318+ /**
319+ * Parse an Address from hex string.
320+ *
321+ * @since 2.0.0
322+ * @category parsing
323+ */
324+ export const fromHex = ( hex : string ) =>
325+ Eff . mapError (
326+ Schema . decode ( FromHex ) ( hex ) ,
327+ ( error ) => new AddressError ( { message : "Failed to decode Address from hex" , cause : error } )
328+ )
329+
330+ /**
331+ * Parse an Address from Bech32 string.
332+ *
333+ * @since 2.0.0
334+ * @category parsing
335+ */
336+ export const fromBech32 = ( bech32 : string ) =>
337+ Eff . mapError (
338+ Schema . decode ( FromBech32 ) ( bech32 ) ,
339+ ( error ) => new AddressError ( { message : "Failed to decode Address from Bech32" , cause : error } )
340+ )
341+
342+ /**
343+ * Convert an Address to bytes.
344+ *
345+ * @since 2.0.0
346+ * @category encoding
347+ */
348+ export const toBytes = ( address : Address ) =>
349+ Eff . mapError (
350+ Schema . encode ( FromBytes ) ( address ) ,
351+ ( error ) => new AddressError ( { message : "Failed to encode Address to bytes" , cause : error } )
352+ )
353+
354+ /**
355+ * Convert an Address to hex string.
356+ *
357+ * @since 2.0.0
358+ * @category encoding
359+ */
360+ export const toHex = ( address : Address ) =>
361+ Eff . mapError (
362+ Schema . encode ( FromHex ) ( address ) ,
363+ ( error ) => new AddressError ( { message : "Failed to encode Address to hex" , cause : error } )
364+ )
365+
366+ /**
367+ * Convert an Address to Bech32 string.
368+ *
369+ * @since 2.0.0
370+ * @category encoding
371+ */
372+ export const toBech32 = ( address : Address ) =>
373+ Eff . mapError (
374+ Schema . encode ( FromBech32 ) ( address ) ,
375+ ( error ) => new AddressError ( { message : "Failed to encode Address to Bech32" , cause : error } )
376+ )
377+ }
0 commit comments