From f0baf302ce03b6b6f1678a945daec2e9b26a0e2b Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Sun, 25 Feb 2024 16:58:56 +0100
Subject: [PATCH 01/17] Update index.html
---
examples/sandbox/index.html | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/examples/sandbox/index.html b/examples/sandbox/index.html
index 5f2bc950..685d09a3 100644
--- a/examples/sandbox/index.html
+++ b/examples/sandbox/index.html
@@ -193,6 +193,21 @@
Osmosis
+
+
Kujira
+
+
+
+
+
+
+
+
+
+
+
+
+
Arkeo
From 1784af11f51014d038abda51a4a9c52ff62c4213 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 16:51:47 +0100
Subject: [PATCH 02/17] Create index.ts
---
integration/src/kujira/index.ts | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 integration/src/kujira/index.ts
diff --git a/integration/src/kujira/index.ts b/integration/src/kujira/index.ts
new file mode 100644
index 00000000..1dc698cd
--- /dev/null
+++ b/integration/src/kujira/index.ts
@@ -0,0 +1,7 @@
+import * as core from "@keepkey/hdwallet-core";
+
+import { kujiraTests as tests } from "./kujira";
+
+export function kujiraTests(get: () => { wallet: core.HDWallet; info: core.HDWalletInfo }): void {
+ tests(get);
+}
From b45d8bf0a3ea74a380be4ee6e0c960df716fdd36 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 16:55:16 +0100
Subject: [PATCH 03/17] Create kujiraAnomiTx.json
---
examples/sandbox/json/kujira/kujiraAnomiTx.json | 1 +
1 file changed, 1 insertion(+)
create mode 100644 examples/sandbox/json/kujira/kujiraAnomiTx.json
diff --git a/examples/sandbox/json/kujira/kujiraAnomiTx.json b/examples/sandbox/json/kujira/kujiraAnomiTx.json
new file mode 100644
index 00000000..593252c8
--- /dev/null
+++ b/examples/sandbox/json/kujira/kujiraAnomiTx.json
@@ -0,0 +1 @@
+//todo
From 62873d9a98b8975067333a0675fcf7fc2ac26dea Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:03:26 +0100
Subject: [PATCH 04/17] Create kujira.ts
---
packages/hdwallet-core/src/kujira.ts | 139 +++++++++++++++++++++++++++
1 file changed, 139 insertions(+)
create mode 100644 packages/hdwallet-core/src/kujira.ts
diff --git a/packages/hdwallet-core/src/kujira.ts b/packages/hdwallet-core/src/kujira.ts
new file mode 100644
index 00000000..5493e863
--- /dev/null
+++ b/packages/hdwallet-core/src/kujira.ts
@@ -0,0 +1,139 @@
+import { addressNListToBIP32, slip44ByCoin } from "./utils";
+import { BIP32Path, HDWallet, HDWalletInfo, PathDescription } from "./wallet";
+
+export interface KujiraGetAddress {
+ addressNList: BIP32Path;
+ showDisplay?: boolean;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Kujira {
+ export interface Msg {
+ type: string;
+ value: any;
+ }
+
+ export type Coins = Coin[];
+
+ export interface Coin {
+ denom: string;
+ amount: string;
+ }
+
+ export interface StdFee {
+ amount: Coins;
+ gas: string;
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-namespace
+ namespace crypto {
+ export interface PubKey {
+ type: string;
+ value: string;
+ }
+ }
+
+ export interface StdSignature {
+ pub_key?: crypto.PubKey;
+ signature: string;
+ }
+
+ export interface StdTx {
+ msg: Msg[];
+ fee: StdFee;
+ signatures: StdSignature[];
+ memo?: string;
+ }
+}
+
+export interface KujiraTx {
+ msg: Kujira.Msg[];
+ fee: Kujira.StdFee;
+ signatures: Kujira.StdSignature[];
+ memo?: string;
+}
+
+export interface KujiraSignTx {
+ addressNList: BIP32Path;
+ tx: Kujira.StdTx;
+ chain_id: string;
+ account_number: string;
+ sequence: string;
+ fee?: number;
+}
+
+export interface KujiraSignedTx {
+ serialized: string;
+ body: string;
+ authInfoBytes: string;
+ signatures: string[];
+}
+
+export interface KujiraGetAccountPaths {
+ accountIdx: number;
+}
+
+export interface KujiraAccountPath {
+ addressNList: BIP32Path;
+}
+
+export interface KujiraWalletInfo extends HDWalletInfo {
+ readonly _supportsKujiraInfo: boolean;
+
+ /**
+ * Returns a list of bip32 paths for a given account index in preferred order
+ * from most to least preferred.
+ */
+ KujiraGetAccountPaths(msg: KujiraGetAccountPaths): Array
;
+
+ /**
+ * Returns the "next" account path, if any.
+ */
+ kujiraNextAccountPath(msg: KujiraAccountPath): KujiraAccountPath | undefined;
+}
+
+export interface KujiraWallet extends KujiraWalletInfo, HDWallet {
+ readonly _supportsKujira: boolean;
+
+ kujiraGetAddress(msg: KujiraGetAddress): Promise;
+ kujiraSignTx(msg: KujiraSignTx): Promise;
+}
+
+export function kujiraDescribePath(path: BIP32Path): PathDescription {
+ const pathStr = addressNListToBIP32(path);
+ const unknown: PathDescription = {
+ verbose: pathStr,
+ coin: "Kuji",
+ isKnown: false,
+ };
+
+ if (path.length != 5) {
+ return unknown;
+ }
+
+ if (path[0] != 0x80000000 + 44) {
+ return unknown;
+ }
+
+ if (path[1] != 0x80000000 + slip44ByCoin("Kuji")) {
+ return unknown;
+ }
+
+ if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) {
+ return unknown;
+ }
+
+ if (path[3] !== 0 || path[4] !== 0) {
+ return unknown;
+ }
+
+ const index = path[2] & 0x7fffffff;
+ return {
+ verbose: `Kujira Account #${index}`,
+ accountIdx: index,
+ wholeAccount: true,
+ coin: "Kuji",
+ isKnown: true,
+ isPrefork: false,
+ };
+}
From 91b21aa6541b55eccaabf1bdbbd2e69a6668b848 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:08:51 +0100
Subject: [PATCH 05/17] Update utils.ts
---
packages/hdwallet-core/src/utils.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/hdwallet-core/src/utils.ts b/packages/hdwallet-core/src/utils.ts
index 0082c81d..8e22b23b 100644
--- a/packages/hdwallet-core/src/utils.ts
+++ b/packages/hdwallet-core/src/utils.ts
@@ -160,6 +160,7 @@ const slip44Table = Object.freeze({
ArbitrumNova: 60,
Mayachain: 931,
Cacao: 931,
+ Kujira: 118,
} as const);
type Slip44ByCoin = T extends keyof typeof slip44Table ? typeof slip44Table[T] : number | undefined;
export function slip44ByCoin(coin: T): Slip44ByCoin {
From 39651c1ba4987eb849ab39269dd260a2fc5fcf6f Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:09:30 +0100
Subject: [PATCH 06/17] Update index.ts
---
packages/hdwallet-core/src/index.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/hdwallet-core/src/index.ts b/packages/hdwallet-core/src/index.ts
index 209fe4a2..1ff4234b 100644
--- a/packages/hdwallet-core/src/index.ts
+++ b/packages/hdwallet-core/src/index.ts
@@ -11,11 +11,12 @@ export * from "./exceptions";
export * from "./fio";
export * from "./kava";
export * from "./keyring";
+export * from "./kujira";
+export * from "./mayachain";
export * from "./ripple";
export * from "./secret";
export * from "./terra";
export * from "./thorchain";
-export * from "./mayachain";
export * from "./transport";
export * from "./utils";
export * from "./wallet";
From e4538bc0e1e3bcbe5fd261d141945b1a6c167875 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:12:03 +0100
Subject: [PATCH 07/17] Update wallet.ts
---
packages/hdwallet-core/src/wallet.ts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/packages/hdwallet-core/src/wallet.ts b/packages/hdwallet-core/src/wallet.ts
index 152f6442..59afc191 100644
--- a/packages/hdwallet-core/src/wallet.ts
+++ b/packages/hdwallet-core/src/wallet.ts
@@ -9,6 +9,7 @@ import { EosWallet, EosWalletInfo } from "./eos";
import { ETHWallet, ETHWalletInfo } from "./ethereum";
import { FioWallet, FioWalletInfo } from "./fio";
import { KavaWallet, KavaWalletInfo } from "./kava";
+import { KujiraWallet, KujiraWalletInfo } from "./kujira";
import { MayachainWallet, MayachainWalletInfo } from "./mayachain";
import { OsmosisWallet, OsmosisWalletInfo } from "./osmosis";
import { RippleWallet, RippleWalletInfo } from "./ripple";
@@ -173,6 +174,14 @@ export function infoOsmosis(info: HDWalletInfo): info is OsmosisWalletInfo {
return isObject(info) && (info as any)._supportsOsmosisInfo;
}
+export function supportsKujira(wallet: HDWallet): wallet is KujiraWallet {
+ return isObject(wallet) && (wallet as any)._supportsKujira;
+}
+
+export function infoKujira(info: HDWalletInfo): info is KujiraWalletInfo {
+ return isObject(info) && (info as any)._supportsKujiraInfo;
+}
+
export function supportsArkeo(wallet: HDWallet): wallet is ArkeoWallet {
return isObject(wallet) && (wallet as any)._supportsArkeo;
}
From 2791f5cf1d220b3ad94ec5c56b0a81000f326cfe Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:20:30 +0100
Subject: [PATCH 08/17] Create kujira.ts
---
packages/hdwallet-keepkey/src/kujira.ts | 215 ++++++++++++++++++++++++
1 file changed, 215 insertions(+)
create mode 100644 packages/hdwallet-keepkey/src/kujira.ts
diff --git a/packages/hdwallet-keepkey/src/kujira.ts b/packages/hdwallet-keepkey/src/kujira.ts
new file mode 100644
index 00000000..54af89e3
--- /dev/null
+++ b/packages/hdwallet-keepkey/src/kujira.ts
@@ -0,0 +1,215 @@
+import type { AminoSignResponse, OfflineAminoSigner, StdSignDoc, StdTx } from "@cosmjs/amino";
+import type { AccountData } from "@cosmjs/proto-signing";
+import type { SignerData } from "@cosmjs/stargate";
+import * as Messages from "@keepkey/device-protocol/lib/messages_pb";
+import * as KujiraMessages from "@keepkey/device-protocol/lib/messages-kujira_pb";
+import * as core from "@keepkey/hdwallet-core";
+import bs58check from "bs58check";
+import PLazy from "p-lazy";
+
+import { Transport } from "./transport";
+
+const protoTxBuilder = PLazy.from(() => import("@keepkey/proto-tx-builder"));
+
+export function kujiraGetAccountPaths(msg: core.KujiraGetAccountPaths): Array {
+ return [
+ {
+ addressNList: [0x80000000 + 44, 0x80000000 + core.slip44ByCoin("Kuji"), 0x80000000 + msg.accountIdx, 0, 0],
+ },
+ ];
+}
+
+export async function kujiraGetAddress(
+ transport: Transport,
+ msg: KujiraMessages.KujiraGetAddress.AsObject
+): Promise {
+ const getAddr = new KujiraMessages.KujiraGetAddress();
+ getAddr.setAddressNList(msg.addressNList);
+ getAddr.setShowDisplay(msg.showDisplay !== false);
+ const response = await transport.call(Messages.MessageType.MESSAGETYPE_KUJIRAGETADDRESS, getAddr, {
+ msgTimeout: core.LONG_TIMEOUT,
+ });
+
+ const kujiraAddress = response.proto as KujiraMessages.KujiraAddress;
+ return core.mustBeDefined(kujiraAddress.getAddress());
+}
+
+export async function kujiraSignTx(transport: Transport, msg: core.KujiraSignTx): Promise {
+ const address = await kujiraGetAddress(transport, { addressNList: msg.addressNList });
+
+ const getPublicKeyMsg = new Messages.GetPublicKey();
+ getPublicKeyMsg.setAddressNList(msg.addressNList);
+ getPublicKeyMsg.setEcdsaCurveName("secp256k1");
+
+ const pubkeyMsg = (
+ await transport.call(Messages.MessageType.MESSAGETYPE_GETPUBLICKEY, getPublicKeyMsg, {
+ msgTimeout: core.DEFAULT_TIMEOUT,
+ })
+ ).proto as Messages.PublicKey;
+ const pubkey = bs58check.decode(core.mustBeDefined(pubkeyMsg.getXpub())).slice(45);
+
+ return transport.lockDuring(async () => {
+ const signTx = new KujiraMessages.KujiraSignTx();
+ signTx.setAddressNList(msg.addressNList);
+ signTx.setAccountNumber(msg.account_number);
+ signTx.setChainId(msg.chain_id);
+ signTx.setFeeAmount(parseInt(msg.tx.fee.amount[0].amount));
+ signTx.setGas(parseInt(msg.tx.fee.gas));
+ signTx.setSequence(msg.sequence);
+ if (msg.tx.memo !== undefined) {
+ signTx.setMemo(msg.tx.memo);
+ }
+ signTx.setMsgCount(1);
+
+ let resp = await transport.call(Messages.MessageType.MESSAGETYPE_KUJIRASIGNTX, signTx, {
+ msgTimeout: core.LONG_TIMEOUT,
+ omitLock: true,
+ });
+
+ for (const m of msg.tx.msg) {
+ if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_KUJIRAMSGREQUEST) {
+ throw new Error(`kujira: unexpected response ${resp.message_type}`);
+ }
+
+ let ack;
+
+ if (m.type === "cosmos-sdk/MsgSend") {
+ if (m.value.amount.length !== 1) {
+ throw new Error("kujira: Multiple amounts per msg not supported");
+ }
+
+ const denom = m.value.amount[0].denom;
+ if (denom !== "ukuji") {
+ throw new Error("kujira: Unsupported denomination: " + denom);
+ }
+
+ const send = new KujiraMessages.KujiraMsgSend();
+ send.setFromAddress(m.value.from_address);
+ send.setToAddress(m.value.to_address);
+ send.setAmount(m.value.amount[0].amount);
+
+ ack = new KujiraMessages.KujiraMsgAck();
+ ack.setSend(send);
+ } else if (m.type === "cosmos-sdk/MsgDelegate") {
+ const denom = m.value.amount.denom;
+ if (denom !== "ukuji") {
+ throw new Error("kujira: Unsupported denomination: " + denom);
+ }
+
+ const delegate = new KujiraMessages.KujiraMsgDelegate();
+ delegate.setDelegatorAddress(m.value.delegator_address);
+ delegate.setValidatorAddress(m.value.validator_address);
+ delegate.setAmount(m.value.amount.amount);
+
+ ack = new KujiraMessages.KujiraMsgAck();
+
+ ack.setDelegate(delegate);
+ } else if (m.type === "cosmos-sdk/MsgUndelegate") {
+ const denom = m.value.amount.denom;
+ if (denom !== "ukuji") {
+ throw new Error("kujira: Unsupported denomination: " + denom);
+ }
+
+ const undelegate = new KujiraMessages.KujiraMsgUndelegate();
+ undelegate.setDelegatorAddress(m.value.delegator_address);
+ undelegate.setValidatorAddress(m.value.validator_address);
+ undelegate.setAmount(m.value.amount.amount);
+
+ ack = new KujiraMessages.KujiraMsgAck();
+ ack.setUndelegate(undelegate);
+ } else if (m.type === "cosmos-sdk/MsgBeginRedelegate") {
+ const denom = m.value.amount.denom;
+ if (denom !== "ukuji") {
+ throw new Error("kujira: Unsupported denomination: " + denom);
+ }
+
+ const redelegate = new KujiraMessages.KujiraMsgRedelegate();
+ redelegate.setDelegatorAddress(m.value.delegator_address);
+ redelegate.setValidatorSrcAddress(m.value.validator_src_address);
+ redelegate.setValidatorDstAddress(m.value.validator_dst_address);
+ redelegate.setAmount(m.value.amount.amount);
+
+ ack = new KujiraMessages.KujiraMsgAck();
+ ack.setRedelegate(redelegate);
+ } else if (m.type === "cosmos-sdk/MsgWithdrawDelegationReward") {
+ const rewards = new KujiraMessages.KujiraMsgRewards();
+ rewards.setDelegatorAddress(m.value.delegator_address);
+ rewards.setValidatorAddress(m.value.validator_address);
+ if (m.value.amount) {
+ const denom = m.value.amount.denom;
+ if (denom !== "ukuji") {
+ throw new Error("kujira: Unsupported denomination: " + denom);
+ }
+ rewards.setAmount(m.value.amount.amount);
+ }
+
+ ack = new KujiraMessages.KujiraMsgAck();
+ ack.setRewards(rewards);
+ } else if (m.type === "cosmos-sdk/MsgTransfer") {
+ const denom = m.value.token.denom;
+ if (denom !== "ukuji") {
+ throw new Error("kujira: Unsupported denomination: " + denom);
+ }
+
+ const ibcTransfer = new KujiraMessages.KujiraMsgIBCTransfer();
+ ibcTransfer.setReceiver(m.value.receiver);
+ ibcTransfer.setSender(m.value.sender);
+ ibcTransfer.setSourceChannel(m.value.source_channel);
+ ibcTransfer.setSourcePort(m.value.source_port);
+ ibcTransfer.setRevisionHeight(m.value.timeout_height.revision_height);
+ ibcTransfer.setRevisionNumber(m.value.timeout_height.revision_number);
+ ibcTransfer.setAmount(m.value.token.amount);
+ ibcTransfer.setDenom(m.value.token.denom);
+
+ ack = new KujiraMessages.KujiraMsgAck();
+ ack.setIbcTransfer(ibcTransfer);
+ } else {
+ throw new Error(`kujira: Message ${m.type} is not yet supported`);
+ }
+
+ resp = await transport.call(Messages.MessageType.MESSAGETYPE_KUJIRAMSGACK, ack, {
+ msgTimeout: core.LONG_TIMEOUT,
+ omitLock: true,
+ });
+ }
+
+ if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_KUJIRASIGNEDTX) {
+ throw new Error(`kujira: unexpected response ${resp.message_type}`);
+ }
+
+ const signedTx = resp.proto as KujiraMessages.KujiraSignedTx;
+
+ const offlineSigner: OfflineAminoSigner = {
+ async getAccounts(): Promise {
+ return [
+ {
+ address,
+ algo: "secp256k1",
+ pubkey,
+ },
+ ];
+ },
+ async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise {
+ if (signerAddress !== address) throw new Error("expected signerAddress to match address");
+ return {
+ signed: signDoc,
+ signature: {
+ pub_key: {
+ type: "tendermint/PubKeySecp256k1",
+ value: signedTx.getPublicKey_asB64(),
+ },
+ signature: signedTx.getSignature_asB64(),
+ },
+ };
+ },
+ };
+
+ const signerData: SignerData = {
+ sequence: Number(msg.sequence),
+ accountNumber: Number(msg.account_number),
+ chainId: msg.chain_id,
+ };
+
+ return (await protoTxBuilder).sign(address, msg.tx as StdTx, offlineSigner, signerData);
+ });
+}
From 256fa7c9f243310e95854ea29644febb3bc0521a Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:21:50 +0100
Subject: [PATCH 09/17] Update typeRegistry.ts
---
packages/hdwallet-keepkey/src/typeRegistry.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/hdwallet-keepkey/src/typeRegistry.ts b/packages/hdwallet-keepkey/src/typeRegistry.ts
index d12bade3..cb3c3bca 100644
--- a/packages/hdwallet-keepkey/src/typeRegistry.ts
+++ b/packages/hdwallet-keepkey/src/typeRegistry.ts
@@ -2,6 +2,7 @@ import * as Messages from "@keepkey/device-protocol/lib/messages_pb";
import * as BinanceMessages from "@keepkey/device-protocol/lib/messages-binance_pb";
import * as CosmosMessages from "@keepkey/device-protocol/lib/messages-cosmos_pb";
import * as EosMessages from "@keepkey/device-protocol/lib/messages-eos_pb";
+import * as KujiraMessages from "@keepkey/device-protocol/lib/messages-kujira_pb";
import * as MayachainMessages from "@keepkey/device-protocol/lib/messages-mayachain_pb";
import * as NanoMessages from "@keepkey/device-protocol/lib/messages-nano_pb";
import * as RippleMessages from "@keepkey/device-protocol/lib/messages-ripple_pb";
@@ -20,7 +21,8 @@ const AllMessages = ([] as Array<[string, core.Constructor]>)
.concat(Object.entries(NanoMessages))
.concat(Object.entries(_.omit(EosMessages, "EosPublicKeyKind", "EosPublicKeyKindMap")))
.concat(Object.entries(ThorchainMessages))
- .concat(Object.entries(MayachainMessages));
+ .concat(Object.entries(MayachainMessages))
+ .concat(Object.entries(KujiraMessages));
const upperCasedMessageClasses = AllMessages.reduce((registry, entry: [string, core.Constructor]) => {
registry[entry[0].toUpperCase()] = entry[1];
From 91202871c4086f5c63932165e2cadd5c2958dcfb Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:32:26 +0100
Subject: [PATCH 10/17] Update keepkey.ts
---
packages/hdwallet-keepkey/src/keepkey.ts | 80 ++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/packages/hdwallet-keepkey/src/keepkey.ts b/packages/hdwallet-keepkey/src/keepkey.ts
index 9b781be4..f6678899 100644
--- a/packages/hdwallet-keepkey/src/keepkey.ts
+++ b/packages/hdwallet-keepkey/src/keepkey.ts
@@ -9,6 +9,7 @@ import * as Btc from "./bitcoin";
import * as Cosmos from "./cosmos";
import * as Eos from "./eos";
import * as Eth from "./ethereum";
+import * as Kujira from "./kujira";
import * as Mayachain from "./mayachain";
import * as Osmosis from "./osmosis";
import * as Ripple from "./ripple";
@@ -246,6 +247,45 @@ function describeOsmosisPath(path: core.BIP32Path): core.PathDescription {
};
}
+function describeKujiraPath(path: core.BIP32Path): core.PathDescription {
+ const pathStr = core.addressNListToBIP32(path);
+ const unknown: core.PathDescription = {
+ verbose: pathStr,
+ coin: "Kuji",
+ isKnown: false,
+ };
+
+ if (path.length != 5) {
+ return unknown;
+ }
+
+ if (path[0] != 0x80000000 + 44) {
+ return unknown;
+ }
+
+ if (path[1] != 0x80000000 + core.slip44ByCoin("Kuji")) {
+ return unknown;
+ }
+
+ if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) {
+ return unknown;
+ }
+
+ if (path[3] !== 0 || path[4] !== 0) {
+ return unknown;
+ }
+
+ const index = path[2] & 0x7fffffff;
+ return {
+ verbose: `Kujira Account #${index}`,
+ accountIdx: index,
+ wholeAccount: true,
+ coin: "Kuji",
+ isKnown: true,
+ isPrefork: false,
+ };
+}
+
function describeThorchainPath(path: core.BIP32Path): core.PathDescription {
const pathStr = core.addressNListToBIP32(path);
const unknown: core.PathDescription = {
@@ -408,6 +448,7 @@ export class KeepKeyHDWalletInfo
core.BTCWalletInfo,
core.ETHWalletInfo,
core.CosmosWalletInfo,
+ core.KujiraWalletInfo,
core.BinanceWalletInfo,
core.RippleWalletInfo,
core.EosWalletInfo,
@@ -418,6 +459,7 @@ export class KeepKeyHDWalletInfo
readonly _supportsETHInfo = true;
readonly _supportsCosmosInfo = true;
readonly _supportsOsmosisInfo = true;
+ readonly _supportsKujiraInfo = true;
readonly _supportsRippleInfo = true;
readonly _supportsBinanceInfo = true;
readonly _supportsEosInfo = true;
@@ -480,6 +522,10 @@ export class KeepKeyHDWalletInfo
return Osmosis.osmosisGetAccountPaths(msg);
}
+ public kujiraGetAccountPaths(msg: core.KujieaGetAccountPaths): Array {
+ return Kujira.kujiraGetAccountPaths(msg);
+ }
+
public thorchainGetAccountPaths(msg: core.ThorchainGetAccountPaths): Array {
return Thorchain.thorchainGetAccountPaths(msg);
}
@@ -541,6 +587,8 @@ export class KeepKeyHDWalletInfo
return describeCosmosPath(msg.path);
case "Osmo":
return describeOsmosisPath(msg.path);
+ case "Kuji":
+ return describeKujiraPath(msg.path);
case "Binance":
return describeBinancePath(msg.path);
case "Ripple":
@@ -625,6 +673,21 @@ export class KeepKeyHDWalletInfo
};
}
+ public kujiraNextAccountPath(msg: core.KujiraAccountPath): core.KujiraAccountPath | undefined {
+ const description = describeKujiraPath(msg.addressNList);
+ if (!description.isKnown) {
+ return undefined;
+ }
+
+ const addressNList = msg.addressNList;
+ addressNList[2] += 1;
+
+ return {
+ ...msg,
+ addressNList,
+ };
+ }
+
public thorchainNextAccountPath(msg: core.ThorchainAccountPath): core.ThorchainAccountPath | undefined {
const description = describeThorchainPath(msg.addressNList);
if (!description.isKnown) {
@@ -705,6 +768,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW
readonly _supportsBTCInfo = true;
readonly _supportsCosmosInfo = true;
readonly _supportsOsmosisInfo = true;
+ readonly _supportsKujiraInfo = true;
readonly _supportsRippleInfo = true;
readonly _supportsBinanceInfo = true;
readonly _supportsEosInfo = true;
@@ -1325,6 +1389,18 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW
return Osmosis.osmosisSignTx(this.transport, msg);
}
+ public kujiraGetAccountPaths(msg: core.KujiraGetAccountPaths): Array {
+ return this.info.kujiraGetAccountPaths(msg);
+ }
+
+ public kujiraGetAddress(msg: core.KujiraGetAddress): Promise {
+ return Kujira.kujiraGetAddress(this.transport, msg);
+ }
+
+ public kujiraSignTx(msg: core.KujiraSignTx): Promise {
+ return Kujira.kujiraSignTx(this.transport, msg);
+ }
+
public thorchainGetAccountPaths(msg: core.ThorchainGetAccountPaths): Array {
return this.info.thorchainGetAccountPaths(msg);
}
@@ -1401,6 +1477,10 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW
return this.info.osmosisNextAccountPath(msg);
}
+ public kujiraNextAccountPath(msg: core.KujiraAccountPath): core.KujiraAccountPath | undefined {
+ return this.info.kujiraNextAccountPath(msg);
+ }
+
public rippleNextAccountPath(msg: core.RippleAccountPath): core.RippleAccountPath | undefined {
return this.info.rippleNextAccountPath(msg);
}
From 7b422c6dc667b673351543c9c8b444a7e296aa63 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:40:59 +0100
Subject: [PATCH 11/17] Create kujira.ts
---
packages/hdwallet-native/src/kujira.ts | 99 ++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 packages/hdwallet-native/src/kujira.ts
diff --git a/packages/hdwallet-native/src/kujira.ts b/packages/hdwallet-native/src/kujira.ts
new file mode 100644
index 00000000..2dc0f6a0
--- /dev/null
+++ b/packages/hdwallet-native/src/kujira.ts
@@ -0,0 +1,99 @@
+import { StdTx } from "@cosmjs/amino";
+import { SignerData } from "@cosmjs/stargate";
+import * as core from "@keepkey/hdwallet-core";
+import * as bech32 from "bech32";
+import CryptoJS from "crypto-js";
+import PLazy from "p-lazy";
+
+import * as Isolation from "./crypto/isolation";
+import { NativeHDWalletBase } from "./native";
+import * as util from "./util";
+
+const KUJIRA_CHAIN = "kaiyo-1";
+
+const protoTxBuilder = PLazy.from(() => import("@keepkey/proto-tx-builder"));
+
+export function MixinNativeKujiraWalletInfo>(Base: TBase) {
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ return class MixinNativeKujiraWalletInfo extends Base implements core.KujiraWalletInfo {
+ readonly _supportsKujiraInfo = true;
+ async kujiraSupportsNetwork(): Promise {
+ return true;
+ }
+
+ async kujiraSupportsSecureTransfer(): Promise {
+ return false;
+ }
+
+ kujiraSupportsNativeShapeShift(): boolean {
+ return false;
+ }
+
+ kujiraGetAccountPaths(msg: core.KujiraGetAccountPaths): Array {
+ const slip44 = core.slip44ByCoin("Kuji");
+ return [
+ {
+ addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0],
+ },
+ ];
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ kujiraNextAccountPath(msg: core.KujiraAccountPath): core.KujiraAccountPath | undefined {
+ // Only support one account for now.
+ return undefined;
+ }
+ };
+}
+
+export function MixinNativeKujiraWallet>(Base: TBase) {
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ return class MixinNativeKujiraWallet extends Base {
+ readonly _supportsKujira = true;
+
+ #masterKey: Isolation.Core.BIP32.Node | undefined;
+
+ async kujiraInitializeWallet(masterKey: Isolation.Core.BIP32.Node): Promise {
+ this.#masterKey = masterKey;
+ }
+
+ kujiraWipe(): void {
+ this.#masterKey = undefined;
+ }
+
+ kujiraBech32ify(address: ArrayLike, prefix: string): string {
+ const words = bech32.toWords(address);
+ return bech32.encode(prefix, words);
+ }
+
+ createKujiraAddress(publicKey: string) {
+ const message = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(publicKey));
+ const hash = CryptoJS.RIPEMD160(message as any).toString();
+ const address = Buffer.from(hash, `hex`);
+ return this.kujiraBech32ify(address, `kujira`);
+ }
+
+ async kujiraGetAddress(msg: core.KujiraGetAddress): Promise {
+ return this.needsMnemonic(!!this.#masterKey, async () => {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "kujira");
+ return this.createKujiraAddress(keyPair.publicKey.toString("hex"));
+ });
+ }
+
+ async kujiraSignTx(msg: core.KujiraSignTx): Promise {
+ return this.needsMnemonic(!!this.#masterKey, async () => {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "osmosis");
+ const adapter = await Isolation.Adapters.CosmosDirect.create(keyPair.node, "osmo");
+
+ const signerData: SignerData = {
+ sequence: Number(msg.sequence),
+ accountNumber: Number(msg.account_number),
+ chainId: KUJIRA_CHAIN,
+ };
+ return (await protoTxBuilder).sign(adapter.address, msg.tx as StdTx, adapter, signerData);
+ });
+ }
+ };
+}
From c77bb48d4a855e8ca17bd88ae7e13c1dbcc759cf Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:46:37 +0100
Subject: [PATCH 12/17] Create kujira.test.ts
---
packages/hdwallet-native/src/kujira.test.ts | 1 +
1 file changed, 1 insertion(+)
create mode 100644 packages/hdwallet-native/src/kujira.test.ts
diff --git a/packages/hdwallet-native/src/kujira.test.ts b/packages/hdwallet-native/src/kujira.test.ts
new file mode 100644
index 00000000..593252c8
--- /dev/null
+++ b/packages/hdwallet-native/src/kujira.test.ts
@@ -0,0 +1 @@
+//todo
From b69745bdc33ea0e87c195da05c9a0ed536f2e8ac Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:51:53 +0100
Subject: [PATCH 13/17] Update native.ts
---
packages/hdwallet-native/src/native.ts | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/packages/hdwallet-native/src/native.ts b/packages/hdwallet-native/src/native.ts
index e6965682..c520a586 100644
--- a/packages/hdwallet-native/src/native.ts
+++ b/packages/hdwallet-native/src/native.ts
@@ -12,6 +12,7 @@ import * as Isolation from "./crypto/isolation";
import { MixinNativeETHWallet, MixinNativeETHWalletInfo } from "./ethereum";
import { MixinNativeFioWallet, MixinNativeFioWalletInfo } from "./fio";
import { MixinNativeKavaWallet, MixinNativeKavaWalletInfo } from "./kava";
+import { MixinNativeKujiraWallet, MixinNativeKujiraWalletInfo } from "./kujira";
import { MixinNativeMayachainWallet, MixinNativeMayachainWalletInfo } from "./mayachain";
import { getNetwork } from "./networks";
import { MixinNativeOsmosisWallet, MixinNativeOsmosisWalletInfo } from "./osmosis";
@@ -129,7 +130,7 @@ class NativeHDWalletInfo
MixinNativeTerraWalletInfo(
MixinNativeKavaWalletInfo(
MixinNativeArkeoWalletInfo(
- MixinNativeOsmosisWalletInfo(MixinNativeMayachainWalletInfo(NativeHDWalletBase))
+ MixinNativeOsmosisWalletInfo(MixinNativeMayachainWalletInfo(MixinNativeKujiraWalletInfo(NativeHDWalletBase)))
)
)
)
@@ -183,6 +184,9 @@ class NativeHDWalletInfo
case "osmosis":
case "osmo":
return core.osmosisDescribePath(msg.path);
+ case "kujira":
+ case "kuji":
+ return core.kujiraDescribePath(msg.path);
case "fio":
return core.fioDescribePath(msg.path);
case "arkeo":
@@ -208,7 +212,7 @@ export class NativeHDWallet
MixinNativeSecretWallet(
MixinNativeTerraWallet(
MixinNativeKavaWallet(
- MixinNativeOsmosisWallet(MixinNativeArkeoWallet(MixinNativeMayachainWallet(NativeHDWalletInfo)))
+ MixinNativeOsmosisWallet(MixinNativeArkeoWallet(MixinNativeMayachainWallet(MixinNativeKujiraWallet(NativeHDWalletInfo))))
)
)
)
@@ -224,6 +228,7 @@ export class NativeHDWallet
core.ETHWallet,
core.CosmosWallet,
core.OsmosisWallet,
+ core.KujiraWallet,
core.FioWallet,
core.ThorchainWallet,
core.SecretWallet,
@@ -252,6 +257,7 @@ export class NativeHDWallet
readonly _supportsKava = true;
readonly _supportsArkeo = true;
readonly _supportsMayachain = true;
+ readonly _supportsKujira = true;
readonly _isNative = true;
#deviceId: string;
@@ -338,6 +344,7 @@ export class NativeHDWallet
super.ethInitializeWallet(masterKey),
super.cosmosInitializeWallet(masterKey),
super.osmosisInitializeWallet(masterKey),
+ super.kujiraInitializeWallet(masterKey),
super.binanceInitializeWallet(masterKey),
super.fioInitializeWallet(masterKey),
super.thorchainInitializeWallet(masterKey),
@@ -387,6 +394,7 @@ export class NativeHDWallet
super.ethWipe();
super.cosmosWipe();
super.osmosisWipe();
+ super.kujiraWipe();
super.binanceWipe();
super.fioWipe();
super.thorchainWipe();
From 80878291d2eb923754bffed2b8c1977c5c4a8e82 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:53:23 +0100
Subject: [PATCH 14/17] Update native.test.ts
---
packages/hdwallet-native/src/native.test.ts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/packages/hdwallet-native/src/native.test.ts b/packages/hdwallet-native/src/native.test.ts
index 248e9708..eed7968b 100644
--- a/packages/hdwallet-native/src/native.test.ts
+++ b/packages/hdwallet-native/src/native.test.ts
@@ -100,6 +100,10 @@ describe("NativeHDWalletInfo", () => {
msg: { coin: "Osmo", path: [44 + 0x80000000, 118 + 0x80000000, 0 + 0x80000000, 0, 0] },
out: { coin: "Osmo", verbose: "Osmosis Account #0", isKnown: true },
},
+ {
+ msg: { coin: "Kuji", path: [44 + 0x80000000, 118 + 0x80000000, 0 + 0x80000000, 0, 0] },
+ out: { coin: "Kuji", verbose: "Kujira Account #0", isKnown: true },
+ },
{
msg: { coin: "cacao", path: [44 + 0x80000000, 931 + 0x80000000, 0 + 0x80000000, 0, 0] },
out: { coin: "Mayachain", verbose: "Mayachain Account #0", isKnown: true },
From 4e040e558f3bb46b951bb466a8c46d7ac25a3c3f Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:56:12 +0100
Subject: [PATCH 15/17] Update networks.ts
---
packages/hdwallet-native/src/networks.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/hdwallet-native/src/networks.ts b/packages/hdwallet-native/src/networks.ts
index 8947a9d5..c4887cfe 100644
--- a/packages/hdwallet-native/src/networks.ts
+++ b/packages/hdwallet-native/src/networks.ts
@@ -171,6 +171,7 @@ for (const coin of [
"cardano",
"cosmos",
"osmosis",
+ "kujira",
"binance",
"ethereum",
"arkeo",
From 2bcd32d4064da790e54e7c3f873474e73c8786f4 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Sun, 3 Mar 2024 17:26:41 +0100
Subject: [PATCH 16/17] Update index.html
---
examples/sandbox/index.html | 3 ---
1 file changed, 3 deletions(-)
diff --git a/examples/sandbox/index.html b/examples/sandbox/index.html
index 685d09a3..6fa302fa 100644
--- a/examples/sandbox/index.html
+++ b/examples/sandbox/index.html
@@ -201,10 +201,7 @@ Kujira
-
-
-
From 9c4e2c8ae36c086486cf60c7a511ddcd5404c585 Mon Sep 17 00:00:00 2001
From: GiMa-Maya <128942712+GiMa-Maya@users.noreply.github.com>
Date: Sun, 3 Mar 2024 17:34:25 +0100
Subject: [PATCH 17/17] Update index.ts
---
examples/sandbox/index.ts | 182 ++++++++++++++++++++++++++++++++++++++
1 file changed, 182 insertions(+)
diff --git a/examples/sandbox/index.ts b/examples/sandbox/index.ts
index c51e3551..18af80c7 100644
--- a/examples/sandbox/index.ts
+++ b/examples/sandbox/index.ts
@@ -50,6 +50,14 @@ import {
osmosisSwapTx,
osmosisUndelegateTx,
} from "./json/osmosis/osmosisAminoTx.json";
+import {
+ kujiraDelegateTx,
+ kujiraIBCTransferTx,
+ kujiraRedelegateTx,
+ kujiraRewardsTx,
+ kujiraSendTx,
+ kujiraUndelegateTx,
+} from "./json/kujira/kujiraAminoTx.json";
import * as rippleTxJson from "./json/rippleTx.json";
import {
thorchainBinanceBaseTx,
@@ -1818,6 +1826,180 @@ $osmosisSwap.on("click", async (e) => {
}
});
+/*
+ * Kujira
+ */
+const $kujiraAddress = $("#kujiraAddress");
+const $kujiraSend = $("#kujiraSend");
+const $kujiraDelegate = $("#kujiraDelegate");
+const $kujiraUndelegate = $("#kujiraUndelegate");
+const $kujiraRedelegate = $("#kujiraRedelegate");
+const $kujiraRewards = $("#kujiraRewards");
+const $kujiraIBCTransfer = $("#kujiraIBCTransfer");
+
+const $kujiraResults = $("#kujiraResults");
+
+$kujiraAddress.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const { addressNList } = wallet.kujiraGetAccountPaths({ accountIdx: 0 })[0];
+ const result = await wallet.kujiraGetAddress({
+ addressNList,
+ showDisplay: false,
+ });
+ await wallet.kujiraGetAddress({
+ addressNList,
+ showDisplay: true,
+ });
+ $kujiraResults.val(result);
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Lujira");
+ }
+});
+
+$kujiraSend.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const unsigned: core.Kujira.StdTx = kujiraSendTx;
+
+ const res = await wallet.kujiraSignTx({
+ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`),
+ chain_id: unsigned.chain_id,
+ account_number: unsigned.account_number,
+ sequence: unsigned.sequence,
+ tx: unsigned,
+ });
+ $kujiraResults.val(JSON.stringify(res));
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Kujira");
+ }
+});
+
+$kujiraDelegate.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const unsigned: core.Kujira.StdTx = kujiraDelegateTx;
+
+ const res = await wallet.kujiraSignTx({
+ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`),
+ chain_id: unsigned.chain_id,
+ account_number: unsigned.account_number,
+ sequence: unsigned.sequence,
+ tx: unsigned,
+ });
+ $kujiraResults.val(JSON.stringify(res));
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Kujira");
+ }
+});
+
+$kujiraUndelegate.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const unsigned: core.Kujira.StdTx = kujiraUndelegateTx;
+
+ const res = await wallet.kujiraSignTx({
+ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`),
+ chain_id: unsigned.chain_id,
+ account_number: unsigned.account_number,
+ sequence: unsigned.sequence,
+ tx: unsigned,
+ });
+ $kujiraResults.val(JSON.stringify(res));
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Kujira");
+ }
+});
+
+$kujiraRedelegate.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const unsigned: core.Kujira.StdTx = kujiraRedelegateTx;
+
+ const res = await wallet.kujiraSignTx({
+ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`),
+ chain_id: unsigned.chain_id,
+ account_number: unsigned.account_number,
+ sequence: unsigned.sequence,
+ tx: unsigned,
+ });
+ $kujiraResults.val(JSON.stringify(res));
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Kujira");
+ }
+});
+
+$kujiraRewards.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const unsigned: core.Kujira.StdTx = kujiraRewardsTx;
+
+ const res = await wallet.kujiraSignTx({
+ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`),
+ chain_id: unsigned.chain_id,
+ account_number: unsigned.account_number,
+ sequence: unsigned.sequence,
+ tx: unsigned,
+ });
+ $kujiraResults.val(JSON.stringify(res));
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Kujira");
+ }
+});
+
+$kujiraIBCTransfer.on("click", async (e) => {
+ e.preventDefault();
+ if (!wallet) {
+ $kujiraResults.val("No wallet?");
+ return;
+ }
+ if (core.supportsKujira(wallet)) {
+ const unsigned: core.Kujira.StdTx = kujiraIBCTransferTx;
+
+ const res = await wallet.kujiraSignTx({
+ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`),
+ chain_id: unsigned.chain_id,
+ account_number: unsigned.account_number,
+ sequence: unsigned.sequence,
+ tx: unsigned,
+ });
+ $kujiraResults.val(JSON.stringify(res));
+ } else {
+ const label = await wallet.getLabel();
+ $kujiraResults.val(label + " does not support Kujira");
+ }
+});
+
/*
* Arkeo
*/