# Keys

To perform actions using an account with Terra.js, you need a Key, which provides an abstraction around signing functions of an account.

# Key interface

A Key provides the following interface:

interface Key {
  publicKey: Buffer;
  accAddress: AccAddress;
  valAddress: ValAddress;
  accPubKey: AccPubKey;
  valPubKey: ValPubKey;

  createSignature(tx: StdSignMsg): StdSignature;
  signTx(tx: StdSignMsg): Promise<StdTx>;
  sign(payload: Buffer): Promise<Buffer>;
}

# Key implementations

Terra.js provides several standard Key implementations that provide a variety of ways to load an account with signing features into your program.

# RawKey

The most basic implementation of Key is RawKey, which is created with a plain private key.

import { RawKey } from '@terra-money/terra.js';

const rk = new RawKey("<private key>");

The private key associated with the RawKey is available through the instance:

console.log(rk.privateKey);

# MnemonicKey

import { MnemonicKey } from '@terra-money/terra.js';

const mk = new MnemonicKey({
  mnemonic: "<24-word mnemonic>",
});

# Generate random mnemonic

If you want to generate a random mnemonic, you can create a MnemonicKey without any arguments:

const mk = new MnemonicKey();
console.log(mk.mnemonic);

# Specifying HD path

MnemonicKey can used to recover a wallet with a particular BIP44 HD path: m/44'/${coinType}'/${account}'/0/${index}.

const mk = new MnemonicKey({
  mnemonic: "<seed-phrase>", // optional, will be random if not provided
  coinType: 330, // optional, default
  account: 0, // optional, default
  index: 0, // optional, default
});

For example, to recover a mnemonic with the old Terra wallet HD path using coin type for ATOM (118):

const mk = new MnemonicKey({
  mnemonic: "<seed-phrase>",
  coinType: 118
});

# CLIKey

NOTE: This requires you to have terrad installed.

If you want to use keys stored in your terrad installation's keyring to sign transactions, you can use CLIKey. This also will work for keys that have been registered in your keyring with --ledger, using a Ledger hardware device.

import { StdFee, MsgSend } from '@terra-money/terra.js';
import { LocalTerra } from '@terra-money/terra.js';
import { CLIKey } from '@terra-money/terra.js';

const terra = new LocalTerra();
const { test1 } = terra.wallets;
const cliKey = new CLIKey('test111');
const cliWallet = terra.wallet(cliKey);

const send = new MsgSend(cliWallet.key.accAddress, test1.key.accAddress, {
  uluna: 100000,
});

async function main() {
  const tx = await cliWallet.createAndSignTx({
    msgs: [send],
    fee: new StdFee(100000, { uluna: 100000 }),
  });

  console.log(await terra.tx.broadcast(tx));
}

main().catch(console.error);

# Custom key implementation

If you need to write your own key management solution, you will need to subclass the abstract Key class and provide your own signing function. Note that the key need not expose any details pertaining to the private key -- you could specify a sign() function that forwards the signing request to a server or to a hardware wallet, for instance. The remaining functions related to signing (createSignature() and signTx()) are automatically provided and use sign() underneath.

The following code listing is the implementation of RawKey, which illustrates how to write a custom Key:

import SHA256 from 'crypto-js/sha256';
import * as secp256k1 from 'secp256k1';
import { Key } from '@terra-money/terra.js';

/**
 * An implementation of the Key interfaces that uses a raw private key.
 */
export class RawKey extends Key {
  /**
   * Raw private key, in bytes.
   */
  public privateKey: Buffer;

  constructor(privateKey: Buffer) {
    const publicKey = secp256k1.publicKeyCreate(
      new Uint8Array(privateKey),
      true
    );
    super(Buffer.from(publicKey));
    this.privateKey = privateKey;
  }

  public sign(payload: Buffer): Promise<Buffer> {
    const hash = Buffer.from(SHA256(payload.toString()).toString(), 'hex');
    const { signature } = secp256k1.ecdsaSign(
      Uint8Array.from(hash),
      Uint8Array.from(this.privateKey)
    );
    return Buffer.from(signature);
  }
}

Note that you must call super() with the public key—this generates the relevant account and validator public keys associated with your key.