Skip to content

Commit

Permalink
feat: add MnemonicWallet to allow programmatic signing and broadcas…
Browse files Browse the repository at this point in the history
…ting of txs (#14)

* feat: add working `signArbitrary` imple

* refactor: shift some functions to codec

* refactor: remove custom codec in favour of `@scure/base`

* feat: add working `broadcastTx`

* test: add tests

* docs: add jsdocs

* docs: add warning to readme

* docs: add jsdocs to `ChainInfo`
  • Loading branch information
AaronCQL authored Oct 14, 2023
1 parent 77caba6 commit f27d844
Show file tree
Hide file tree
Showing 35 changed files with 516 additions and 266 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

A tree-shakeable, framework agnostic, [pure ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) alternative of [CosmJS](https://github.com/cosmos/cosmjs) and [Cosmos Kit](https://cosmoskit.com).

> [!WARNING]
> **This is still a work in progress**. Before `v1` is reached, the API is not guaranteed to be semver compatible - patch releases may break everything. See the [changelog](./CHANGELOG.md) for notable changes.
- [Features](#features)
- [Installing](#installing)
- [Using with TypeScript](#using-with-typescript)
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
},
"peerDependencies": {
"@bufbuild/protobuf": "^1.2.0",
"@noble/hashes": "^1.3.2",
"@noble/secp256k1": "^2.0.0",
"@scure/base": "^1.1.3",
"@scure/bip32": "^1.3.2",
"@scure/bip39": "^1.2.1",
"@walletconnect/legacy-client": "^2.0.0",
"@walletconnect/sign-client": "^2.8.0",
"lodash-es": "^4.17.21"
Expand All @@ -40,6 +45,11 @@
"@bufbuild/protoc-gen-es": "^1.2.0",
"@bufbuild/protoplugin": "^1.2.0",
"@keplr-wallet/types": "^0.11.62",
"@noble/hashes": "^1.3.2",
"@noble/secp256k1": "^2.0.0",
"@scure/base": "^1.1.3",
"@scure/bip32": "^1.3.2",
"@scure/bip39": "^1.2.1",
"@types/degit": "^2.8.3",
"@types/lodash-es": "^4.17.7",
"@typescript-eslint/eslint-plugin": "^5.59.5",
Expand Down
49 changes: 49 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/client/apis/broadcastTx.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fromUint8ArrayToBase64 } from "cosmes/codec";
import { base64 } from "cosmes/codec";

import { Prettify } from "../../typeutils/prettify";
import { RpcClient } from "../clients/RpcClient";
Expand All @@ -19,6 +19,6 @@ export async function broadcastTx(
) {
return RpcClient.broadcastTx(
endpoint,
fromUint8ArrayToBase64(tx.toTxRaw(toTxRawParams).toBinary())
base64.encode(tx.toTxRaw(toTxRawParams).toBinary())
);
}
6 changes: 3 additions & 3 deletions src/client/apis/queryContract.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JsonValue } from "@bufbuild/protobuf";
import { fromStringToUint8Array, fromUint8ArrayToString } from "cosmes/codec";
import { utf8 } from "cosmes/codec";
import { CosmwasmWasmV1QuerySmartContractStateService as QuerySmartContractStateService } from "cosmes/protobufs";

import { RpcClient } from "../clients/RpcClient";
Expand All @@ -22,8 +22,8 @@ export async function queryContract<T extends JsonValue>(
QuerySmartContractStateService,
{
address,
queryData: fromStringToUint8Array(JSON.stringify(query)),
queryData: utf8.decode(JSON.stringify(query)),
}
);
return JSON.parse(fromUint8ArrayToString(data));
return JSON.parse(utf8.encode(data));
}
6 changes: 3 additions & 3 deletions src/client/clients/RpcClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Message, PartialMessage } from "@bufbuild/protobuf";
import { fromBase64ToUint8Array, fromUint8ArrayToHex } from "cosmes/codec";
import { base16, base64 } from "cosmes/codec";

import { FetchClient } from "./FetchClient";

Expand Down Expand Up @@ -81,14 +81,14 @@ export class RpcClient {
"abci_query",
{
path: `/${typeName}/${method}`,
data: fromUint8ArrayToHex(new Request(requestMsg).toBinary()),
data: base16.encode(new Request(requestMsg).toBinary()),
}
);
const { log, value } = response;
if (!value) {
throw new Error(log);
}
return Response.fromBinary(fromBase64ToUint8Array(value));
return Response.fromBinary(base64.decode(value));
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/client/models/MsgExecuteContract.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PlainMessage } from "@bufbuild/protobuf";
import { fromStringToUint8Array } from "cosmes/codec";
import { utf8 } from "cosmes/codec";
import { CosmwasmWasmV1MsgExecuteContract as ProtoMsgExecuteContract } from "cosmes/protobufs";

import { DeepPrettify, Prettify } from "../../typeutils/prettify";
Expand All @@ -21,7 +21,7 @@ export class MsgExecuteContract<T> implements Adapter {
public toProto() {
return new ProtoMsgExecuteContract({
...this.data,
msg: fromStringToUint8Array(JSON.stringify(this.data.msg)),
msg: utf8.decode(JSON.stringify(this.data.msg)),
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/client/models/Secp256k1PubKey.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PlainMessage } from "@bufbuild/protobuf";
import { fromUint8ArrayToBase64 } from "cosmes/codec";
import { base64 } from "cosmes/codec";
import { CosmosCryptoSecp256k1PubKey as ProtoSecp256k1PubKey } from "cosmes/protobufs";

import { DeepPrettify } from "../../typeutils/prettify";
Expand All @@ -22,7 +22,7 @@ export class Secp256k1PubKey implements Adapter {
return {
type: "tendermint/PubKeySecp256k1",
value: {
key: fromUint8ArrayToBase64(this.data.key),
key: base64.encode(this.data.key),
},
};
}
Expand Down
30 changes: 30 additions & 0 deletions src/codec/address.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";

import { resolveBech32Address } from "./address";

const PUB_KEY_1 = "A6Y9fcWSn5Av/HLHBwthTaVE/vdyRKvsTzi5U7j9bFj5"; // random pub pub key
const PUB_KEY_2 = "Ag/a1BOl3cdwh67Z8iCbGmAu4WWmBwtuQlQMbDaN385V"; // coinhall.org val pubkey

describe("resolveBech32Address", () => {
it("should resolve stars address correctly", () => {
const translated = resolveBech32Address(PUB_KEY_1, "stars");
expect(translated).toBe("stars14y420auq56p6xgt78sl8vwz3jxy77r9cuw900r");
});

it("should resolve cosmos address correctly", () => {
const translated = resolveBech32Address(PUB_KEY_1, "cosmos");
expect(translated).toBe("cosmos14y420auq56p6xgt78sl8vwz3jxy77r9cgjjjyj");
});

it("should resolve terra address correctly", () => {
const translated = resolveBech32Address(PUB_KEY_2, "terra");
expect(translated).toBe("terra1ge3vqn6cjkk2xkfwpg5ussjwxvahs2f6aytr5j");
});

it("should resolve terravaloper address correctly", () => {
const translated = resolveBech32Address(PUB_KEY_2, "terravaloper");
expect(translated).toBe(
"terravaloper1ge3vqn6cjkk2xkfwpg5ussjwxvahs2f6at87yp"
);
});
});
16 changes: 16 additions & 0 deletions src/codec/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ripemd160 } from "@noble/hashes/ripemd160";
import { sha256 } from "@noble/hashes/sha256";
import { base64, bech32 } from "@scure/base";

/**
* Resolves the bech32 address from the given `publicKey` and `prefix`.
* @param publicKey Must be either a base64 encoded string or a `Uint8Array`.
*/
export function resolveBech32Address(
publicKey: string | Uint8Array,
prefix: string
): string {
const bytes =
typeof publicKey === "string" ? base64.decode(publicKey) : publicKey;
return bech32.encode(prefix, bech32.toWords(ripemd160(sha256(bytes))));
}
Loading

0 comments on commit f27d844

Please sign in to comment.