Skip to content

Commit 88a6878

Browse files
authored
[TypeScript SDK] Add methods for the CoinRead endpoints (#7507)
1 parent 69b85e8 commit 88a6878

File tree

9 files changed

+354
-34
lines changed

9 files changed

+354
-34
lines changed

.changeset/lucky-lobsters-joke.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@mysten/sui.js": minor
3+
---
4+
5+
Add methods for the CoinRead endpoints

sdk/typescript/src/providers/json-rpc-provider.ts

+123-9
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ import {
4545
normalizeSuiObjectId,
4646
SuiTransactionAuthSignersResponse,
4747
CoinMetadataStruct,
48+
PaginatedCoins,
4849
GetObjectDataResponse,
4950
GetOwnedObjectsResponse,
5051
DelegatedStake,
5152
ValidatorMetaData,
53+
CoinBalance,
54+
CoinSupply,
5255
} from '../types';
5356
import { DynamicFieldPage } from '../types/dynamic_fields'
5457
import {
@@ -169,6 +172,103 @@ export class JsonRpcProvider extends Provider {
169172
return undefined;
170173
}
171174

175+
async requestSuiFromFaucet(
176+
recipient: SuiAddress,
177+
httpHeaders?: HttpHeaders
178+
): Promise<FaucetResponse> {
179+
if (!this.endpoints.faucet) {
180+
throw new Error('Faucet URL is not specified');
181+
}
182+
return requestSuiFromFaucet(this.endpoints.faucet, recipient, httpHeaders);
183+
}
184+
185+
// Coins
186+
async getCoins(
187+
owner: SuiAddress,
188+
coinType: String | null = null,
189+
cursor: ObjectId | null = null,
190+
limit: number | null = null
191+
) : Promise<PaginatedCoins> {
192+
try {
193+
if (!owner || !isValidSuiAddress(normalizeSuiAddress(owner))) {
194+
throw new Error('Invalid Sui address');
195+
}
196+
return await this.client.requestWithType(
197+
'sui_getCoins',
198+
[owner, coinType, cursor, limit],
199+
PaginatedCoins,
200+
this.options.skipDataValidation
201+
);
202+
} catch (err) {
203+
throw new Error(
204+
`Error getting coins for owner ${owner}: ${err}`
205+
);
206+
}
207+
}
208+
209+
async getAllCoins(
210+
owner: SuiAddress,
211+
cursor: ObjectId | null = null,
212+
limit: number | null = null
213+
) : Promise<PaginatedCoins> {
214+
try {
215+
if (!owner || !isValidSuiAddress(normalizeSuiAddress(owner))) {
216+
throw new Error('Invalid Sui address');
217+
}
218+
return await this.client.requestWithType(
219+
'sui_getAllCoins',
220+
[owner, cursor, limit],
221+
PaginatedCoins,
222+
this.options.skipDataValidation
223+
);
224+
} catch (err) {
225+
throw new Error(
226+
`Error getting all coins for owner ${owner}: ${err}`
227+
)
228+
}
229+
}
230+
231+
async getBalance(
232+
owner: SuiAddress,
233+
coinType: String | null = null,
234+
) : Promise<CoinBalance> {
235+
try {
236+
if (!owner || !isValidSuiAddress(normalizeSuiAddress(owner))) {
237+
throw new Error('Invalid Sui address');
238+
}
239+
return await this.client.requestWithType(
240+
'sui_getBalance',
241+
[owner, coinType],
242+
CoinBalance,
243+
this.options.skipDataValidation
244+
);
245+
} catch (err) {
246+
throw new Error(
247+
`Error getting balance for coin type ${coinType} for owner ${owner}: ${err}`
248+
)
249+
}
250+
}
251+
252+
async getAllBalances(
253+
owner: SuiAddress
254+
) : Promise<CoinBalance[]> {
255+
try {
256+
if (!owner || !isValidSuiAddress(normalizeSuiAddress(owner))) {
257+
throw new Error('Invalid Sui address');
258+
}
259+
return await this.client.requestWithType(
260+
'sui_getAllBalances',
261+
[owner],
262+
array(CoinBalance),
263+
this.options.skipDataValidation
264+
);
265+
} catch (err) {
266+
throw new Error(
267+
`Error getting all balances for owner ${owner}: ${err}`
268+
)
269+
}
270+
}
271+
172272
async getCoinMetadata(coinType: string): Promise<CoinMetadata> {
173273
try {
174274
return await this.client.requestWithType(
@@ -182,14 +282,21 @@ export class JsonRpcProvider extends Provider {
182282
}
183283
}
184284

185-
async requestSuiFromFaucet(
186-
recipient: SuiAddress,
187-
httpHeaders?: HttpHeaders
188-
): Promise<FaucetResponse> {
189-
if (!this.endpoints.faucet) {
190-
throw new Error('Faucet URL is not specified');
285+
async getTotalSupply(
286+
coinType: String
287+
) : Promise<CoinSupply> {
288+
try {
289+
return await this.client.requestWithType(
290+
'sui_getTotalSupply',
291+
[coinType],
292+
CoinSupply,
293+
this.options.skipDataValidation
294+
);
295+
} catch (err) {
296+
throw new Error(
297+
`Error fetching total supply for Coin type ${coinType}: ${err}`
298+
);
191299
}
192-
return requestSuiFromFaucet(this.endpoints.faucet, recipient, httpHeaders);
193300
}
194301

195302
// RPC endpoint
@@ -337,6 +444,9 @@ export class JsonRpcProvider extends Provider {
337444
return objects.filter((obj: SuiObjectInfo) => Coin.isSUI(obj));
338445
}
339446

447+
/**
448+
* @deprecated The method should not be used
449+
*/
340450
async getCoinBalancesOwnedByAddress(
341451
address: SuiAddress,
342452
typeArg?: string
@@ -359,7 +469,9 @@ export class JsonRpcProvider extends Provider {
359469
typeArg: string = SUI_TYPE_ARG,
360470
exclude: ObjectId[] = []
361471
): Promise<GetObjectDataResponse[]> {
362-
const coins = await this.getCoinBalancesOwnedByAddress(address, typeArg);
472+
const coinsStruct = await this.getCoins(address, typeArg);
473+
const coinIds = coinsStruct.data.map((c) => c.coinObjectId);
474+
const coins = await this.getObjectBatch(coinIds);
363475
return (await Coin.selectCoinsWithBalanceGreaterThanOrEqual(
364476
coins,
365477
amount,
@@ -373,7 +485,9 @@ export class JsonRpcProvider extends Provider {
373485
typeArg: string = SUI_TYPE_ARG,
374486
exclude: ObjectId[] = []
375487
): Promise<GetObjectDataResponse[]> {
376-
const coins = await this.getCoinBalancesOwnedByAddress(address, typeArg);
488+
const coinsStruct = await this.getCoins(address, typeArg);
489+
const coinIds = coinsStruct.data.map((c) => c.coinObjectId);
490+
const coins = await this.getObjectBatch(coinIds);
377491
return (await Coin.selectCoinSetWithCombinedBalanceGreaterThanOrEqual(
378492
coins,
379493
amount,

sdk/typescript/src/providers/provider.ts

+61-11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ import {
3535
TransactionEffects,
3636
CoinMetadata,
3737
DevInspectResults,
38+
PaginatedCoins,
39+
CoinBalance,
40+
CoinSupply,
3841
} from '../types';
3942

4043
import { DynamicFieldPage } from '../types/dynamic_fields';
@@ -51,15 +54,6 @@ export abstract class Provider {
5154
*/
5255
abstract getRpcApiVersion(): Promise<RpcApiVersion | undefined>;
5356

54-
/**
55-
* Fetch CoinMetadata for a given coin type
56-
*
57-
* @param coinType fully qualified type names for the coin (e.g.,
58-
* 0x168da5bf1f48dafc111b0a488fa454aca95e0b5e::usdc::USDC)
59-
*
60-
*/
61-
abstract getCoinMetadata(coinType: string): Promise<CoinMetadata>;
62-
6357
// Faucet
6458
/**
6559
* Request gas tokens from a faucet server
@@ -82,6 +76,63 @@ export abstract class Provider {
8276
params: Array<any>
8377
) : Promise<any>;
8478

79+
// Coins
80+
/**
81+
* Get all Coin<`coin_type`> objects owned by an address.
82+
* @param coinType optional fully qualified type names for the coin (e.g., 0x168da5bf1f48dafc111b0a488fa454aca95e0b5e::usdc::USDC), default to 0x2::sui::SUI if not specified.
83+
* @param cursor optional paging cursor
84+
* @param limit maximum number of items per page
85+
*/
86+
abstract getCoins(
87+
owner: SuiAddress,
88+
coinType: String | null,
89+
cursor: ObjectId | null,
90+
limit: number | null
91+
) : Promise<PaginatedCoins>;
92+
93+
/**
94+
* Get all Coin objects owned by an address.
95+
* @param cursor optional paging cursor
96+
* @param limt maximum number of items per page
97+
*/
98+
abstract getAllCoins(
99+
owner: SuiAddress,
100+
cursor: ObjectId | null,
101+
limit: number | null
102+
) : Promise<PaginatedCoins>;
103+
104+
/**
105+
* Get the total coin balance for one coin type, owned by the address owner.
106+
* @param coinType optional fully qualified type names for the coin (e.g., 0x168da5bf1f48dafc111b0a488fa454aca95e0b5e::usdc::USDC), default to 0x2::sui::SUI if not specified.
107+
*/
108+
abstract getBalance(
109+
owner: SuiAddress,
110+
coinType: String | null
111+
) : Promise<CoinBalance>;
112+
113+
/**
114+
* Get the total coin balance for all coin type, owned by the address owner.
115+
*/
116+
abstract getAllBalances(
117+
owner: SuiAddress
118+
) : Promise<CoinBalance[]>;
119+
120+
/**
121+
* Fetch CoinMetadata for a given coin type
122+
* @param coinType fully qualified type names for the coin (e.g.,
123+
* 0x168da5bf1f48dafc111b0a488fa454aca95e0b5e::usdc::USDC)
124+
*
125+
*/
126+
abstract getCoinMetadata(coinType: string): Promise<CoinMetadata>;
127+
128+
/**
129+
* Fetch total supply for a coin
130+
* @param coinType fully qualified type names for the coin (e.g., 0x168da5bf1f48dafc111b0a488fa454aca95e0b5e::usdc::USDC), default to 0x2::sui::SUI if not specified.
131+
*/
132+
abstract getTotalSupply(
133+
coinType: string
134+
) : Promise<CoinSupply>;
135+
85136
// Objects
86137
/**
87138
* Get all objects owned by an address
@@ -98,8 +149,7 @@ export abstract class Provider {
98149
): Promise<SuiObjectInfo[]>;
99150

100151
/**
101-
* Convenience method for getting all coins objects owned by an address
102-
* @param typeArg optional argument for filter by coin type, e.g., '0x2::sui::SUI'
152+
* @deprecated The method should not be used
103153
*/
104154
abstract getCoinBalancesOwnedByAddress(
105155
address: string,

sdk/typescript/src/providers/void-provider.ts

+47-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ import {
3636
TransactionEffects,
3737
CoinMetadata,
3838
DevInspectResults,
39+
PaginatedCoins,
40+
CoinBalance,
41+
CoinSupply,
3942
} from '../types';
4043
import { Provider } from './provider';
4144

@@ -47,10 +50,6 @@ export class VoidProvider extends Provider {
4750
throw this.newError('getRpcApiVersion');
4851
}
4952

50-
getCoinMetadata(_coinType: string): Promise<CoinMetadata> {
51-
throw new Error('getCoinMetadata');
52-
}
53-
5453
// Governance
5554
async getReferenceGasPrice(): Promise<number> {
5655
throw this.newError('getReferenceGasPrice');
@@ -71,6 +70,47 @@ export class VoidProvider extends Provider {
7170
throw this.newError('call');
7271
}
7372

73+
// Coins
74+
async getCoins(
75+
_owner: SuiAddress,
76+
_coinType: String | null,
77+
_cursor: ObjectId | null,
78+
_limit: number | null
79+
) : Promise<PaginatedCoins> {
80+
throw this.newError('getCoins');
81+
}
82+
83+
async getAllCoins(
84+
_owner: SuiAddress,
85+
_cursor: ObjectId | null,
86+
_limit: number | null
87+
) : Promise<PaginatedCoins> {
88+
throw this.newError('getAllCoins');
89+
}
90+
91+
async getBalance(
92+
_owner: string,
93+
_coinType: String | null
94+
): Promise<CoinBalance> {
95+
throw this.newError('getBalance');
96+
}
97+
98+
async getAllBalances(
99+
_owner: string,
100+
): Promise<CoinBalance[]> {
101+
throw this.newError('getAllBalances');
102+
}
103+
104+
async getCoinMetadata(_coinType: string): Promise<CoinMetadata> {
105+
throw new Error('getCoinMetadata');
106+
}
107+
108+
async getTotalSupply(
109+
_coinType: string
110+
) : Promise<CoinSupply> {
111+
throw new Error('getTotalSupply');
112+
}
113+
74114
// Objects
75115
async getObjectsOwnedByAddress(_address: string): Promise<SuiObjectInfo[]> {
76116
throw this.newError('getObjectsOwnedByAddress');
@@ -82,6 +122,9 @@ export class VoidProvider extends Provider {
82122
throw this.newError('getGasObjectsOwnedByAddress');
83123
}
84124

125+
/**
126+
* @deprecated The method should not be used
127+
*/
85128
async getCoinBalancesOwnedByAddress(
86129
_address: string,
87130
_typeArg?: string

0 commit comments

Comments
 (0)