@mysten/sui
Read state with a Sui client, build transactions with the Transaction builder, sign and execute through the wallet.
@mysten/sui is Sui's official TypeScript SDK. butr discovers and manages the
wallet; @mysten/sui owns the RPC client and the transaction builder
(Transaction); butr's Sui adapter hands them to the wallet's Wallet
Standard features.
Get the wallet
useSelectedWallet("sui") types the connector as a SuiAdapter, so
sendTx takes a Transaction and signTransaction resolves
{ bytes, signature }:
import { useSelectedWallet } from "@usebutr/react";
const wallet = useSelectedWallet("sui");Read state
The client talks to a Sui full node directly; read balances without routing through the wallet:
import { SuiGrpcClient } from "@mysten/sui/grpc";
const client = new SuiGrpcClient({
baseUrl: "https://fullnode.testnet.sui.io:443",
network: "testnet",
});
const { balance } = await client.getBalance({ owner: wallet.account.walletAddress });
const sui = `${Number(balance.balance) / 1_000_000_000} SUI`; // MIST → SUISign a message
signMessage exists when the wallet advertises sui:signPersonalMessage, so
check it first:
if (!wallet.connector.signMessage) {
throw new Error(`${wallet.connector.name} cannot sign messages`);
}
const message = new TextEncoder().encode("Hello from butr + @mysten/sui");
const { signature, signedMessage } = await wallet.connector.signMessage(message, {
account: wallet.account,
});Build and send a transaction
Build with the Transaction builder and hand the object to sendTx. The Sui
adapter routes it through sui:signAndExecuteTransaction, which signs and
executes, and chain pins testnet for this one call without moving the
wallet:
import { Transaction } from "@mysten/sui/transactions";
import { SUI_CHAINS } from "@usebutr/core";
if (!wallet.connector.sendTx) {
throw new Error(`${wallet.connector.name} cannot execute transactions`);
}
const tx = new Transaction();
tx.setSender(wallet.account.walletAddress);
// A no-op self-transfer: split 0 MIST off the gas coin, send it back.
const [coin] = tx.splitCoins(tx.gas, [0]);
tx.transferObjects([coin], wallet.account.walletAddress);
const digest = await wallet.connector.sendTx(tx, {
account: wallet.account,
chain: SUI_CHAINS.testnet,
});
// digest → look up on https://suiscan.xyz/testnet/tx/<digest>Without chain, the adapter uses its current chain, which is mainnet when the
wallet advertises it.
sendTx resolves the digest, not signed bytes. For a sign-only flow, signTransaction (present
with sui:signTransaction) resolves { bytes, signature }, the two halves
executeTransactionBlock needs.
Other features
For a Wallet Standard feature butr does not wrap, narrow the signer and read
the feature with getFeature, typed by the shapes @usebutr/sui exports:
import type { SuiSignTransactionFeature } from "@usebutr/sui";
import { getFeature } from "@usebutr/wallet-standard-shared";
const signer = await wallet.connector.getSigner();
if (signer.kind === "wallet-standard") {
const feature = getFeature<SuiSignTransactionFeature>(
signer.wallet,
"sui:signTransaction",
"signTransaction",
);
}Source: apps/demo-with-sui/src/app.tsx in the butr
repository. Targets Sui
testnet. Run pnpm dev --filter=demo-with-sui → https://usebutr.demo-with-sui.localhost.