@@ -10,11 +10,13 @@ import type {
1010 TokenRawAmount ,
1111 UniversalChainId ,
1212} from "@unionlabs/sdk/schema"
13- import { Effect , Match , Option , ParseResult , pipe } from "effect"
13+ import { Effect , Either , flow , Match , Option , ParseResult , pipe } from "effect"
1414import * as A from "effect/Array"
1515import type { NoSuchElementException , UnknownException } from "effect/Cause"
16+ import { constFalse , constTrue } from "effect/Function"
1617import * as S from "effect/Schema"
1718import { fromHex , isHex } from "viem"
19+ import { GenericFlowError } from "../../errors"
1820import type { TransferArgs } from "./check-filling"
1921
2022export type Intent = {
@@ -51,17 +53,35 @@ export const createContext = Effect.fn((
5153 args : TransferArgs ,
5254) : Effect . Effect <
5355 TransferContext ,
54- NoSuchElementException | ParseResult . ParseError | UnknownException ,
56+ NoSuchElementException | ParseResult . ParseError | UnknownException | GenericFlowError ,
5557 never
5658> =>
5759 Effect . gen ( function * ( ) {
5860 console . debug ( "[createContext] args:" , args )
5961
62+ const baseAmount = yield * parseBaseAmount ( args . baseAmount ) . pipe (
63+ Effect . mapError ( ( cause ) =>
64+ new GenericFlowError ( {
65+ message : "Could not parse base amount" ,
66+ cause,
67+ } )
68+ ) ,
69+ )
70+
71+ const quoteAmount = yield * parseBaseAmount ( args . quoteAmount ) . pipe (
72+ Effect . mapError ( ( cause ) =>
73+ new GenericFlowError ( {
74+ message : "Could not parse quote amount" ,
75+ cause,
76+ } )
77+ ) ,
78+ )
79+
6080 const sendOrder = yield * TokenOrder . make ( {
61- baseAmount : Option . getOrThrow ( parseBaseAmount ( args . baseAmount ) ) ,
81+ baseAmount,
6282 baseToken : args . baseToken ,
6383 quoteToken : args . quoteToken ,
64- quoteAmount : Option . getOrThrow ( parseBaseAmount ( args . quoteAmount ) ) ,
84+ quoteAmount,
6585 destination : args . destinationChain ,
6686 receiver : args . receiver ,
6787 sender : args . sender ,
@@ -78,43 +98,66 @@ export const createContext = Effect.fn((
7898 } )
7999
80100 // on destination chain tokens, find wrappings[] such that one exists where unwrapped_denom matches basetoken and unwrapped_chain and wrapped_chain universal ids match
81- const encodedFeeBaseToken = S . encodeSync ( Token . AnyFromEncoded ( args . sourceChain . rpc_type ) ) (
101+ const encodedFeeBaseToken = yield * pipe (
82102 args . fee . baseToken ,
103+ S . encode ( Token . AnyFromEncoded ( args . sourceChain . rpc_type ) ) ,
104+ Effect . mapError ( ( cause ) =>
105+ new GenericFlowError ( {
106+ message : "Could not base token" ,
107+ cause,
108+ } )
109+ ) ,
83110 )
84111
85- const shouldIncludeFees = shouldChargeFees ( args . fee , uiStore . edition , args . sourceChain )
112+ const shouldIncludeFees = shouldChargeFees (
113+ args . fee ,
114+ uiStore . edition ,
115+ args . sourceChain ,
116+ args . destinationChain ,
117+ )
86118
87119 const produceBatch = Effect . gen ( function * ( ) {
120+ console . log ( { shouldIncludeFees } )
88121 if ( shouldIncludeFees ) {
89- const feeQuoteToken = yield * maybeFeeQuoteToken . pipe (
90- Option . orElse ( ( ) =>
122+ const feeQuoteToken = yield * Effect . if ( args . baseToken . address === "au" , {
123+ onTrue : ( ) => Effect . succeed ( args . quoteToken ) ,
124+ onFalse : ( ) =>
91125 pipe (
92- tokensStore . getData ( args . destinationChain . universal_chain_id ) ,
93- Option . flatMap (
94- A . findFirst ( ( token ) =>
95- A . filter ( token . wrapping , ( x ) =>
96- x . unwrapped_denom === encodedFeeBaseToken
97- && x . unwrapped_chain . universal_chain_id === args . sourceChain . universal_chain_id
98- && x . wrapped_chain . universal_chain_id
99- === args . destinationChain . universal_chain_id )
100- . length
101- === 1
102- ) ,
126+ maybeFeeQuoteToken ,
127+ Either . fromOption ( ( ) => "No fee quote token" ) ,
128+ Either . orElse ( ( ) =>
129+ pipe (
130+ tokensStore . getData ( args . destinationChain . universal_chain_id ) ,
131+ Either . fromOption ( ( ) => "No matching token in token store" ) ,
132+ Either . flatMap ( flow (
133+ A . findFirst ( ( token ) =>
134+ A . filter ( token . wrapping , ( x ) =>
135+ x . unwrapped_denom === encodedFeeBaseToken
136+ && x . unwrapped_chain . universal_chain_id
137+ === args . sourceChain . universal_chain_id
138+ && x . wrapped_chain . universal_chain_id
139+ === args . destinationChain . universal_chain_id )
140+ . length
141+ === 1
142+ ) ,
143+ Either . fromOption ( ( ) =>
144+ `No quote token wrapping found for ${ args . destinationChain . universal_chain_id } given ${ args . fee . baseToken } `
145+ ) ,
146+ ) ) ,
147+ Either . map ( x => x . denom ) ,
148+ Either . flatMap ( ( raw ) =>
149+ S . decodeEither ( Token . AnyFromEncoded ( args . destinationChain . rpc_type ) ) ( raw )
150+ ) ,
151+ )
103152 ) ,
104- Option . map ( x => x . denom ) ,
105- Option . flatMap ( ( raw ) =>
106- S . decodeOption ( Token . AnyFromEncoded ( args . destinationChain . rpc_type ) ) ( raw )
153+ Effect . mapError ( ( cause ) =>
154+ new GenericFlowError ( {
155+ message : "Could not determine fee quote token" ,
156+ cause,
157+ } )
107158 ) ,
108- )
109- ) ,
110- Option . orElse ( ( ) => {
111- if ( args . baseToken . address === "au" ) {
112- return Option . some ( args . quoteToken )
113- }
114- console . error ( "Could not determine fee quote token." )
115- return Option . none ( )
116- } ) ,
117- )
159+ ) ,
160+ } )
118161
119162 const feeOrder = yield * TokenOrder . make ( {
120163 baseAmount : args . fee . baseAmount ,
@@ -130,7 +173,8 @@ export const createContext = Effect.fn((
130173 version : args . version ,
131174 } )
132175
133- return Batch . make ( [ sendOrder , feeOrder ] ) . pipe (
176+ return pipe (
177+ Batch . make ( [ sendOrder , feeOrder ] ) ,
134178 Batch . optimize ,
135179 )
136180 } else {
@@ -150,19 +194,17 @@ export const createContext = Effect.fn((
150194 Option . some ,
151195 )
152196
153- const ctx = yield * parseBaseAmount ( args . baseAmount ) . pipe (
154- Option . flatMap ( ( baseAmount ) => {
155- const intents = createIntents ( args , baseAmount )
156-
157- return intents . length > 0
197+ const ctx = yield * pipe (
198+ createIntents ( args , baseAmount ) ,
199+ ( intents ) =>
200+ intents . length > 0
158201 ? Option . some ( {
159202 intents,
160203 allowances : Option . none ( ) ,
161204 request : Option . none ( ) ,
162205 message : Option . none ( ) ,
163206 } )
164- : Option . none ( )
165- } ) ,
207+ : Option . none ( ) ,
166208 )
167209
168210 return {
@@ -173,7 +215,12 @@ export const createContext = Effect.fn((
173215)
174216
175217const createIntents = ( args : TransferArgs , baseAmount : TokenRawAmount ) : Intent [ ] => {
176- const shouldIncludeFees = shouldChargeFees ( args . fee , uiStore . edition , args . sourceChain )
218+ const shouldIncludeFees = shouldChargeFees (
219+ args . fee ,
220+ uiStore . edition ,
221+ args . sourceChain ,
222+ args . destinationChain ,
223+ )
177224 const baseIntent = createBaseIntent ( args , baseAmount )
178225
179226 return Match . value ( args . sourceChain . rpc_type ) . pipe (
@@ -240,16 +287,38 @@ const createBaseIntent = (
240287 ucs03address : args . ucs03address ,
241288} )
242289
243- // Fee strategy: BTC edition only charges fees when going FROM Babylon
244- const shouldChargeFees = ( fee : FeeIntent , edition : string , sourceChain : Chain ) : boolean => {
245- if ( fee . baseAmount === 0n ) {
246- return false
247- }
248- if ( sourceChain . testnet ) {
249- return true
250- }
251- return sourceChain . universal_chain_id === "babylon.bbn-1"
252- }
290+ const shouldChargeFees = (
291+ fee : FeeIntent ,
292+ edition : string ,
293+ sourceChain : Chain ,
294+ destinationChain : Chain ,
295+ ) : boolean =>
296+ pipe (
297+ Match . value ( {
298+ baseAmount : fee . baseAmount ,
299+ edition,
300+ sourceChain,
301+ destinationChain,
302+ } ) ,
303+ Match . when (
304+ { baseAmount : 0n } ,
305+ constFalse ,
306+ ) ,
307+ Match . when (
308+ { sourceChain : { testnet : true } } ,
309+ constTrue ,
310+ ) ,
311+ Match . when (
312+ { sourceChain : { universal_chain_id : "babylon.bbn-1" } } ,
313+ constTrue ,
314+ ) ,
315+ Match . whenOr (
316+ { sourceChain : { universal_chain_id : "ethereum.1" } } ,
317+ { destinationChain : { universal_chain_id : "ethereum.1" } } ,
318+ constTrue ,
319+ ) ,
320+ Match . orElse ( constTrue ) ,
321+ )
253322
254323const normalizeToken = ( token : string | `0x${string } `, rpcType : string ) : string => {
255324 return rpcType === "cosmos" && isHex ( token ) ? fromHex ( token , "string" ) : token
0 commit comments