Skip to content

Commit

Permalink
feat: set up docs toc, better init function definitions, clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
jurevans committed Mar 1, 2024
1 parent 1f286ec commit d685e23
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 37 deletions.
109 changes: 107 additions & 2 deletions packages/sdk/docs/specs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,110 @@
# @namada/sdk specs

TODO: Specs for `@namada/sdk` package
The `@namada/sdk` package is a wrapper library to provides an easy way to integrate with the `@namada/shared` WebAssembly Rust library.

[API](./api.md)
View the [API](./api.md) docs for specific information on API interfaces.

## Table of Contents

- [Basic Usage](#basic-usage)
- [Keys](#keys)
- [Rpc](#rpc)
- [Tx](#tx)
- [Ledger](#ledger)
- [Masp](#masp)
- [Mnemonic](#mnemonic)
- [Signing](#signing)

## Basic Usage

The initialization of the `Sdk` must happen asynchronously for web applications.

In your application, you can initialize the Sdk with the following:

```typescript
import { Sdk } from "@namada/sdk";

async function myApp() {
const rpcUrl = "http://localhost:27657";

// You can use deconstruction to access submodules
const { tx, keys, signing, mnemonic, keys } = await Sdk.init(rpcUrl);

// .....
}

myApp();
```

Alternatively, you can import the `initAsync` function, which will also return an instance of the SDK:

```typescript
import { initAsync } from "@namada/sdk";

async function myApp() {
const rpcUrl = "http://localhost:27657";
const sdk = await initAsync(rpcUrl);
}

myApp();
```

[ [Table of Contents](#table-of-contents) ]

## Keys

This module contains the functionality to generate transparent and shielded keys for Namada.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]

## Rpc

This module contains RPC queries for interacting with the chain, as well as Tx broadcasting.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]

## Tx

This module contains functionality for building and signing transactions.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]

## Ledger

This class provides functionality for interacting with `NamadaApp` for the Ledger Hardware Wallet.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]

## Masp

This module provides a few basic utilities for handling `Masp` params, as well as adding spending keys to the
wallet context.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]

## Mnemonic

This provides basic funcitonality for generating 12 or 24 word mnemonics, validating mnemonics, and generating
seeds from mnemonics.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]

## Signing

This class provides the functionality from our Rust library for signing and verifying arbitrary data.

More info _TBD_

[ [Table of Contents](#table-of-contents) ]
13 changes: 10 additions & 3 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Make Ledger available for direct-import as it is not dependent on Sdk initialization
import { init } from "init";
import initAsync from "./initAsync";
import { Sdk as SDK } from "./sdk";

// Make Ledger available for direct-import as it is not dependent on Sdk initialization
export * from "./ledger";

// Wrap Sdk export so initialization can happen outside of
// class definition (for running tests using wasm built for Node JS)
export const Sdk = {
/**
* Initialize Sdk for web applications
*/
init: (url: string, token?: string): Promise<SDK> => {
return init(url, token);
return initAsync(url, token);
},
};

Expand All @@ -24,3 +27,7 @@ export type {
Unbonds,
} from "./rpc";
export { EncodedTx, SignedTx } from "./tx";

// Export init functions for direct usage
export { default as initAsync } from "./initAsync";
export { default as initSync } from "./initSync";
8 changes: 6 additions & 2 deletions packages/sdk/src/init.ts → packages/sdk/src/initAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import { init as initShared } from "@namada/shared/src/init";
import { Sdk } from "sdk";

/**
* Returns an initialized Sdk class
* Returns an initialized Sdk class asynchronously. This is required to use
* this library in web applications.
* @async
* @param {string} url - RPC url for use with SDK
* @param {string} [token] - Native token of the target chain, if not provided, an attempt to query it will be made
* @return {Sdk} Instance of initialized Sdk class
*/
export async function init(url: string, token?: string): Promise<Sdk> {
export default async function initAsync(
url: string,
token?: string
): Promise<Sdk> {
// Load and initialize shared wasm
const sharedWasm = await fetch("shared.namada.wasm").then((wasm) =>
wasm.arrayBuffer()
Expand Down
17 changes: 17 additions & 0 deletions packages/sdk/src/initSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Query as QueryWasm, Sdk as SdkWasm } from "@namada/shared";
import { Sdk } from "./sdk";

/* eslint-disable @typescript-eslint/no-var-requires */
const cryptoMemory = require("@namada/crypto").__wasm.memory;

/**
* Initialize SDK for Node JS environments
* @param {string} url
* @param {string} nativeToken
* @returns {Sdk}
*/
export default function initSync(url: string, nativeToken: string): Sdk {
const sdk = new SdkWasm(url, nativeToken);
const query = new QueryWasm(url);
return new Sdk(sdk, query, cryptoMemory, url, nativeToken);
}
5 changes: 4 additions & 1 deletion packages/sdk/src/keys/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ export class Keys {
* @param {Bip44Path} [path={account: 0, change: 0, index: 0}] - Bip44 path object
* @returns {ShieldedKeys}
*/
deriveShielded(seed: Uint8Array, path: Bip44Path): ShieldedKeys {
deriveShielded(
seed: Uint8Array,
path: Bip44Path = DEFAULT_PATH
): ShieldedKeys {
const { index } = path;
const zip32 = ShieldedHDWallet.from_seed(seed);
const account = zip32.derive_to_serialized_keys(index);
Expand Down
8 changes: 5 additions & 3 deletions packages/sdk/src/tests/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ export const ACCOUNT_2 = {

export const SHIELDED_ACCOUNT = {
paymentAddress:
"znam1qz7te22s3790wf53q7v6tcm5tc79xq5dx2h93us5e8pqeadqn63c6rwx2wq4mgdrjnsgxxgkh45wp",
xsk: "",
viewingKey: "",
"znam1qzs8yweqduxya5tlzh858wgsv8d2j4uu2zvha8hgxzt6lzvs4lu2syaxar4eefey42txpkqd6skd5",
spendingKey:
"zsknam1q9slcrk9qqqqqqps3w3yfgaxsw0t0jkzwm5ghlexqdzhz8vulz9cw03daakult7a7m7ud3ld77ax69r6p9h46qet69yp6sa4p4yjwwemg5twrd09usgqk8284zyq9fxsv3u8v2nz737r770xd0ehahwrcpdnzplaecdj95cdzh8tmp8w72yjcg2t47tzcwnkkhn852ru0z4kt87hdxlt9lulzgkchcqes2fuac87l2h0gczfyef7n7uvq3n43pc3thwgrf543cwgjwcyzjrej",
viewingKey:
"zvknam1q9slcrk9qqqqqqps3w3yfgaxsw0t0jkzwm5ghlexqdzhz8vulz9cw03daakult7a7cjwxdh77a36rcs28lvs7emvl9dhcpz2jg57hvjwrau2g9ssstswuyrxq9tpajhe8uxluhakgevlzqcsg2ccleq5qfqtaqz72kvkxek6zh8tmp8w72yjcg2t47tzcwnkkhn852ru0z4kt87hdxlt9lulzgkchcqes2fuac87l2h0gczfyef7n7uvq3n43pc3thwgrf543cwgjwckhrt6j",
};

export const SIG_VALID = {
Expand Down
13 changes: 4 additions & 9 deletions packages/sdk/src/tests/initSdk.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { Query as QueryWasm, Sdk as SdkWasm } from "@namada/shared";
import initSync from "initSync";
import { Sdk } from "../sdk";
import { NATIVE_TOKEN as nativeToken, RPC_URL as rpcUrl } from "./data";

/* eslint-disable @typescript-eslint/no-var-requires */
const cryptoMemory = require("@namada/crypto").__wasm.memory;

export const initSdk = async (): Promise<Sdk> => {
const sdk = new SdkWasm(rpcUrl, nativeToken);
const query = new QueryWasm(rpcUrl);

return new Sdk(sdk, query, cryptoMemory, rpcUrl, nativeToken);
// Simplified wrapper to handle initializing SDK for tests
export const initSdk = (): Sdk => {
return initSync(rpcUrl, nativeToken);
};
25 changes: 20 additions & 5 deletions packages/sdk/src/tests/keys.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { ACCOUNT_1 as account1, MNEMONIC_1 as mnemonic1 } from "./data";
import {
ACCOUNT_1 as account1,
MNEMONIC_1 as mnemonic1,
SHIELDED_ACCOUNT as shieldedAccount,
} from "./data";
import { initSdk } from "./initSdk";

describe("Keys", () => {
it("should derive keys from mnemonic phrase", async () => {
const { keys } = await initSdk();
it("should derive transparent keys from mnemonic phrase", () => {
const { keys } = initSdk();

const { address, publicKey, privateKey } =
keys.deriveFromMnemonic(mnemonic1);
Expand All @@ -13,8 +17,19 @@ describe("Keys", () => {
expect(privateKey).toBe(account1.privateKey);
});

it("should derive keys from seed", async () => {
const { keys, mnemonic } = await initSdk();
it("should derive shielded keys from seed", () => {
const { keys, mnemonic } = initSdk();
const seed = mnemonic.toSeed(mnemonic1);

const { address, viewingKey, spendingKey } = keys.deriveShielded(seed);

expect(address).toBe(shieldedAccount.paymentAddress);
expect(viewingKey).toBe(shieldedAccount.viewingKey);
expect(spendingKey).toBe(shieldedAccount.spendingKey);
});

it("should derive keys from seed", () => {
const { keys, mnemonic } = initSdk();
// Generate a seed from a mnemonic phrase
const seed = mnemonic.toSeed(mnemonic1);
// Derive account from that seed
Expand Down
12 changes: 10 additions & 2 deletions packages/sdk/src/tests/mnemonic.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { PhraseSize } from "@namada/crypto";
import { MNEMONIC_1 as mnemonic1 } from "./data";
import { initSdk } from "./initSdk";

describe("mnemonic", () => {
it("should generate a 12 word mnemonic phrase", async () => {
const { mnemonic } = await initSdk();
const { mnemonic } = initSdk();
const words = await mnemonic.generate();
expect(words.length).toEqual(12);
});

it("should generate a 24 word mnemonic phrase", async () => {
const { mnemonic } = await initSdk();
const { mnemonic } = initSdk();
const words = await mnemonic.generate(PhraseSize.N24);
expect(words.length).toEqual(24);
});

it("should generate a seed from mnemonic", () => {
const { mnemonic } = initSdk();
const seed = mnemonic.toSeed(mnemonic1);
expect(seed).toBeDefined();
expect(seed.length).toEqual(64);
});
});
4 changes: 2 additions & 2 deletions packages/sdk/src/tests/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { Tx } from "tx";
import { initSdk } from "./initSdk";

describe("Sdk", () => {
it("should initialize Sdk with all sub-components", async () => {
const { tx, keys, mnemonic, rpc, masp, signing } = await initSdk();
it("should initialize Sdk with all sub-components", () => {
const { tx, keys, mnemonic, rpc, masp, signing } = initSdk();
expect(tx).toBeInstanceOf(Tx);
expect(keys).toBeInstanceOf(Keys);
expect(mnemonic).toBeInstanceOf(Mnemonic);
Expand Down
12 changes: 6 additions & 6 deletions packages/sdk/src/tests/signing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
import { initSdk } from "./initSdk";

describe("Signing", () => {
it("should generate a signature", async () => {
const { signing } = await initSdk();
it("should generate a signature", () => {
const { signing } = initSdk();
const result = signing.signArbitrary(
account1.privateKey,
JSON.stringify({ test: "test" })
Expand All @@ -18,8 +18,8 @@ describe("Signing", () => {
expect(signature).toBe(validSignature.signature);
});

it("should validate a signature", async () => {
const { signing } = await initSdk();
it("should validate a signature", () => {
const { signing } = initSdk();

const result = signing.verifyArbitrary(
account1.publicKey,
Expand All @@ -30,8 +30,8 @@ describe("Signing", () => {
expect(result).toBe(null);
});

it("should throw error when validating an invalid signature", async () => {
const { signing } = await initSdk();
it("should throw error when validating an invalid signature", () => {
const { signing } = initSdk();

const verify = (): void =>
signing.verifyArbitrary(
Expand Down
6 changes: 4 additions & 2 deletions packages/sdk/src/tests/tx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ describe("Tx", () => {
afterAll(() => server.stop());
beforeEach(() => server.reset());

// TODO: This isn't working. buildTransfer expects a response when it checks whether
// source & target exist. This may apply to other transactions as well.
it.skip("should build a transfer tx", async () => {
// Mock response for RPC queries that validate that
// source and target exist on chain
Expand All @@ -24,7 +26,7 @@ describe("Tx", () => {
ctx.body = true;
});

const { tx } = await initSdk();
const { tx } = initSdk();

const txProps = {
chainId,
Expand All @@ -48,6 +50,6 @@ describe("Tx", () => {
const txBytes = encodedTx.toBytes();
// TODO: Better test here, this is just a placeholder
expect(txBytes.length).toEqual(1000);
expect(addressExistsRoute).toHaveBeenCalledTimes(1);
expect(addressExistsRoute).toHaveBeenCalledTimes(2);
});
});

0 comments on commit d685e23

Please sign in to comment.