@suigar/sdk
v2.0.0-beta.10
Published
TypeScript SDK for Suigar v2 Move contracts on Sui.
Maintainers
Readme
@suigar/sdk
TypeScript SDK for building Suigar v2 game transactions on Sui.
Installation
npm install --save @suigar/sdk @mysten/sui @mysten/bcsRuntime requirements:
- Node.js
>=22 - ESM project configuration (
"type": "module") @mysten/suiv2@mysten/bcsv2
This SDK targets Sui TypeScript SDK 2.0+ only. Follow the official Sui 2.0 migration guide if your app still uses the pre-2.0 client API.
What This Package Exposes
The package ships three public entrypoints:
@suigar/sdkfor the extension factory and runtime client class@suigar/sdk/gamesfor game-specific public types@suigar/sdk/utilsfor public parser, constants, and numeric helpers
The package root exposes the extension factory and client class:
import { suigar, SuigarClient } from '@suigar/sdk';It does not export the individual transaction builders from the package root.
Those stay on the registered extension instance under client.suigar.tx.
Utility exports are available from the utils subpath:
import {
DEFAULT_GAS_BUDGET_MIST,
DEFAULT_LIMBO_MULTIPLIER_SCALE,
DEFAULT_RANGE_SCALE,
fromMoveFloat,
fromMoveI64,
parseCoinType,
parseGameDetails,
RANGE_POINT_LIMIT,
toBigInt,
toU8,
} from '@suigar/sdk/utils';Game-specific type exports are available from the dedicated games subpath:
import type {
BuildCoinflipTransactionOptions,
BuildCreatePvPCoinflipTransactionOptions,
CoinSide,
PvPCoinflipAction,
} from '@suigar/sdk/games';Current game-type subpath exports:
@suigar/sdk/games:CoinSide,PvPCoinflipAction,BuildCoinflipTransactionOptions,BuildLimboTransactionOptions,BuildPlinkoTransactionOptions,BuildRangeTransactionOptions,BuildWheelTransactionOptions,BuildCreatePvPCoinflipTransactionOptions,BuildJoinPvPCoinflipTransactionOptions,BuildCancelPvPCoinflipTransactionOptions
What you actually use at runtime is the registered extension instance:
const client = new SuiGrpcClient({ baseUrl, network }).$extend(suigar());
client.suigar.serializeTransactionToBase64(...);
client.suigar.getConfig();
client.suigar.getPvPCoinflipGames(...);
client.suigar.bcs;
client.suigar.tx;Quick Start
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { suigar } from '@suigar/sdk';
const client = new SuiGrpcClient({
baseUrl: 'https://fullnode.testnet.sui.io:443',
network: 'testnet',
}).$extend(suigar());
const tx = client.suigar.tx.createBetTransaction('coinflip', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
stake: 1_000_000_000n,
side: 'heads',
});
const base64 = await client.suigar.serializeTransactionToBase64(tx);Extension Registration
suigar(options?)
Creates a named Sui client extension. By default, it registers under client.suigar.
Partner Setup
Important:
partneris the partner wallet address. Configure it once when you register the extension so the SDK can prepend that wallet address to supported bet metadata automatically.
const client = new SuiGrpcClient({ baseUrl, network }).$extend(
suigar({ partner: '0xpartner_wallet_address' }),
);
client.suigar;Do not pass a partner slug, label, or display name here. Use the wallet address that should receive partner attribution onchain.
You can rename the extension:
const client = new SuiGrpcClient({ baseUrl, network }).$extend(
suigar({ name: 'games' }),
);
client.games.tx;
client.games.bcs;Config
suigar(options?) resolves config from:
- internal package ids by network
- internal supported coin types by network
- internal price info object ids by network
- the connected client network
- the extension name
Supported override areas:
namepartner
If partner is configured, the SDK automatically writes that partner wallet
address into the onchain metadata vec-map. Transaction builder options may also
include metadata, but reserved keys such as partner and referrer are
ignored with a warning when provided manually.
Runtime Surface
The registered extension instance exposes the main runtime surface:
getConfig()serializeTransactionToBase64(transaction, options?)getPvPCoinflipGames(options?)bcstx
getConfig()
Returns the resolved SDK configuration for the connected network.
This is intended mainly for debugging and inspection, for example to verify the resolved package ids or supported coin mappings for the active client network.
It includes:
packageIdsregistryIdscoinTypespriceInfoObjectIds
const config = client.suigar.getConfig();
console.log(config.packageIds);serializeTransactionToBase64(transaction, options?)
Builds a transaction with the configured Sui client and returns base64-encoded transaction bytes.
Use this when you need a transport-safe payload for a wallet, API, or external signer.
const base64 = await client.suigar.serializeTransactionToBase64(tx);getPvPCoinflipGames(options?)
Lists unresolved PvP coinflip games from the configured PvP registry.
This reads the registry dynamic fields for the active network and resolves each
entry into parsed game state through a bulk client.core.getObjects() lookup.
Registry membership is the unresolved-state signal: once a match is joined and
resolved, the Move flow removes it from the registry and deletes the live
Game object.
Use this when a product needs the current set of open PvP coinflip matches for browsing or lobby views.
By default, per-object fetch or parse failures are skipped so one broken or
already-deleted registry entry does not reject the full lookup. Pass
throwOnError: true if you want the call to reject instead.
Any supported listDynamicFields() options such as limit, cursor, or
signal can be passed through options.
const games = await client.suigar.getPvPCoinflipGames({ limit: 20 });
for (const game of games) {
console.log(game.id);
console.log(game.coinType);
}const games = await client.suigar.getPvPCoinflipGames({
limit: 20,
throwOnError: true,
});tx
Transaction builders live under client.suigar.tx.
Standard Games
Use createBetTransaction(gameId, options) for:
coinfliplimboplinkorangewheel
const tx = client.suigar.tx.createBetTransaction('coinflip', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
stake: 1_000_000_000n,
side: 'tails',
});Shared option shape:
playerAddress: stringcoinType: stringstake: number | bigintcashStake?: number | bigintbetCount?: number | bigintmetadata?: Record<string, string | number | boolean | bigint | Uint8Array | number[] | null | undefined>gasBudget?: number | bigintallowGasCoinShortcut?: boolean
Shared behavior:
stakeis the logical stake passed into the Move callcashStakecontrols the withdrawn balance and defaults tostakebetCountdefaults to1metadatais encoded intokeysandvaluesbyte arrayspartnerconfigured viasuigar({ partner })is prepended automatically to metadata as the partner wallet addressmetadata.partnerandmetadata.referrerare reserved and ignored with a warning- the SDK resolves the price info object from the configured supported-coin mapping
- the reward object is transferred back to
playerAddress
Per-game options:
coinflip:side: 'heads' | 'tails'limbo:targetMultiplier: number,scale?: numberplinko:configId: numberrange:leftPoint: number,rightPoint: number,outOfRange?: boolean,scale?: numberwheel:configId: number
Examples:
const limboTx = client.suigar.tx.createBetTransaction('limbo', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
stake: 1_000_000_000n,
targetMultiplier: 2.5,
});
const rangeTx = client.suigar.tx.createBetTransaction('range', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
stake: 1_000_000_000n,
leftPoint: 25,
rightPoint: 75,
outOfRange: false,
});Note:
- limbo converts
targetMultiplierwithMath.round(targetMultiplier * scale)- with the default limbo scale
100, exposed asDEFAULT_LIMBO_MULTIPLIER_SCALE, a target multiplier of2.5becomes250onchain- range converts each point with
Math.round(value * scale)- range points are bounded by the contract limit exposed as
RANGE_POINT_LIMIT- with the default range scale
1_000_000, exposed asDEFAULT_RANGE_SCALE, valid UI values are0to100- plinko and wheel
configIdmust fit inu8
Tip:
- if you set
scaleto10_000_000, valid UI values become0to10- do not pre-scale range points before passing them to the SDK; pass the human value and let the SDK scale it once
PvP Coinflip
Use createPvPCoinflipTransaction(action, options) for PvP coinflip flows:
createjoincancel
Create:
const tx = client.suigar.tx.createPvPCoinflipTransaction('create', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
stake: 1_000_000_000n,
side: 'heads',
isPrivate: false,
});Join:
const tx = client.suigar.tx.createPvPCoinflipTransaction('join', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
gameId: '0xGAME_ID',
});Cancel:
const tx = client.suigar.tx.createPvPCoinflipTransaction('cancel', {
playerAddress: '0x123',
coinType: '0x2::sui::SUI',
gameId: '0xGAME_ID',
});Join derives the stake from gameId and uses the configured price info object
id for coinType.
PvP shared options:
playerAddress: stringcoinType: stringmetadata?: Record<string, string | number | boolean | bigint | Uint8Array | number[] | null | undefined>gasBudget?: number | bigintallowGasCoinShortcut?: boolean
Action-specific options:
create:stake,side,isPrivate?join:gameIdcancel:gameId
bcs
BCS helpers live under client.suigar.bcs.
Current exposed helpers:
PvPCoinflipGameBetResultEventPvPCoinflipGameCreatedEventPvPCoinflipGameResolvedEventPvPCoinflipGameCancelledEvent
These are generated Move event decoders. Use them to parse Suigar event payloads from transaction results. The @suigar/sdk/utils subpath also exposes parser helpers for generated BCS values:
PvPCoinflipGameparses a PvP coinflip game object'scontentfromMoveI64(float.exp)converts a generated Movei64exponent to a JavaScript numberfromMoveFloat(float)converts a generated MoveFloatstruct to a JavaScript numberparseCoinType(type)extracts the normalized coin type from generic Move object type strings such as PvP coinflipGame<T>parseGameDetails(game_details)decodesBetResultEvent.game_detailsentries into the expected string, number, and boolean values
Parse PvP Coinflip Game Object Data
Use the generated BCS helper when you want to fetch and parse a game object:
const game = await client.suigar.bcs.PvPCoinflipGame.get({
client,
objectId: '0xGAME_ID',
});
console.log(game.json);Parse Standard Bet Result Data
const executeResult = await client.core.executeTransaction({
transaction: transactionBytes,
signatures: [signature],
include: {
events: true,
},
});
const finalResult = await client.core.waitForTransaction({
result: executeResult,
include: {
effects: true,
events: true,
},
});
if (finalResult.$kind === 'FailedTransaction') {
throw new Error(finalResult.FailedTransaction.status.error?.message);
}
console.log(finalResult.Transaction.digest);
const transactionResult = finalResult.Transaction;
const betResults = [];
for (const event of transactionResult.events ?? []) {
try {
const decoded = client.suigar.bcs.BetResultEvent.parse(event.bcs);
betResults.push(decoded);
} catch {
// Ignore non-BetResultEvent payloads.
}
}Parsed fields include:
playercoin_typestake_amountunsafe_oracle_usd_coin_priceadjusted_oracle_usd_coin_priceoutcome_amountgame_detailsmetadata
game_details and metadata decode as VecMap<string, vector<u8>>-shaped data, so values come back as byte arrays. Use parseGameDetails from @suigar/sdk/utils to decode game_details with the SDK's known game-detail schemas.
import { parseGameDetails } from '@suigar/sdk/utils';
const decoded = client.suigar.bcs.BetResultEvent.parse(event.bcs);
const gameDetails = parseGameDetails(decoded.game_details);parseGameDetails preserves the onchain keys and only changes the value representation. For example, coinflip details keep keys such as player_bet and coin_outcome; range details keep keys such as roll_value, win, and payout_multiplier.
When the extension is configured with partner, decoded event metadata will
contain that partner wallet address under the partner entry.
Important:
- execute or wait for the transaction with
include: { events: true }- unwrap the core API union with
result.$kind,result.Transaction, andresult.FailedTransaction- parse emitted events from the unwrapped transaction result
- use
event.bcsfor consistent decoding across transports- use
parseGameDetails(decoded.game_details)instead of hand-decoding standard game detail byte arrays
Tip:
waitForTransaction({ result, include: { effects: true, events: true } })is useful when you want the finalized transaction result before decoding- these helpers decode the event payload itself, not a full transaction response
Parse PvP Coinflip Event Data
Use the matching helper for each PvP coinflip event payload found in transactionResult.events:
client.suigar.bcs.PvPCoinflipGameCreatedEventclient.suigar.bcs.PvPCoinflipGameResolvedEventclient.suigar.bcs.PvPCoinflipGameCancelledEvent
Development
pnpm --dir packages/sdk build
pnpm --dir packages/sdk typecheck
pnpm --dir packages/sdk testExample App
This repository includes a Next.js integration playground in apps/playground.
It demonstrates:
- standard game transactions through
client.suigar.tx.createBetTransaction(...) - PvP coinflip create, join, and cancel flows through
client.suigar.tx.createPvPCoinflipTransaction(...), exposed in the example through a PvP coinflip action selector - unresolved PvP lobby browsing through
client.suigar.getPvPCoinflipGames(...), including public join cards while disconnected, an optional private-lobby join toggle, and connected-wallet filtering for cancel - wallet connection and execution with
@mysten/dapp-kit-coreand@mysten/dapp-kit-react - supported coin selection from
client.suigar.getConfig() - connected-wallet balance display for each supported coin in the example app
- privacy badges and copyable PvP game ids in the lobby UI
- decoding
BetResultEventand PvP events into a persistent event log - parsing
BetResultEvent.game_detailswithparseGameDetails
Run it from the repo root with:
pnpm install
pnpm turbo run dev --filter='./apps/playground'