1
1
import { Data , Effect , Match , Option } from "effect"
2
2
import type { AddressCanonicalBytes , Chain } from "@unionlabs/sdk/schema"
3
3
import { fromHex , http , isHex } from "viem"
4
- import { createViemPublicClient , readErc20Allowance , ViemPublicClient } from "@unionlabs/sdk/evm"
5
- import { CosmWasmClientSource , createCosmWasmClient } from "@unionlabs/sdk/cosmos"
4
+ import {
5
+ createViemPublicClient ,
6
+ readErc20Allowance ,
7
+ ViemPublicClient
8
+ } from "@unionlabs/sdk/evm"
9
+ import {
10
+ CosmWasmClientSource ,
11
+ createCosmWasmClient
12
+ } from "@unionlabs/sdk/cosmos"
6
13
import { isValidBech32ContractAddress } from "@unionlabs/client"
7
14
import { cosmosSpenderAddresses } from "$lib/constants/spender-addresses.ts"
8
15
import {
9
16
AllowanceCheckError ,
10
17
CosmosQueryError ,
11
18
type TransferFlowError
12
19
} from "$lib/components/Transfer/state/errors.ts"
13
- import type { TransferIntents } from "$lib/components/Transfer/state/filling/create-intents.ts"
14
- import { groupBy } from "effect/Array"
20
+ import type { TransferIntent } from "$lib/components/Transfer/state/filling/create-intents.ts"
15
21
16
22
export class ApprovalStep extends Data . TaggedClass ( "ApprovalStep" ) < {
17
23
token : string
18
24
requiredAmount : bigint
19
25
currentAllowance : bigint
20
26
} > { }
21
27
22
- function gatherNeededAmounts ( intents : TransferIntents ) : Map < string , bigint > {
23
- const neededMap = new Map < string , bigint > ( )
24
- for ( const { context } of intents ) {
25
- const current = neededMap . get ( context . baseToken ) ?? 0n
26
- neededMap . set ( context . baseToken , current + context . baseAmount )
28
+ function gatherNeededAmounts ( contexts : Array < { baseToken : string ; baseAmount : bigint } > ) {
29
+ const map = new Map < string , bigint > ( )
30
+ for ( const { baseToken , baseAmount } of contexts ) {
31
+ const current = map . get ( baseToken ) ?? 0n
32
+ map . set ( baseToken , current + baseAmount )
27
33
}
28
- return neededMap
34
+ return map
29
35
}
30
36
31
37
export function checkAllowances (
32
- intents : TransferIntents
38
+ intent : TransferIntent
33
39
) : Effect . Effect < Option . Option < Array < ApprovalStep > > , TransferFlowError > {
34
40
return Effect . gen ( function * ( ) {
35
- if ( intents . length === 0 ) return Option . none ( )
36
-
37
- const grouped = groupBy ( intents , intent => intent . context . sourceChain . universal_chain_id )
41
+ if ( intent . contexts . length === 0 ) return Option . none ( )
38
42
39
- const allSteps : Array < ApprovalStep > = [ ]
43
+ const [ firstContext ] = intent . contexts
44
+ const chain = firstContext . sourceChain
45
+ const sender = firstContext . sender
46
+ const spender = firstContext . ucs03address
40
47
41
- for ( const [ , group ] of Object . entries ( grouped ) ) {
42
- const chain = group [ 0 ] . context . sourceChain
43
- const sender = group [ 0 ] . context . sender
44
- const neededMap = gatherNeededAmounts ( group )
45
- const tokenAddresses = [ ...neededMap . keys ( ) ]
46
-
47
- const allowancesOpt = yield * Match . value ( chain . rpc_type ) . pipe (
48
- Match . when ( "evm" , ( ) =>
49
- handleEvmAllowances ( tokenAddresses , sender , group [ 0 ] . context . ucs03address , chain ) . pipe (
50
- Effect . mapError ( err => new AllowanceCheckError ( { cause : err } ) )
51
- )
52
- ) ,
53
- Match . when ( "cosmos" , ( ) =>
54
- handleCosmosAllowances ( tokenAddresses , sender , chain ) . pipe (
55
- Effect . mapError ( err => new AllowanceCheckError ( { cause : err } ) )
56
- )
57
- ) ,
58
- Match . orElse ( ( ) => Effect . succeed ( Option . none ( ) ) )
59
- )
48
+ const neededMap = gatherNeededAmounts (
49
+ intent . contexts . map ( ( { baseToken, baseAmount } ) => ( { baseToken, baseAmount } ) )
50
+ )
51
+ const tokenAddresses = [ ...neededMap . keys ( ) ]
60
52
61
- const allowances = Option . getOrElse ( allowancesOpt , ( ) => [ ] )
53
+ const allowancesOpt = yield * Match . value ( chain . rpc_type ) . pipe (
54
+ Match . when ( "evm" , ( ) =>
55
+ handleEvmAllowances ( tokenAddresses , sender , spender , chain ) . pipe (
56
+ Effect . mapError ( err => new AllowanceCheckError ( { cause : err } ) )
57
+ )
58
+ ) ,
59
+ Match . when ( "cosmos" , ( ) =>
60
+ handleCosmosAllowances ( tokenAddresses , sender , chain ) . pipe (
61
+ Effect . mapError ( err => new AllowanceCheckError ( { cause : err } ) )
62
+ )
63
+ ) ,
64
+ Match . orElse ( ( ) => Effect . succeed ( Option . none ( ) ) )
65
+ )
62
66
63
- for ( const { token, allowance } of allowances ) {
64
- const requiredAmount = neededMap . get ( token ) ?? 0n
65
- if ( allowance < requiredAmount ) {
66
- allSteps . push ( new ApprovalStep ( { token, requiredAmount, currentAllowance : allowance } ) )
67
- }
67
+ const allowances = Option . getOrElse ( allowancesOpt , ( ) => [ ] )
68
+ const steps : Array < ApprovalStep > = [ ]
69
+
70
+ for ( const { token, allowance } of allowances ) {
71
+ const requiredAmount = neededMap . get ( token ) ?? 0n
72
+ if ( allowance < requiredAmount ) {
73
+ steps . push (
74
+ new ApprovalStep ( {
75
+ token,
76
+ requiredAmount,
77
+ currentAllowance : allowance
78
+ } )
79
+ )
68
80
}
69
81
}
70
82
71
- return allSteps . length > 0 ? Option . some ( allSteps ) : Option . none ( )
83
+ return steps . length > 0 ? Option . some ( steps ) : Option . none ( )
72
84
} )
73
85
}
74
86
87
+
75
88
function handleEvmAllowances (
76
89
tokenAddresses : Array < string > ,
77
90
sender : AddressCanonicalBytes ,
@@ -80,9 +93,7 @@ function handleEvmAllowances(
80
93
) : Effect . Effect < Option . Option < Array < { token : string ; allowance : bigint } > > , unknown > {
81
94
return Effect . gen ( function * ( ) {
82
95
const viemChainOpt = sourceChain . toViemChain ( )
83
- if ( Option . isNone ( viemChainOpt ) ) {
84
- return Option . none ( )
85
- }
96
+ if ( Option . isNone ( viemChainOpt ) ) return Option . none ( )
86
97
87
98
const publicClientSource = yield * createViemPublicClient ( {
88
99
chain : viemChainOpt . value ,
@@ -167,28 +178,20 @@ export function handleCosmosAllowances(
167
178
}
168
179
}
169
180
170
- if ( ! bech32Address ) {
171
- return { token : tokenAddress , allowance : 0n }
172
- }
181
+ if ( ! bech32Address ) return { token : tokenAddress , allowance : 0n }
173
182
174
183
const result = yield * Effect . tryPromise ( ( ) =>
175
184
cosmwasmClient . queryContractSmart ( bech32Address , {
176
185
allowance : { owner, spender }
177
186
} )
178
- ) . pipe ( Effect . mapError ( err => new CosmosQueryError ( { token : tokenAddress , cause : err } ) ) )
187
+ ) . pipe (
188
+ Effect . mapError ( err => new CosmosQueryError ( { token : tokenAddress , cause : err } ) )
189
+ )
179
190
180
191
const allowance = result . allowance ? BigInt ( result . allowance ) : 0n
181
192
return { token : tokenAddress , allowance }
182
193
} ) . pipe ( Effect . provideService ( CosmWasmClientSource , { client : cosmwasmClient } ) )
183
194
)
184
- ) . pipe (
185
- Effect . mapError (
186
- err =>
187
- new CosmosQueryError ( {
188
- token : "N/A" ,
189
- cause : err
190
- } )
191
- )
192
195
)
193
196
194
197
return Option . some ( checks )
0 commit comments