@@ -8,6 +8,41 @@ import { constFalse, constTrue } from "effect/Function"
8
8
import * as Chain from "./schema/chain.js"
9
9
import * as Hex from "./schema/hex.js"
10
10
import * as Utils from "./Utils.js"
11
+ import { isValidSuiAddress , normalizeSuiAddress } from "@mysten/sui/utils"
12
+
13
+ /**
14
+ * @category schemas
15
+ * @since 2.0.0
16
+ */
17
+ export class SuiCoin extends S . TaggedClass < SuiCoin > ( ) ( "SuiCoin" , {
18
+ address : S . String . pipe (
19
+ S . filter ( ( value ) => {
20
+ const parts = value . split ( "::" )
21
+ if ( parts . length !== 3 ) return false
22
+ const [ addr , module , name ] = parts
23
+
24
+ const ident = / ^ [ A - Z a - z _ ] [ A - Z a - z 0 - 9 _ ] * $ /
25
+ if ( ! ident . test ( module ) || ! ident . test ( name ) ) return false
26
+
27
+ const norm = normalizeSuiAddress ( addr )
28
+ return isValidSuiAddress ( norm )
29
+ } , {
30
+ description :
31
+ "Sui coin type in the form 0x<hex>::<Module>::<Name> with a valid Sui address and Move identifiers" ,
32
+ } ) ,
33
+ S . annotations ( {
34
+ examples : [
35
+ "0x2::sui::SUI" ,
36
+ "0x9003c05db750fe8fb33d8e9a7de814b2ca1af024dc67e06f8529260b03d86fdd::usdt_faucet::USDT_FAUCET" ,
37
+ ] ,
38
+ } ) ,
39
+ ) ,
40
+ } ) {
41
+ [ Hash . symbol ] ( ) : number {
42
+ return Hash . string ( this . address )
43
+ }
44
+ }
45
+
11
46
12
47
/**
13
48
* @category schemas
@@ -133,6 +168,7 @@ export const Any = S.Union(
133
168
CosmosTokenFactory ,
134
169
CosmosBank ,
135
170
CosmosIbcClassic ,
171
+ SuiCoin ,
136
172
)
137
173
/**
138
174
* @category models
@@ -155,6 +191,7 @@ export const TokenFromString = S.transformOrFail(
155
191
S . decodeEither ( CosmosIbcClassic ) ( { _tag : "CosmosIbcClassic" , address } ) ,
156
192
S . decodeEither ( CosmosTokenFactory ) ( { _tag : "CosmosTokenFactory" , address } ) ,
157
193
S . decodeEither ( Cw20 ) ( { _tag : "Cw20" , address } ) ,
194
+ S . decodeEither ( SuiCoin ) ( { _tag : "SuiCoin" , address } ) ,
158
195
] ) ,
159
196
Effect . orElse ( ( ) => S . decodeEither ( Erc20 ) ( { _tag : "Erc20" , address } ) ) ,
160
197
Effect . orElse ( ( ) => S . decodeEither ( CosmosBank ) ( { _tag : "CosmosBank" , address } ) ) ,
@@ -185,6 +222,8 @@ export const AnyFromEncoded = (rpcType: Chain.RpcType) =>
185
222
) ) ,
186
223
Match . when ( "aptos" , ( fromA ) =>
187
224
Effect . fail ( new ParseResult . Type ( ast , fromA , "Aptos not supported." ) ) ) ,
225
+ Match . when ( "sui" , ( ) =>
226
+ pipe ( fromA , S . decode ( S . compose ( Hex . StringFromHex , TokenFromString ) ) ) ) ,
188
227
Match . exhaustive ,
189
228
Effect . catchTag ( "ParseError" , ( error ) =>
190
229
ParseResult . fail ( error . issue ) ) ,
@@ -197,6 +236,17 @@ export const AnyFromEncoded = (rpcType: Chain.RpcType) =>
197
236
} ,
198
237
)
199
238
239
+ export const normalizeSuiTypeTag = ( t : string ) : string => {
240
+ const [ addr , mod , name ] = t . split ( "::" )
241
+ return `${ normalizeSuiAddress ( addr ) } ::${ mod } ::${ name } `
242
+ }
243
+
244
+ const isNativeSui = ( t : string ) : boolean => {
245
+ // compare on normalized address to avoid short/long mismatch
246
+ const norm = normalizeSuiTypeTag ( t )
247
+ return norm === "0x2::sui::SUI"
248
+ }
249
+
200
250
/**
201
251
* @category predicates
202
252
* @since 2.0.0
@@ -209,5 +259,6 @@ export const isNative = Match.type<Any>().pipe(
209
259
Cw20 : constFalse ,
210
260
Erc20 : constFalse ,
211
261
EvmGas : constTrue ,
262
+ SuiCoin : ( t ) => isNativeSui ( t . address ) ,
212
263
} ) ,
213
264
)
0 commit comments