Cardano
UKey Wallet Cardano Provider is used in web applications to connect to Cardano wallets, read addresses and UTxOs, sign transactions, submit transactions, and sign off-chain data. The page can be accessed through window.cardano.ukey, and a window.cardano.nami compatible entrance is also provided.
UKey Wallet implements the CIP-30 Cardano dApp Connector and provides Nami compatibility to facilitate the migration of existing Cardano DApps.
Fast Links
Find Provider
// UKey Wallet exposes both the ukey and nami interfaces.
const ukey = window.cardano?.ukey;
const nami = window.cardano?.nami;
// Verify availability
if (!ukey) {
throw new Error("UKey Wallet Cardano provider not detected");
}
// Read wallet information
console.log("Wallet name:", ukey.name); // 'UKey Wallet'
console.log("API build:", ukey.apiVersion); // '0.1.0'
console.log("Icon URL:", ukey.icon);
Get Going
Request to enable wallet
// Request wallet permission
const api = await window.cardano.ukey.enable();
// The complete API is now available
const networkId = await api.getNetworkId();
console.log("Active network:", networkId === 1 ? "mainnet" : "testnet");
Check if it is enabled
const isEnabled = await window.cardano.ukey.isEnabled();
if (isEnabled) {
const api = await window.cardano.ukey.enable();
// Continue with the API...
}
Account information
Read network ID
const api = await window.cardano.ukey.enable();
const networkId = await api.getNetworkId();
// 0 代表 testnet,1 代表 mainnet
console.log("Active network:", networkId);
Read balance
const balance = await api.getBalance();
// Returns a CBOR-encoded value (hex string)
console.log("CBOR balance:", balance);
// Decode it with cardano-serialization-lib
import { Value } from "@emurgo/cardano-serialization-lib-browser";
const value = Value.from_bytes(Buffer.from(balance, "hex"));
const lovelace = value.coin().to_str();
console.log("Current balance:", parseInt(lovelace) // 1000000, "ADA");
Read address
// Retrieve used addresses (addresses with transaction history)
const usedAddresses = await api.getUsedAddresses();
console.log("Previously used addresses:", usedAddresses);
// Retrieve unused addresses (fresh addresses)
const unusedAddresses = await api.getUnusedAddresses();
console.log("Fresh addresses:", unusedAddresses);
// Retrieve the change address
const changeAddress = await api.getChangeAddress();
console.log("Change output address:", changeAddress);
// Retrieve reward/staking addresses
const rewardAddresses = await api.getRewardAddresses();
console.log("Reward/staking address:", rewardAddresses);
Read UTxO
// Retrieve all UTxOs
const utxos = await api.getUtxos();
console.log("Fetched UTxOs:", utxos);
// Retrieve UTxOs that satisfy the minimum amount
const utxosWithAmount = await api.getUtxos(
"1000000", // Example minimum: 1 ADA in CBOR form
);
// Retrieve results by page
const paginatedUtxos = await api.getUtxos(undefined, {
page: 0,
limit: 10,
});
Transfers
signature transaction
// transaction must be a CBOR-encoded hexadecimal string
const signedTx = await api.signTx(transactionCbor, partialSign);
// partialSign flag: boolean
// - false: sign every input controlled by the wallet
// - true: only sign inputs explicitly marked as owned (useful for multisig)
console.log("Signed payload:", signedTx);
Submit transaction
// Submit the signed transaction
const txHash = await api.submitTx(signedTransactionCbor);
console.log("Tx hash:", txHash);
Complete transaction process
import {
TransactionBuilder,
TransactionBuilderConfigBuilder,
LinearFee,
BigNum,
Address,
TransactionOutput,
Value,
} from "@emurgo/cardano-serialization-lib-browser";
async function submitAdaTransfer(recipientAddress, amountLovelace) {
const api = await window.cardano.ukey.enable();
// Retrieve UTxOs
const utxos = await api.getUtxos();
const changeAddress = await api.getChangeAddress();
// Build transaction
const txBuilder = TransactionBuilder.new(
TransactionBuilderConfigBuilder.new()
.fee_algo(LinearFee.new(BigNum.from_str("44"), BigNum.from_str("155381")))
.pool_deposit(BigNum.from_str("500000000"))
.key_deposit(BigNum.from_str("2000000"))
.max_value_size(5000)
.max_tx_size(16384)
.coins_per_utxo_byte(BigNum.from_str("4310"))
.build(),
);
// Collect inputs from UTxOs
utxos.forEach((utxo) => {
// Add UTxO entries as inputs...
});
// Append an output
txBuilder.add_output(
TransactionOutput.new(
Address.from_bech32(recipientAddress),
Value.new(BigNum.from_str(amountLovelace)),
),
);
// Set the change address
txBuilder.add_change_if_needed(Address.from_bech32(changeAddress));
// Build transaction
const tx = txBuilder.build_tx();
const txCbor = Buffer.from(tx.to_bytes()).toString("hex");
// Sign with the wallet
const signedTxCbor = await api.signTx(txCbor, false);
// Submit the signed transaction
const txHash = await api.submitTx(signedTxCbor);
console.log("Tx hash:", txHash);
return txHash;
}
Data signature
Signature data (CIP-8)
Data signatures can be used for login authentication, address ownership proof, and off-chain authorization. Include the nonce and application source in the message:
const api = await window.cardano.ukey.enable();
const addresses = await api.getUsedAddresses();
const address = addresses[0];
// Message payload to sign
const payload = Buffer.from("Cardano demo message").toString("hex");
const signature = await api.signData(address, payload);
console.log({
signature: signature.signature, // COSE_Sign1 signature
key: signature.key, // COSE_Key 格式的公钥
});
Verify signature
import { COSESign1, COSEKey } from "@emurgo/cardano-message-signing-browser";
const coseSign1 = COSESign1.from_bytes(Buffer.from(signature.signature, "hex"));
const coseKey = COSEKey.from_bytes(Buffer.from(signature.key, "hex"));
// Retrieve signed payload
const signedPayload = coseSign1.payload();
// Validate signature
const publicKey = coseKey.header(0); // Retrieve the Ed25519 key
// place your validation logic here
Event Flow
Watch Accounts
const api = await window.cardano.ukey.enable();
// Nami-compatible event API
api.experimental.on("accountChange", (addresses) => {
console.log("Active account changed:", addresses);
});
API Notes
Wallet Interface (CIP-30)
| Properties/Method list | description |
|---|---|
name | Wallet name ('UKey Wallet') |
icon | Wallet Icon URL |
apiVersion | API version |
isEnabled() | Check if it is enabled |
enable() | Request wallet access |
dApp API (after enabled)
| Method | Details |
|---|---|
getNetworkId() | Get the Network (0=Testnet, 1=mainnet) |
getBalance() | Get total balance (CBOR) |
getUtxos(amount?, paginate?) | Get UTxOs |
getUsedAddresses() | Get used address |
getUnusedAddresses() | Get unused address |
getChangeAddress() | Get change address |
getRewardAddresses() | Get the pledge address |
signTx(tx, partialSign?) | Sign transaction |
signData(addr, payload) | Signature data (CIP-8) |
submitTx(tx) | Submit transaction |
Experimental API
| Method | Details |
|---|---|
experimental.getCollateral() | Get staked UTxOs |
experimental.on(event, cb) | Subscribe to events |
experimental.off() | Unsubscribe |
Types
type Cbor = string; // CBOR encoded as hex
interface Paginate {
page: number;
limit: number;
}
interface Cip30DataSignature {
signature: string; // COSE_Sign1 in hex
key: string; // COSE_Key in hex
}
type NetworkId = 0 | 1; // 0 means testnet, 1 means mainnet
Events
| Event | Details |
|---|---|
accountChange | Selected account changed |
Handle Errors
try {
const api = await window.cardano.ukey.enable();
} catch (error) {
if (error.code === -1) {
console.log("The user declined the connection");
} else if (error.code === -2) {
console.log("No account was returned");
} else {
console.error("Operation error:", error.message);
}
}
Top Errors
| Code | Description |
|---|---|
| -1 | User rejected the request |
| -2 | Account could not be found |
| -3 | Network selection is invalid |
Use Lucid
Lucid can simplify the transaction construction and submission process:
npm install lucid-cardano
import { Lucid } from "lucid-cardano";
// Initialize Lucid with UKey Wallet
const lucid = await Lucid.new(
new Blockfrost("https://cardano-mainnet.blockfrost.io/api", "demo-api-key"),
"Mainnet",
);
// Open a connection to UKey Wallet
const api = await window.cardano.ukey.enable();
lucid.selectWallet(api);
// Send an ADA transfer
const tx = await lucid
.newTx()
.payToAddress("addr1demoexample...", { lovelace: 5000000n })
.complete();
const signedTx = await tx.sign().complete();
const txHash = await signedTx.submit();
console.log("Tx hash:", txHash);
Use Mesh
Mesh You can also use the browser wallet capability directly:
npm install @meshsdk/core
import { BrowserWallet, Transaction } from "@meshsdk/core";
// Open a connection to UKey Wallet
const wallet = await BrowserWallet.enable("ukey");
// Read wallet information
const balance = await wallet.getBalance();
const addresses = await wallet.getUsedAddresses();
// Note: Build and send transactions
const tx = new Transaction({ initiator: wallet }).sendLovelace(
"addr1demoexample...",
"5000000",
);
const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
Nami compatibility
If you want to be compatible with existing Nami access, you can also use the same set of capabilities through window.cardano.nami:
// Access through the Nami interface
const nami = window.cardano.nami;
const api = await nami.enable();
// Same API surface as UKey Wallet
const balance = await api.getBalance();