Skip to main content

Sui

UKey Wallet Sui Provider is used in web applications to connect Sui accounts, read the current chain, sign messages, and sign and execute transactions. The page is accessible via window.$ukey.sui.

ℹ️

UKey Wallet implements Sui Wallet Standard and can be automatically recognized by wallet tools and DApps that are compatible with this standard.



Find Provider

// Detect the UKey Wallet Sui provider
const provider = window.$ukey?.sui;

if (!provider) {
throw new Error("UKey Wallet Sui provider not detected");
}

Get Going

Check permissions

// Check whether it is connected
const hasPermission = await provider.hasPermissions();

if (hasPermission) {
const accounts = await provider.getAccounts();
console.log("Authorized account:", accounts);
}

Request authorized connection

// Ask for connection permission
const callResult = await provider.requestPermissions();

if (callResult) {
const accounts = await provider.getAccounts();
console.log("Connection established:", accounts[0].address);
}
await provider.disconnect();

Account management

Read wallet

const accounts = await provider.getAccounts();

accounts.forEach((account) => {
console.log({
address: account.address, // Note: Address
publicKey: account.publicKey, // 十六进制形式的公钥
});
});

Read the current chain

const chain = await provider.getActiveChain();

console.log("Current network key:", chain); // For instance 'sui:testnet'

Transfers

Sign and execute transaction blocks

import { TransactionBlock } from "@mysten/sui.js/transactions";

// Create the transaction block
const txb = new TransactionBlock();

// Add a transfer action
txb.transferObjects(
[txb.object("0xdemo_object_id")],
txb.pure("0xdemo_recipient_address"),
);

// Or send SUI directly
const [coin] = txb.splitCoins(txb.gas, [txb.pure(1000000000)]); // one SUI
txb.transferObjects([coin], txb.pure("0xdemo_recipient_address"));

// Sign and execute it
const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const callResult = await provider.signAndExecuteTransactionBlock({
account,
chain,
transactionBlock: txb,
options: {
showEffects: true,
showEvents: true,
showObjectChanges: true,
},
});

console.log({
digest: callResult.digest,
effects: callResult.effects,
events: callResult.events,
});

Sign transaction blocks only

const txb = new TransactionBlock();
// 其余逻辑按需补充 assemble the transaction details here

const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const signedTxb = await provider.signTransactionBlock({
account,
chain,
transactionBlock: txb,
});

console.log({
transactionBlockBytes: signedTxb.transactionBlockBytes,
signature: signedTxb.signature,
});

// Execute it later with the Sui SDK
// Example call: const callResult = await suiClient.executeTransactionBlock({
// transactionBlock: signedTxb.transactionBlockBytes, // reuse the signed bytes
// signature: signedTxb.signature, // pass along the returned signature
// }) // run this when you are ready to submit

Sign and execute transactions (new API)

// Prefer the newer signAndExecuteTransaction API here (using Transaction type)
import { Transaction } from "@mysten/sui.js/transactions";

const tx = new Transaction();
// 其余逻辑按需补充 assemble the transaction details here

const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const callResult = await provider.signAndExecuteTransaction({
transaction: tx,
account,
chain,
options: {
showEffects: true,
},
});

Sign transactions only (new API)

import { Transaction } from "@mysten/sui.js/transactions";

const tx = new Transaction();
// 其余逻辑按需补充 assemble the transaction details here

const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const signedTxn = await provider.signTransaction({
transaction: tx,
account,
chain,
});

Msg Sign

Sign personal message

Message signatures can be used for login authentication, address ownership proof, and off-chain authorization. Add nonce and application source:

const message = new TextEncoder().encode("Sui sample payload");
const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const callResult = await provider.signPersonalMessage({
message,
account,
chain,
});

console.log({
signature: callResult.signature, // Note: Base64 encoded signature
bytes: callResult.bytes, // Note: Signed message bytes
});

Signed message (old version)

const message = new TextEncoder().encode("Sui sample payload");
const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const callResult = await provider.signMessage({
message,
account,
chain,
});

console.log({
signature: callResult.signature,
messageBytes: callResult.messageBytes,
});

Verify signature

import { verifyPersonalMessage } from "@mysten/sui.js/verify";

const message = new TextEncoder().encode("Sui sample payload");
const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();
const { signature } = await provider.signPersonalMessage({
message,
account,
chain,
});

const accounts = await provider.getAccounts();
const publicKey = accounts[0].publicKey;

const isValid = await verifyPersonalMessage(message, signature);
console.log("Signature check passed:", isValid);

Smart contract interaction

Move call

const txb = new TransactionBlock();

// Invoke the Move function
txb.moveCall({
target: "0xpackage::module::function",
arguments: [txb.pure("arg1"), txb.object("0xdemo_object_id")],
typeArguments: ["0x2::sui::SUI"],
});

const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const callResult = await provider.signAndExecuteTransactionBlock({
account,
chain,
transactionBlock: txb,
});

release package

const txb = new TransactionBlock();

const [upgradeCap] = txb.publish({
modules: compiledModules,
dependencies: [
"0x1", // Note: Move stdlib
"0x2", // Note: Sui framework
],
});

txb.transferObjects([upgradeCap], txb.pure(senderAddress));

const [account] = await provider.getAccounts();
const chain = await provider.getActiveChain();

const callResult = await provider.signAndExecuteTransactionBlock({
account,
chain,
transactionBlock: txb,
});

API Notes

Methods

MethodDetails
hasPermissions()Check if connected
requestPermissions()Request connection
disconnect()Disconnect wallet
getAccounts()Get connected accounts (return address, publicKey)
getActiveChain()Get the current chain
signAndExecuteTransactionBlock(input)To sign and execute the transaction, you need to pass in account/chain
signTransactionBlock(input)Only Sign transactions need to be passed in account/chain
signAndExecuteTransaction(input)Sign and execute (new API, uses Transaction, requires account/chain)
signTransaction(input)Signature transaction (new API, uses Transaction, requires account/chain)
signMessage(input)Signed message (old version, requires account/chain)
signPersonalMessage(input)Signed personal message (requires account/chain)

Types

interface WalletAccount {
address: string;
publicKey: string;
}

interface SignAndExecuteTransactionBlockInput {
account: WalletAccount;
chain?: string;
transactionBlock: TransactionBlock;
options?: {
showEffects?: boolean;
showEvents?: boolean;
showObjectChanges?: boolean;
showBalanceChanges?: boolean;
showInput?: boolean;
showRawInput?: boolean;
};
}

interface SignTransactionBlockOutput {
transactionBlockBytes: string;
signature: string;
}

interface SignPersonalMessageInput {
account: WalletAccount;
chain?: string;
message: Uint8Array;
}

interface SignPersonalMessageOutput {
signature: string;
bytes: string;
}

Chain List

NetworkIdentifier
Testnetsui:testnet
Devnetsui:devnet

Handle Errors

try {
await provider.requestPermissions();
} catch (error) {
if (error.code === 4001) {
console.log("The connection request was declined");
} else {
console.error("Connection issue:", error.message);
}
}

Common error codes

CodeDescription
4001User rejected the request
4100Authorization required
-32603Provider internal failure

Using Sui Wallet Kit

React projects can use Sui Wallet Kit. UKey Wallet will appear in the list of optional wallets through Wallet Standard:

npm install @mysten/dapp-kit @mysten/sui.js @tanstack/react-query
import { SuiClientProvider, WalletProvider } from "@mysten/dapp-kit";
import { getFullnodeUrl } from "@mysten/sui.js/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();
const networks = {
mainnet: { url: getFullnodeUrl("mainnet") },
testnet: { url: getFullnodeUrl("testnet") },
};

function SuiWalletShell() {
return (
<QueryClientProvider client={queryClient}>
<SuiClientProvider networks={networks} defaultNetwork="mainnet">
<WalletProvider>
<YourApp />
</WalletProvider>
</SuiClientProvider>
</QueryClientProvider>
);
}

UKey Wallet is automatically detected via Sui Wallet Standard.


Using Sui SDK

import { SuiClient } from "@mysten/sui.js/client";

const client = new SuiClient({ url: "https://fullnode.mainnet.sui.io" });

// Retrieve balance
const balance = await client.getBalance({
owner: accountAddress,
});

// Retrieve object
const objects = await client.getOwnedObjects({
owner: accountAddress,
});

// Retrieve transaction
const txn = await client.getTransactionBlock({
digest: txDigest,
options: { showEffects: true },
});