Skip to main content

Algorand

UKey Wallet Algorand Provider is used to connect Algorand accounts, sign transactions, broadcast transactions and sign messages in web applications. The page is accessible via window.$ukey.algo.

ℹ️

UKey Wallet Algorand Provider supports both traditional interfaces and ARC-0001 compatible interfaces, making it easy to access new and old projects.



Find Provider

// Detect the UKey Wallet Algorand provider
const provider = window.$ukey?.algo;

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

// Verify compatibility
console.log("isUKey:", provider.isUKey); // Reference value: true

Get Going

Connect (legacy API)

const { address } = await provider.connect();
console.log("Connection established:", address);

// Verify connection state
console.log("isConnected:", provider.isConnected);
console.log("Resolved address:", provider.address);

Enable (ARC-0001)

const callResult = await provider.enable({
genesisID: "mainnet-v1.0",
genesisHash: "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
});

console.log("Selected account:", callResult.accounts);
console.log("Network genesis hash:", callResult.genesisHash);
await provider.disconnect();
console.log("Connection closed");

Transfers

Signed transaction (ARC-0001)

The following example constructs a payment transaction, encodes it and gives it to the wallet for signature:

import algosdk from "algosdk";

// Build transaction
const suggestedParams = await algodClient.getTransactionParams().do();

const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
from: senderAddress,
to: recipientAddress,
amount: 1000000, // one ALGO (Micro Algos)
suggestedParams,
});

// Prepare for signing
const walletTxns = [
{
txn: Buffer.from(algosdk.encodeUnsignedTransaction(txn)).toString("base64"),
},
];

// Sign with the wallet
const signedTxns = await provider.signTxns(walletTxns);

console.log("Signed payload:", signedTxns);

Sign multiple transactions

// Group the transactions
const txns = [txn1, txn2, txn3];
algosdk.assignGroupID(txns);

// Prepare for signing
const walletTxns = txns.map((txn) => ({
txn: Buffer.from(algosdk.encodeUnsignedTransaction(txn)).toString("base64"),
}));

// Sign every transaction
const signedTxns = await provider.signTxns(walletTxns);

Partial signature (multiple signatures)

// Sign only selected transactions in the group
const walletTxns = [
{ txn: encodedTxn1 },
{ txn: encodedTxn2, signers: [] }, // Skip this one
{ txn: encodedTxn3 },
];

const signedTxns = await provider.signTxns(walletTxns);
// signedTxns[1] 在这种情况下会是 null

broadcast deal

Broadcast a signed transaction:

const callResult = await provider.postTxns(signedTxns);
console.log("Returned tx IDs:", callResult.txIDs);

Sign and broadcast

Complete signing and broadcasting in one step:

const callResult = await provider.signAndPostTxns(walletTxns);
console.log("Returned tx IDs:", callResult.txIDs);

Traditional trading API

Signature transaction (traditional)

// Build the transaction as Uint8Array
const txnBytes = algosdk.encodeUnsignedTransaction(txn);

// Sign with the wallet
const signedTxnBytes = await provider.signTransaction([txnBytes]);

// submit
const { txId } = await algodClient.sendRawTransaction(signedTxnBytes[0]).do();

Sign and send (traditional)

const callResult = await provider.signAndSendTransaction([txnBytes]);
console.log("Tx ID:", callResult.txId);

Msg Sign

Sign any message

const message = new TextEncoder().encode("Algorand sample message");

const { signature, address } = await provider.signMessage(
message,
"utf8", // 也可以改成 'base64'
);

console.log("Generated signature:", signature);
console.log("Recovered signer:", address);

Verify signature

import nacl from "tweetnacl";

const message = new TextEncoder().encode("Algorand sample message");
const { signature } = await provider.signMessage(message);

// Retrieve public key from address
const publicKey = algosdk.decodeAddress(provider.address).publicKey;

const isValid = nacl.sign.detached.verify(message, signature, publicKey);
console.log("Validation result:", isValid);

Event Flow

Watch Accounts

provider.on("accountChanged", (address) => {
if (address) {
console.log("Active account changed:", address);
} else {
console.log("Connection closed");
}
});

Listen for connections

provider.on("connect", ({ address }) => {
console.log("Connection established:", address);
});

provider.on("disconnect", () => {
console.log("Connection closed");
});

Using the Algod client

Get the client from Provider

// Retrieve a preconfigured client
const algodClient = await provider.getAlgodv2Client();
const indexerClient = await provider.getIndexerClient();

// Use the client
const status = await algodClient.status().do();
console.log("Reported network status:", status);

Manually set up the client

import algosdk from "algosdk";

const algodClient = new algosdk.Algodv2(
"", // 公共节点一般不需要 Token
"https://mainnet-api.algonode.cloud",
"",
);

const indexerClient = new algosdk.Indexer(
"",
"https://mainnet-idx.algonode.cloud",
"",
);

API Notes

Methods

MethodDetails
connect()Connected wallet (legacy)
disconnect()Disconnect wallet
enable(opts?)Enable wallet (ARC-0001)
signTxns(transactions)Signed Transaction (ARC-0001)
postTxns(signedTxns)Broadcast signed transactions
signAndPostTxns(transactions)Sign and broadcast
signTransaction(txns)Sign transaction (traditional)
signAndSendTransaction(txns)Sign and send (traditional)
signMessage(message, encoding?)Sign any message
getAlgodv2Client()Get the Algod client
getIndexerClient()Get Indexer client

Types

interface EnableOpts {
genesisID?: string;
genesisHash?: string;
}

interface EnableResult {
genesisID: string;
genesisHash: string;
accounts: string[];
}

interface WalletTransaction {
txn: string; // Note: Base64 encoded unsigned transaction
signers?: string[]; // addresses that should sign (null means skip)
stxn?: string; // Note: Pre-signed transaction
message?: string; // Note: message displayed
msig?: MultisigMetadata;
authAddr?: string; // Note: Rekeyed auth address
}

interface SignTxnsResult {
// Base64 数组,对应已签名交易(null 表示跳过该项)
[index: number]: string | null;
}

interface PostTxnsResult {
txIDs: string[];
}

interface TransactionResult {
txId: string;
}

property

PropertyTypeDescription
isConnectedbooleanconnection status
addressstring | nullConnected address
isUKeybooleanUKey Wallet logo

Events

EventInputsDetails
connect{ address }Wallet is connected
disconnect-Wallet session closed
accountChangedaddressSelected account changed

Handle Errors

try {
const signedTxns = await provider.signTxns(walletTxns);
} catch (error) {
if (error.code === 4001) {
console.log("The user declined the request");
} else if (error.code === 4100) {
console.log("Wallet is locked");
} else {
console.error("Operation error:", error.message);
}
}

Common error codes

CodeDescription
4001User rejected the request
4100Authorization required
4200Action is unavailable
4300Input value is invalid

Complete example

import algosdk from "algosdk";

async function submitAlgoTransfer() {
// establish connection
const provider = window.$ukey?.algo;
const { address } = await provider.connect();

// Set up the client
const algodClient = new algosdk.Algodv2(
"",
"https://mainnet-api.algonode.cloud",
"",
);

// Retrieve inputs
const suggestedParams = await algodClient.getTransactionParams().do();

// Build transaction
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
from: address,
to: "TARGET_ACCOUNT_ADDRESS",
amount: 1000000, // one ALGO
suggestedParams,
});

// Sign with the wallet
const walletTxns = [
{
txn: Buffer.from(algosdk.encodeUnsignedTransaction(txn)).toString(
"base64",
),
},
];

const signedTxns = await provider.signTxns(walletTxns);

// submit
const { txId } = await algodClient
.sendRawTransaction(Buffer.from(signedTxns[0], "base64"))
.do();

console.log("Tx ID:", txId);

// Wait for confirmation
await algosdk.waitForConfirmation(algodClient, txId, 4);
console.log("Confirmation received!");
}