Skip to content

Commit 5e9a6d0

Browse files
feat(sdk-coin-sui): update balance querying to handle fundsInAddressBalance field
TICKET: CSHLD-407
1 parent 5b82c98 commit 5e9a6d0

19 files changed

Lines changed: 979 additions & 96 deletions

modules/sdk-coin-sui/src/lib/iface.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface TxData {
4646
ProgrammableTransaction: SuiProgrammableTransaction;
4747
};
4848
inputObjects?: SuiObjectRef[];
49+
fundsInAddressBalance?: string;
4950
}
5051

5152
export type TransferProgrammableTransaction =
@@ -103,7 +104,9 @@ export interface SuiTransaction<T = SuiProgrammableTransaction> {
103104
sender: string;
104105
tx: T;
105106
gasData: GasData;
107+
expiration?: TransactionExpiration;
106108
inputObjects?: SuiObjectRef[];
109+
fundsInAddressBalance?: string;
107110
}
108111

109112
export interface RequestAddStake {
@@ -172,9 +175,25 @@ export enum MethodNames {
172175
* @see https://github.com/MystenLabs/walrus-docs/blob/9307e66df0ea3f6555cdef78d46aefa62737e216/contracts/walrus/sources/staking/staked_wal.move#L143
173176
*/
174177
WalrusSplitStakedWal = '::staked_wal::split',
178+
/**
179+
* Redeem funds from the address balance system into a Coin<T> object.
180+
* Used with the BalanceWithdrawal CallArg to spend from address balance.
181+
*
182+
* @see https://docs.sui.io/concepts/sui-move-concepts/address-balance
183+
*/
184+
RedeemFunds = '::coin::redeem_funds',
175185
}
176186

177187
export interface SuiObjectInfo extends SuiObjectRef {
178188
/** balance */
179189
balance: BigNumber;
180190
}
191+
192+
export interface SuiBalanceInfo {
193+
/** Total balance combining coin object balance and address balance */
194+
totalBalance: string;
195+
/** Balance held in coin objects (UTXO-style Coin<T> objects) */
196+
coinObjectBalance: string;
197+
/** Balance held in the address balance system (not in coin objects) */
198+
fundsInAddressBalance: string;
199+
}

modules/sdk-coin-sui/src/lib/mystenlab/builder/Inputs.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { array, boolean, Infer, integer, object, string, union } from 'superstruct';
2-
import { normalizeSuiAddress, ObjectId, SharedObjectRef, SuiObjectRef } from '../types';
1+
import { any, array, boolean, Infer, integer, object, string, union } from 'superstruct';
2+
import { normalizeSuiAddress, ObjectId, SharedObjectRef, SuiObjectRef, TypeTag } from '../types';
33
import { builder } from './bcs';
44

55
const ObjectArg = union([
@@ -15,10 +15,17 @@ const ObjectArg = union([
1515

1616
export const PureCallArg = object({ Pure: array(integer()) });
1717
export const ObjectCallArg = object({ Object: ObjectArg });
18+
export const BalanceWithdrawalCallArg = object({
19+
BalanceWithdrawal: object({
20+
amount: any(),
21+
type_: any(),
22+
}),
23+
});
1824
export type PureCallArg = Infer<typeof PureCallArg>;
1925
export type ObjectCallArg = Infer<typeof ObjectCallArg>;
26+
export type BalanceWithdrawalCallArg = Infer<typeof BalanceWithdrawalCallArg>;
2027

21-
export const BuilderCallArg = union([PureCallArg, ObjectCallArg]);
28+
export const BuilderCallArg = union([PureCallArg, ObjectCallArg, BalanceWithdrawalCallArg]);
2229
export type BuilderCallArg = Infer<typeof BuilderCallArg>;
2330

2431
export const Inputs = {
@@ -33,12 +40,27 @@ export const Inputs = {
3340
SharedObjectRef(ref: SharedObjectRef): ObjectCallArg {
3441
return { Object: { Shared: ref } };
3542
},
43+
/**
44+
* Create a BalanceWithdrawal CallArg that withdraws `amount` from the sender's
45+
* address balance at execution time. Use with `0x2::coin::redeem_funds` to
46+
* convert the withdrawal into a `Coin<T>` object.
47+
*
48+
* @param amount - amount in base units (MIST for SUI)
49+
* @param type_ - the TypeTag of the coin (defaults to SUI)
50+
*/
51+
BalanceWithdrawal(amount: bigint | number, type_: TypeTag): BalanceWithdrawalCallArg {
52+
return { BalanceWithdrawal: { amount, type_ } };
53+
},
3654
};
3755

38-
export function getIdFromCallArg(arg: ObjectId | ObjectCallArg): string {
56+
export function getIdFromCallArg(arg: ObjectId | ObjectCallArg | BalanceWithdrawalCallArg): string {
3957
if (typeof arg === 'string') {
4058
return normalizeSuiAddress(arg);
4159
}
60+
if ('BalanceWithdrawal' in arg) {
61+
// BalanceWithdrawal inputs have no object ID; they cannot be deduplicated by ID
62+
return '';
63+
}
4264
if ('ImmOrOwned' in arg.Object) {
4365
return arg.Object.ImmOrOwned.objectId;
4466
}

modules/sdk-coin-sui/src/lib/mystenlab/builder/TransactionBlock.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ObjectId, SuiObjectRef } from '../types';
44
import { Transactions, TransactionArgument, TransactionType, TransactionBlockInput } from './Transactions';
55
import { BuilderCallArg, getIdFromCallArg, Inputs, ObjectCallArg } from './Inputs';
66
import { TransactionBlockDataBuilder, TransactionExpiration } from './TransactionDataBlock';
7+
import { TypeTagSerializer } from '../txn-data-serializers/type-tag-serializer';
78
import { create } from './utils';
89

910
type TransactionResult = TransactionArgument & TransactionArgument[];
@@ -239,6 +240,28 @@ export class TransactionBlock {
239240

240241
// Method shorthands:
241242

243+
/**
244+
* Create a BalanceWithdrawal argument that withdraws `amount` from the sender's
245+
* address balance at execution time. Pass this as an argument to
246+
* `0x2::coin::redeem_funds` to receive a `Coin<T>` object:
247+
*
248+
* ```typescript
249+
* const [coin] = tx.moveCall({
250+
* target: '0x2::coin::redeem_funds',
251+
* typeArguments: ['0x2::sui::SUI'],
252+
* arguments: [tx.withdrawal({ amount: 1_000_000n })],
253+
* });
254+
* tx.transferObjects([coin], recipientAddress);
255+
* ```
256+
*
257+
* @param amount - amount in base units (e.g. MIST for SUI)
258+
* @param type - coin type string, defaults to `'0x2::sui::SUI'`
259+
*/
260+
withdrawal({ amount, type: coinType = '0x2::sui::SUI' }: { amount: bigint | number; type?: string }): TransactionArgument {
261+
const typeTag = TypeTagSerializer.parseFromStr(coinType, true);
262+
return this.input('object', Inputs.BalanceWithdrawal(amount, typeTag));
263+
}
264+
242265
splitCoins(...args: Parameters<(typeof Transactions)['SplitCoins']>) {
243266
return this.add(Transactions.SplitCoins(...args));
244267
}

modules/sdk-coin-sui/src/lib/mystenlab/builder/TransactionDataBlock.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ import { BuilderCallArg, PureCallArg } from './Inputs';
2121
import { create } from './utils';
2222

2323
export const TransactionExpiration = optional(
24-
nullable(union([object({ Epoch: integer() }), object({ None: union([literal(true), literal(null)]) })]))
24+
nullable(
25+
union([
26+
object({ Epoch: integer() }),
27+
object({ None: union([literal(true), literal(null)]) }),
28+
object({ ValidDuring: object({ minEpoch: integer(), maxEpoch: integer(), chain: string(), nonce: integer() }) }),
29+
])
30+
)
2531
);
2632
export type TransactionExpiration = Infer<typeof TransactionExpiration>;
2733

modules/sdk-coin-sui/src/lib/mystenlab/types/coin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const CoinBalance = object({
2525
coinType: string(),
2626
coinObjectCount: number(),
2727
totalBalance: number(),
28+
fundsInAddressBalance: optional(number()),
2829
lockedBalance: object({
2930
epochId: optional(number()),
3031
number: optional(number()),

modules/sdk-coin-sui/src/lib/mystenlab/types/sui-bcs.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function isPureArg(arg: any): arg is PureArg {
6666
* For `Pure` arguments BCS is required. You must encode the values with BCS according
6767
* to the type required by the called function. Pure accepts only serialized values
6868
*/
69-
export type CallArg = PureArg | { Object: ObjectArg };
69+
export type CallArg = PureArg | { Object: ObjectArg } | { BalanceWithdrawal: { amount: bigint | number; type_: TypeTag } };
7070

7171
/**
7272
* Kind of a TypeTag which is represented by a Move type identifier.
@@ -106,12 +106,24 @@ export type GasData = {
106106
budget: number;
107107
};
108108

109+
/**
110+
* ValidDuring expiration — used when gasData.payment is empty (address-balance-funded gas).
111+
* Both minEpoch and maxEpoch must be set; maxEpoch must equal minEpoch or minEpoch + 1.
112+
* The nonce (u32) prevents duplicate transaction digests across same-epoch builds.
113+
*/
114+
export type ValidDuringExpiration = {
115+
minEpoch: number;
116+
maxEpoch: number;
117+
chain: string;
118+
nonce: number;
119+
};
120+
109121
/**
110122
* TransactionExpiration
111123
*
112124
* Indications the expiration time for a transaction.
113125
*/
114-
export type TransactionExpiration = { None: null } | { Epoch: number };
126+
export type TransactionExpiration = { None: null } | { Epoch: number } | { ValidDuring: ValidDuringExpiration };
115127

116128
// Move name of the Vector type.
117129
const VECTOR = 'vector';
@@ -144,6 +156,7 @@ const BCS_SPEC: TypeSchema = {
144156
Pure: [VECTOR, BCS.U8],
145157
Object: 'ObjectArg',
146158
ObjVec: [VECTOR, 'ObjectArg'],
159+
BalanceWithdrawal: 'BalanceWithdrawal',
147160
},
148161
TypeTag: {
149162
bool: null,
@@ -169,12 +182,23 @@ const BCS_SPEC: TypeSchema = {
169182
TransactionExpiration: {
170183
None: null,
171184
Epoch: BCS.U64,
185+
ValidDuring: 'ValidDuringExpiration',
172186
},
173187
TransactionData: {
174188
V1: 'TransactionDataV1',
175189
},
176190
},
177191
structs: {
192+
BalanceWithdrawal: {
193+
amount: BCS.U64,
194+
type_: 'TypeTag',
195+
},
196+
ValidDuringExpiration: {
197+
minEpoch: BCS.U64,
198+
maxEpoch: BCS.U64,
199+
chain: BCS.STRING,
200+
nonce: BCS.U32,
201+
},
178202
SuiObjectRef: {
179203
objectId: BCS.ADDRESS,
180204
version: BCS.U64,

0 commit comments

Comments
 (0)