Skip to main content

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.



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 listdescription
nameWallet name ('UKey Wallet')
iconWallet Icon URL
apiVersionAPI version
isEnabled()Check if it is enabled
enable()Request wallet access

dApp API (after enabled)

MethodDetails
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

MethodDetails
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

EventDetails
accountChangeSelected 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

CodeDescription
-1User rejected the request
-2Account could not be found
-3Network 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();