Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
0db7a4e
Merge pull request #1189 from multiversx/development
popenta Jul 6, 2026
2fbd032
Merge pull request #1196 from multiversx/development
popenta Jul 8, 2026
0fa8ec0
Merge pull request #1198 from multiversx/development
stefangutica Jul 10, 2026
6170455
cookbook design layer: components + swizzle + scoped CSS + llms exten…
lamentierschweinchen Jul 15, 2026
f9792b6
cookbook: TS verification CI mirroring the Rust tutorial CI + 3 exemp…
lamentierschweinchen Jul 15, 2026
fb51ec1
cookbook: port start-here, migration, and wallets recipes (wave 1)
lamentierschweinchen Jul 15, 2026
388ad1b
cookbook: normalize last_validated to CI-verify date (uniform badges)
lamentierschweinchen Jul 15, 2026
ab6a9d4
cookbook: port accounts and signing recipes (wave 2)
lamentierschweinchen Jul 15, 2026
2e3f8ae
cookbook: port tokens, transactions, and governance recipes (wave 3)
lamentierschweinchen Jul 16, 2026
e8c1107
cookbook: port network-providers, delegation, and multisig recipes (w…
lamentierschweinchen Jul 16, 2026
c499b34
cookbook: port smart-contract call, deploy, and Rust authoring recipe…
lamentierschweinchen Jul 16, 2026
596c478
cookbook: front door, agent on-ramp, and machine-surface assembly (pa…
lamentierschweinchen Jul 16, 2026
7122c54
cookbook: restore cross-link graph and surgical em-dashes (assembly p…
lamentierschweinchen Jul 16, 2026
216d176
docs(cookbook): warm the recipe prose, cut self-narrating AI tells
lamentierschweinchen Jul 16, 2026
ef9fe94
cookbook: correctness-review prose fixes (theme/version/https/mkcert …
lamentierschweinchen Jul 16, 2026
a3b2e81
cookbook: replace sdk-js-cookbook-v15 with the new cookbook (redirect…
lamentierschweinchen Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/cookbook-ts-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Cookbook TS verification

on:
push:
branches:
- master
pull_request:

jobs:
cookbook_ts_test:
name: Cookbook TypeScript recipe test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Extract cookbook recipes and type-check
run: ./testing/cookbook-ts-ci.sh
2 changes: 1 addition & 1 deletion docs/developers/relayed-transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,4 @@ Here's an example of a relayed v3 transaction. Its intent is to call the `add` m
The SDKs have built-in support for relayed transactions. Please follow:
- [mxpy support](/sdk-and-tools/mxpy/mxpy-cli/#relayed-transactions-v3)
- [sdk-py support](/sdk-and-tools/sdk-py/#relayed-transactions)
- [sdk-js v15 support](/sdk-and-tools/sdk-js/sdk-js-cookbook#relayed-transactions)
- [sdk-js relayed transactions](/sdk-and-tools/sdk-js/cookbook/accounts/relayed-v3-transaction)
2 changes: 1 addition & 1 deletion docs/developers/sc-calls-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ There are multiple ways of formatting the data field:

- manually convert each argument, and then join the function name, alongside the argument via the `@` character.
- use a pre-defined arguments serializer, such as [the one found in sdk-js](https://github.com/multiversx/mx-sdk-js-core/blob/main/src/smartcontracts/argSerializer.ts).
- use sdk-js's [contract calls](/sdk-and-tools/sdk-js/sdk-js-cookbook/#smart-contracts).
- use sdk-js's [contract calls](/sdk-and-tools/sdk-js/cookbook/smart-contracts-call/call-contract-endpoint).
- use sdk-cpp's [contract calls](https://github.com/multiversx/mx-sdk-cpp/blob/main/src/smartcontracts/contract_call.cpp).
- and so on

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ description: "Sign transactions programmatically using the SDKs: build, sign and

In order to sign a transaction (or a message) using one of the SDKs, follow:

- [Signing objects using **sdk-js**](/sdk-and-tools/sdk-js/sdk-js-cookbook#signing-objects)
- [Signing objects using **sdk-js**](/sdk-and-tools/sdk-js/cookbook/accounts/sign-verify-transaction)
- [Signing objects using **sdk-py**](/sdk-and-tools/sdk-py#signing-objects)
2 changes: 1 addition & 1 deletion docs/sdk-and-tools/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Note that Rust is also the recommended programming language for writing Smart Co
| Name | Description |
| -------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| [sdk-js](/sdk-and-tools/sdk-js) | High level overview about sdk-js. |
| [sdk-js cookbook](/sdk-and-tools/sdk-js/sdk-js-cookbook) | Learn how to handle common tasks by using sdk-js. |
| [sdk-js cookbook](/sdk-and-tools/sdk-js/cookbook) | Learn how to handle common tasks by using sdk-js. |
| [Extending sdk-js](/sdk-and-tools/sdk-js/extending-sdk-js) | How to extend and tailor certain modules of sdk-js. |
| [Writing and testing sdk-js interactions](/sdk-and-tools/sdk-js/writing-and-testing-sdk-js-interactions) | Write sdk-js interactions for Visual Studio Code |
| [sdk-js signing providers](/sdk-and-tools/sdk-js/sdk-js-signing-providers) | Integrate sdk-js signing providers. |
Expand Down
157 changes: 157 additions & 0 deletions docs/sdk-and-tools/sdk-js/cookbook/accounts/account-from-keys.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
title: Create an Account from a KeyPair, secret key, or mnemonic
description: Build an sdk-core Account from a KeyPair, a raw secret key, or a mnemonic, fully offline, and learn which named constructors are sync versus async.
difficulty: beginner
est_minutes: 6
last_validated: "2026-07-16"
sdk_versions:
sdk-core: "^15.4.0"
tags:
- sdk-core
- typescript
- wallet
---

The `Account` is the object you sign with. This recipe builds one three ways:
from a `KeyPair`, from a raw secret key, and from a mnemonic, all fully offline,
with no network call. It also flags a real inconsistency in the named
constructors: one of them is asynchronous while the rest are not.

## Prerequisites

- Node.js >= 20.13.1.
- Nothing else. This recipe generates its own throwaway keys.

## Install

```bash
git clone https://github.com/multiversx/cookbook.git
cd cookbook/recipes/account-from-keys
npm install
npm run build
npm start
```

## The code

```ts title="src/index.ts"
// src/index.ts — build an Account four ways: from a KeyPair, from a raw
// secret key, from a mnemonic, and (for contrast) note the async PEM path.
//
// Fully offline. No devnet, no network call — every constructor here works
// on local key material. Fresh keys are generated on each run, so exact
// addresses differ; the equivalences printed do not.
//
// Grounded in the installed @multiversx/sdk-core v15.4.1 Account class
// (accounts/account.d.ts).

import { Account, KeyPair, Mnemonic } from '@multiversx/sdk-core';

async function main(): Promise<void> {
// === A. From a freshly generated KeyPair. ===
// KeyPair.generate() makes a new Ed25519 keypair; newFromKeypair wraps it
// in an Account. Synchronous.
const keyPair = KeyPair.generate();
const fromKeyPair = Account.newFromKeypair(keyPair);
console.log(`A. From KeyPair: ${fromKeyPair.address.toBech32()}`);

// === B. From a raw secret key — the plain constructor. ===
// We reuse the KeyPair's own secret key, so B is the same account as A and
// the addresses must match. Synchronous.
const fromSecretKey = new Account(keyPair.secretKey);
console.log(`B. From secretKey: ${fromSecretKey.address.toBech32()}`);
console.log(
` A and B share one key, so addresses match: ${fromKeyPair.address.toBech32() === fromSecretKey.address.toBech32()}`,
);

// === C. From a mnemonic at addressIndex 0. ===
// newFromMnemonic derives the key first, then builds the Account.
// Synchronous, despite deriving a key.
const mnemonic = Mnemonic.generate();
const fromMnemonic = Account.newFromMnemonic(mnemonic.toString(), 0);
console.log(`C. From mnemonic: ${fromMnemonic.address.toBech32()}`);

// === D. Every Account exposes the same surface. ===
// address, publicKey, secretKey, and a LOCAL nonce (a bigint you manage
// yourself — see the Manage nonces recipe). An Account can sign, too.
console.log(`\nfromMnemonic.nonce starts at ${fromMnemonic.nonce} (a local bigint, not fetched from the network).`);
const data = new Uint8Array(Buffer.from('proof this account can sign'));
const signature = await fromMnemonic.sign(data);
const verified = await fromMnemonic.verify(data, signature);
console.log(`fromMnemonic can sign and verify its own data: ${verified}`);

// === E. The one async constructor. ===
// newFromPem is the odd one out: it returns Promise<Account> and must be
// awaited. newFromKeypair / new Account(...) / newFromMnemonic /
// newFromKeystore are all synchronous. See the "Save and load a PEM" recipe.
console.log('\nNote: Account.newFromPem(...) is async (returns a Promise); the four constructors used above are synchronous.');

console.log('\nExpected: A and B identical, two "true"-style confirmations.');
}

main().catch((err: unknown) => {
console.error(err);
process.exitCode = 1;
});
```

## Run it

Keys are generated fresh each run, so addresses differ. A and B reuse one key, so
their addresses always match:

```text
A. From KeyPair: erd13cj3sr6vxqknjaegpc3txvjtz3ermc3dvlznhj6lpfzm4wtu69vs2695hk
B. From secretKey: erd13cj3sr6vxqknjaegpc3txvjtz3ermc3dvlznhj6lpfzm4wtu69vs2695hk
A and B share one key, so addresses match: true
C. From mnemonic: erd1uast8l23rlhkee32xx59tmuc54gh8xp5eveayw7ecql9vqxtnqzsmg2yj6

fromMnemonic.nonce starts at 0 (a local bigint, not fetched from the network).
fromMnemonic can sign and verify its own data: true
```

## How it works

Grounded in the installed `@multiversx/sdk-core` v15.4.1 `Account` class
(`accounts/account.d.ts`):

1. **From a KeyPair.** `KeyPair.generate()` makes a new Ed25519 keypair;
`Account.newFromKeypair(keyPair)` wraps it.
2. **From a raw secret key.** `new Account(secretKey)`, the plain constructor.
Reusing the KeyPair's own key makes A and B the same account.
3. **From a mnemonic.** `Account.newFromMnemonic(phrase, addressIndex)` derives
the key first, then builds the Account.
4. **The shared surface.** Every `Account` exposes `address`, `publicKey`,
`secretKey`, and a *local* `nonce` (a `bigint` you manage yourself), and can
`sign` / `verify`.

## Pitfalls

:::warning[Pitfall 1: the named constructors are inconsistent, one is async]
`Account.newFromPem(...)` returns `Promise<Account>` and must be `await`ed. But
`Account.newFromKeypair(...)`, `new Account(...)`, `Account.newFromMnemonic(...)`,
and `Account.newFromKeystore(...)` are all **synchronous**. Forgetting to await
`newFromPem` gives you a `Promise`, not an account. Check the specific
constructor.
:::

:::note[Pitfall 2: account.nonce starts at 0 and is not the on-chain nonce]
It is a local counter you must seed from the network before sending
transactions, see [Manage nonces](/sdk-and-tools/sdk-js/cookbook/accounts/manage-nonces).
Constructing an account does not query the chain.
:::

:::tip[Pitfall 3: same key means same address, always]
The address is a function of the public key alone. If two constructions share
key material (as A and B do here), they are the same account, there is no
per-object identity beyond the key.
:::

## See also

- [Generate a mnemonic + derive keys](/sdk-and-tools/sdk-js/cookbook/accounts/generate-mnemonic-derive-keys)
is where constructor C's key material comes from.
- [Save and load a keystore](/sdk-and-tools/sdk-js/cookbook/accounts/keystore-save-load)
persists an account as encrypted JSON.
- [Save and load a PEM (dev only)](/sdk-and-tools/sdk-js/cookbook/accounts/pem-save-load)
is the async `newFromPem` path in full.
Loading
Loading