Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions modules/sdk-coin-sui/src/lib/iface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface TxData {
ProgrammableTransaction: SuiProgrammableTransaction;
};
inputObjects?: SuiObjectRef[];
fundsInAddressBalance?: string;
}

export type TransferProgrammableTransaction =
Expand Down Expand Up @@ -103,7 +104,9 @@ export interface SuiTransaction<T = SuiProgrammableTransaction> {
sender: string;
tx: T;
gasData: GasData;
expiration?: TransactionExpiration;
inputObjects?: SuiObjectRef[];
fundsInAddressBalance?: string;
}

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

export interface SuiObjectInfo extends SuiObjectRef {
/** balance */
balance: BigNumber;
}

export interface SuiBalanceInfo {
/** Total balance combining coin object balance and address balance */
totalBalance: string;
/** Balance held in coin objects (UTXO-style Coin<T> objects) */
coinObjectBalance: string;
/** Balance held in the address balance system (not in coin objects) */
fundsInAddressBalance: string;
}
30 changes: 26 additions & 4 deletions modules/sdk-coin-sui/src/lib/mystenlab/builder/Inputs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { array, boolean, Infer, integer, object, string, union } from 'superstruct';
import { normalizeSuiAddress, ObjectId, SharedObjectRef, SuiObjectRef } from '../types';
import { any, array, boolean, Infer, integer, object, string, union } from 'superstruct';
import { normalizeSuiAddress, ObjectId, SharedObjectRef, SuiObjectRef, TypeTag } from '../types';
import { builder } from './bcs';

const ObjectArg = union([
Expand All @@ -15,10 +15,17 @@ const ObjectArg = union([

export const PureCallArg = object({ Pure: array(integer()) });
export const ObjectCallArg = object({ Object: ObjectArg });
export const BalanceWithdrawalCallArg = object({
BalanceWithdrawal: object({
amount: any(),
type_: any(),
}),
});
export type PureCallArg = Infer<typeof PureCallArg>;
export type ObjectCallArg = Infer<typeof ObjectCallArg>;
export type BalanceWithdrawalCallArg = Infer<typeof BalanceWithdrawalCallArg>;

export const BuilderCallArg = union([PureCallArg, ObjectCallArg]);
export const BuilderCallArg = union([PureCallArg, ObjectCallArg, BalanceWithdrawalCallArg]);
export type BuilderCallArg = Infer<typeof BuilderCallArg>;

export const Inputs = {
Expand All @@ -33,12 +40,27 @@ export const Inputs = {
SharedObjectRef(ref: SharedObjectRef): ObjectCallArg {
return { Object: { Shared: ref } };
},
/**
* Create a BalanceWithdrawal CallArg that withdraws `amount` from the sender's
* address balance at execution time. Use with `0x2::coin::redeem_funds` to
* convert the withdrawal into a `Coin<T>` object.
*
* @param amount - amount in base units (MIST for SUI)
* @param type_ - the TypeTag of the coin (defaults to SUI)
*/
BalanceWithdrawal(amount: bigint | number, type_: TypeTag): BalanceWithdrawalCallArg {
return { BalanceWithdrawal: { amount, type_ } };
},
};

export function getIdFromCallArg(arg: ObjectId | ObjectCallArg): string {
export function getIdFromCallArg(arg: ObjectId | ObjectCallArg | BalanceWithdrawalCallArg): string {
if (typeof arg === 'string') {
return normalizeSuiAddress(arg);
}
if ('BalanceWithdrawal' in arg) {
// BalanceWithdrawal inputs have no object ID; they cannot be deduplicated by ID
return '';
}
if ('ImmOrOwned' in arg.Object) {
return arg.Object.ImmOrOwned.objectId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ObjectId, SuiObjectRef } from '../types';
import { Transactions, TransactionArgument, TransactionType, TransactionBlockInput } from './Transactions';
import { BuilderCallArg, getIdFromCallArg, Inputs, ObjectCallArg } from './Inputs';
import { TransactionBlockDataBuilder, TransactionExpiration } from './TransactionDataBlock';
import { TypeTagSerializer } from '../txn-data-serializers/type-tag-serializer';
import { create } from './utils';

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

// Method shorthands:

/**
* Create a BalanceWithdrawal argument that withdraws `amount` from the sender's
* address balance at execution time. Pass this as an argument to
* `0x2::coin::redeem_funds` to receive a `Coin<T>` object:
*
* ```typescript
* const [coin] = tx.moveCall({
* target: '0x2::coin::redeem_funds',
* typeArguments: ['0x2::sui::SUI'],
* arguments: [tx.withdrawal({ amount: 1_000_000n })],
* });
* tx.transferObjects([coin], recipientAddress);
* ```
*
* @param amount - amount in base units (e.g. MIST for SUI)
* @param type - coin type string, defaults to `'0x2::sui::SUI'`
*/
withdrawal({ amount, type: coinType = '0x2::sui::SUI' }: { amount: bigint | number; type?: string }): TransactionArgument {
const typeTag = TypeTagSerializer.parseFromStr(coinType, true);
return this.input('object', Inputs.BalanceWithdrawal(amount, typeTag));
}

splitCoins(...args: Parameters<(typeof Transactions)['SplitCoins']>) {
return this.add(Transactions.SplitCoins(...args));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import { BuilderCallArg, PureCallArg } from './Inputs';
import { create } from './utils';

export const TransactionExpiration = optional(
nullable(union([object({ Epoch: integer() }), object({ None: union([literal(true), literal(null)]) })]))
nullable(
union([
object({ Epoch: integer() }),
object({ None: union([literal(true), literal(null)]) }),
object({ ValidDuring: object({ minEpoch: integer(), maxEpoch: integer(), chain: string(), nonce: integer() }) }),
])
)
);
export type TransactionExpiration = Infer<typeof TransactionExpiration>;

Expand Down
1 change: 1 addition & 0 deletions modules/sdk-coin-sui/src/lib/mystenlab/types/coin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const CoinBalance = object({
coinType: string(),
coinObjectCount: number(),
totalBalance: number(),
fundsInAddressBalance: optional(number()),
lockedBalance: object({
epochId: optional(number()),
number: optional(number()),
Expand Down
28 changes: 26 additions & 2 deletions modules/sdk-coin-sui/src/lib/mystenlab/types/sui-bcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function isPureArg(arg: any): arg is PureArg {
* For `Pure` arguments BCS is required. You must encode the values with BCS according
* to the type required by the called function. Pure accepts only serialized values
*/
export type CallArg = PureArg | { Object: ObjectArg };
export type CallArg = PureArg | { Object: ObjectArg } | { BalanceWithdrawal: { amount: bigint | number; type_: TypeTag } };

/**
* Kind of a TypeTag which is represented by a Move type identifier.
Expand Down Expand Up @@ -106,12 +106,24 @@ export type GasData = {
budget: number;
};

/**
* ValidDuring expiration — used when gasData.payment is empty (address-balance-funded gas).
* Both minEpoch and maxEpoch must be set; maxEpoch must equal minEpoch or minEpoch + 1.
* The nonce (u32) prevents duplicate transaction digests across same-epoch builds.
*/
export type ValidDuringExpiration = {
minEpoch: number;
maxEpoch: number;
chain: string;
nonce: number;
};

/**
* TransactionExpiration
*
* Indications the expiration time for a transaction.
*/
export type TransactionExpiration = { None: null } | { Epoch: number };
export type TransactionExpiration = { None: null } | { Epoch: number } | { ValidDuring: ValidDuringExpiration };

// Move name of the Vector type.
const VECTOR = 'vector';
Expand Down Expand Up @@ -144,6 +156,7 @@ const BCS_SPEC: TypeSchema = {
Pure: [VECTOR, BCS.U8],
Object: 'ObjectArg',
ObjVec: [VECTOR, 'ObjectArg'],
BalanceWithdrawal: 'BalanceWithdrawal',
},
TypeTag: {
bool: null,
Expand All @@ -169,12 +182,23 @@ const BCS_SPEC: TypeSchema = {
TransactionExpiration: {
None: null,
Epoch: BCS.U64,
ValidDuring: 'ValidDuringExpiration',
},
TransactionData: {
V1: 'TransactionDataV1',
},
},
structs: {
BalanceWithdrawal: {
amount: BCS.U64,
type_: 'TypeTag',
},
ValidDuringExpiration: {
minEpoch: BCS.U64,
maxEpoch: BCS.U64,
chain: BCS.STRING,
nonce: BCS.U32,
},
SuiObjectRef: {
objectId: BCS.ADDRESS,
version: BCS.U64,
Expand Down
Loading
Loading