PK Accounts
Imported-key accounts — importPrivateKey() with the vm inferred from the key format (EVM hex, Solana base58, solana-keygen JSON), one secret controlling both chains, and duplicate-import rejection.
A PK account holds individually imported private keys. There is no shared mnemonic — each key becomes its own wallet. A PK account is created by passing a private key as the secret (and, to be explicit, opts.kind: "PK"); that key is auto-imported as wallet 0.
Create from a private key
import { Workspace } from "wative-core";
const EVM_PK = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
const ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });
const acc = await ws.accounts.create("External Hot Wallet", "wsp-pwd", EVM_PK, undefined, {
kind: "PK",
});
acc.organizationType; // "PK"
acc.wallets.length; // 1 — the imported key as wallet 0
acc.wallets[0].addresses[0].vm; // "evm" — the imported key's own chain
acc.wallets[0].addresses[1].vm; // "svm" — the cross-curve siblingImporting more keys
importPrivateKey(pk, vm?) adds one wallet per call. The imported key's own chain is always addresses[0]; the cross-curve sibling, when it exists, is appended after it.
const SVM_PK =
"xtGWcHvQsr5ue8zG2F5fm31bW8vyn597Y92gUWaZZg3S1Z6FeJMATL8KU3xJMGbLfALnokcct9y5wYtCRrwNXZW";
const wallet = await acc.importPrivateKey(SVM_PK, "svm");
acc.wallets.length; // 2
wallet.addresses[0].vm; // "svm"
wallet.addresses[1].vm; // "evm"Signature
importPrivateKey(pk: string, vm?: ChainVM): Promise<Wallet>vm is optional. When omitted, the chain is read off the key's own encoding — the two formats are disjoint. Pass it explicitly to override the inference, or to get a clear rejection when you already know what the key should be.
Accepted key formats
| Format | Example shape | Inferred vm |
|---|---|---|
| EVM hex | 64 hex chars, 0x prefix optional | evm |
| Solana base58 | 64-byte base58 string | svm |
solana-keygen JSON | JSON.stringify([...64 byte values]) | svm |
// EVM hex (0x optional)
const evm = await acc.importPrivateKey(
"0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1",
);
evm.addresses[0].vm; // "evm"
// Solana base58
const svm = await acc.importPrivateKey(SVM_PK_2);
svm.addresses[0].vm; // "svm"
// solana-keygen JSON byte array
const fromKeygen = await acc.importPrivateKey(JSON.stringify(svmKeygenBytes));
fromKeygen.addresses[0].vm; // "svm"One secret, both chains
A single imported key produces addresses on both chains. A secp256k1 private key is 32 bytes and an ed25519 seed is 32 arbitrary bytes, so the same secret material seeds the other curve, and the wallet gets a sibling address on the opposite chain.
This is deliberate cross-curve key reuse and is not what an HD wallet does. On an HD slot the two addresses come from independent BIP-44 paths; here there is one secret, so whoever learns it holds both chains.
The EVM → Solana direction always succeeds. The Solana → EVM direction can, astronomically rarely, fail (secp256k1 only accepts a scalar in range); when the sibling cannot exist the import still succeeds with a single address rather than erroring.
Rejections
| Situation | Error code |
|---|---|
| Secret is not a recognizable private key | INVALID_PRIVATE_KEY |
| An out-of-range key (e.g. all-zero EVM key) | INVALID_PRIVATE_KEY |
| Importing an identity the account already holds | PARAMETER_ERROR |
| Dropping the account's last wallet | UNSUPPORTED_OP |
// Not a key
await acc.importPrivateKey("definitely-not-a-key");
// → WativeError { code: "INVALID_PRIVATE_KEY" }
// Out-of-range EVM key
await acc.importPrivateKey("0x" + "0".repeat(64), "evm");
// → WativeError { code: "INVALID_PRIVATE_KEY" }
// Duplicate identity (already held by this account)
await acc.importPrivateKey(EVM_PK, "evm");
// → WativeError { code: "PARAMETER_ERROR" }The duplicate check covers every address an import would add, including the cross-curve sibling — so a key already present as a sibling on another wallet is rejected too.
Dropping wallets
Remove a wallet with acc.wallets.drop(wallet). An account must always keep at least one wallet, so dropping the last one throws UNSUPPORTED_OP.
await acc.importPrivateKey(SVM_PK, "svm");
await acc.wallets.drop(acc.wallets[1]);
acc.wallets.length; // 1
await acc.wallets.drop(acc.wallets[0]);
// → WativeError { code: "UNSUPPORTED_OP" } (last wallet)For the full account surface — sessions, passwords, default network — see Account Reference.