Skip to content

Commit

Permalink
Merge pull request #23 from leapwallet/update/package-version
Browse files Browse the repository at this point in the history
Update/package version
  • Loading branch information
baryon2 authored Apr 4, 2024
2 parents c6802f3 + de0cd75 commit b2f0160
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 19 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/publish-beta.workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
on:
push:
branches: [staging]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 16
- run: yarn
- run: yarn lint:check
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 16
- run: yarn
- run: yarn test:coverage

- name: Comment Test Coverage
uses: raulanatol/jest-coverage-comment-action@main
with:
github-token: ${{ secrets.NODE_AUTH_TOKEN }}
use-existing-reports: true
publish-npm-registry:
if: github.ref == 'refs/heads/staging'
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: 'https://registry.npmjs.org'
- run: yarn
- run: yarn build
- run: yarn publish --tag beta
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish-github-registry:
if: github.ref == 'refs/heads/staging'
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: 'https://npm.pkg.github.com'
scope: '@leapwallet'
- run: yarn
- run: yarn build
- run: yarn publish --tag beta --non-interactive
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@leapwallet/leap-keychain",
"version": "0.2.2",
"version": "0.2.3",
"description": "A javascript library for crypto key management",
"scripts": {
"test": "jest",
Expand Down
31 changes: 28 additions & 3 deletions src/key/eth-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ import { encodeSecp256k1Signature } from '../utils/encode-signature';
import { HDNode } from '@ethersproject/hdnode';
import { bip39Token, getBip39 } from '../crypto/bip39/bip39-token';
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
import { TransactionRequest, Provider } from '@ethersproject/abstract-provider';
import Container from 'typedi';
import { pubkeyToAddress } from './wallet';

export class EthWallet {
private constructor(
private mnemonic: string,
private pvtKey: string,
private walletType: 'mnemonic' | 'pvtKey',
private options: WalletOptions,
private provider?: Provider,
) {}

/**
Expand All @@ -32,6 +35,10 @@ export class EthWallet {
return new EthWallet(mnemonic, '', 'mnemonic', options);
}

setProvider(provider: Provider) {
this.provider = provider;
}

/**
* Generates a wallet from a private key.
* @param {string} pvtKey - The private key to generate the wallet from.
Expand Down Expand Up @@ -66,13 +73,18 @@ export class EthWallet {
this.walletType === 'mnemonic' ? HDNode.fromSeed(seed).derivePath(path) : new Wallet(this.pvtKey);

const ethAddr = EthereumUtilsAddress.fromString(hdWallet.address).toBuffer();
const bech32Address = bech32.encode(this.options.addressPrefix, bech32.toWords(ethAddr));
const ethWallet = new Wallet(hdWallet.privateKey);

const ethWallet = new Wallet(hdWallet.privateKey, this.provider);
const pubkey = fromHex(ethWallet._signingKey().compressedPublicKey.replace('0x', ''));

const bech32Address = this.options.pubKeyBech32Address
? pubkeyToAddress(this.options.addressPrefix, pubkey)
: bech32.encode(this.options.addressPrefix, bech32.toWords(ethAddr));
return {
algo: 'ethsecp256k1',
address: bech32Address,
ethWallet: ethWallet,
pubkey: fromHex(ethWallet._signingKey().compressedPublicKey.replace('0x', '')),
pubkey,
};
});
}
Expand All @@ -87,6 +99,17 @@ export class EthWallet {
return ethWallet._signingKey().signDigest(signBytes);
}

public async sendTransaction(transaction: TransactionRequest) {
const accounts = this.getAccountsWithPrivKey();
const account = accounts[0];
if (!account) throw new Error('Account not found');
// if (account === undefined) {
// throw new Error(`Address ${signerAddress} not found in wallet`);
// }
const { ethWallet } = account;
return await ethWallet.sendTransaction(transaction);
}

signMessage(signerAddress: string, message: Uint8Array) {
const accounts = this.getAccountsWithPrivKey();
const account = accounts.find(({ address }) => address === signerAddress);
Expand All @@ -104,6 +127,7 @@ export class EthWallet {
throw new Error(`Address ${signerAddress} not found in wallet`);
}
const { ethWallet } = account;

return ethWallet.signTransaction(transaction);
}

Expand All @@ -117,6 +141,7 @@ export class EthWallet {
const rawSignature = this.sign(signerAddress, keccak256(Buffer.from(hash)));
const splitSignature = bytes.splitSignature(rawSignature);
const signature = bytes.arrayify(bytes.concat([splitSignature.r, splitSignature.s]));

return {
signed: signDoc,
signature: encodeSecp256k1Signature(account.pubkey, signature),
Expand Down
19 changes: 16 additions & 3 deletions src/key/wallet-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,26 @@ import { Wallet } from './wallet';
import * as base64js from 'base64-js';
import { bip39Token } from '../crypto/bip39/bip39-token';

export function generateWalletFromMnemonic(mnemonic: string, hdPath: string, addressPrefix: string) {
export function generateWalletFromMnemonic(
mnemonic: string,
{
hdPath,
addressPrefix,
ethWallet,
pubKeyBech32Address,
}: {
hdPath: string;
addressPrefix: string;
ethWallet: boolean;
pubKeyBech32Address?: boolean;
},
) {
const bip39 = Container.get(bip39Token);
bip39.mnemonicToEntropy(mnemonic);
const hdPathParams = hdPath.split('/');
const coinType = hdPathParams[2];
if (coinType?.replace("'", '') === '60') {
return EthWallet.generateWalletFromMnemonic(mnemonic, { paths: [hdPath], addressPrefix });
if (coinType?.replace("'", '') === '60' || ethWallet) {
return EthWallet.generateWalletFromMnemonic(mnemonic, { paths: [hdPath], addressPrefix, pubKeyBech32Address });
}
return Wallet.generateWallet(mnemonic, { paths: [hdPath], addressPrefix });
}
Expand Down
29 changes: 19 additions & 10 deletions src/keychain/keychain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,17 @@ export class KeyChain {
public static async getSigner<T extends string>(
walletId: string,
password: string,
addressPrefix: string,
coinType: string,
{
addressPrefix,
coinType,
ethWallet,
pubKeyBech32Address,
}: {
addressPrefix: string;
coinType: string;
ethWallet?: boolean;
pubKeyBech32Address?: boolean;
},
) {
const storage = Container.get(storageToken);
const keychain = (await storage.get(KEYCHAIN)) as unknown as Keystore<T>;
Expand All @@ -222,14 +231,14 @@ export class KeyChain {
throw new Error('Wallet type not supported');
}
if (walletData.walletType === WALLETTYPE.PRIVATE_KEY) {
if (coinType === '60') {
if (coinType === '60' || ethWallet) {
const hdPath = getHDPath(coinType, walletData.addressIndex.toString());
return EthWallet.generateWalletFromPvtKey(secret, { paths: [hdPath], addressPrefix });
return EthWallet.generateWalletFromPvtKey(secret, { paths: [hdPath], addressPrefix, pubKeyBech32Address });
}
return PvtKeyWallet.generateWallet(secret, addressPrefix);
} else {
const hdPath = getHDPath(coinType, walletData.addressIndex.toString());
return generateWalletFromMnemonic(secret, hdPath, addressPrefix);
return generateWalletFromMnemonic(secret, { hdPath, addressPrefix, ethWallet: !!ethWallet, pubKeyBech32Address });
}
}

Expand Down Expand Up @@ -285,11 +294,11 @@ export class KeyChain {
const addresses: Record<string, string> = {};
const pubKeys: Record<string, string> = {};
for (const chainInfo of chainsData) {
const wallet = generateWalletFromMnemonic(
mnemonic,
getHDPath(chainInfo.coinType, addressIndex.toString()),
chainInfo.addressPrefix,
);
const wallet = generateWalletFromMnemonic(mnemonic, {
hdPath: getHDPath(chainInfo.coinType, addressIndex.toString()),
addressPrefix: chainInfo.addressPrefix,
ethWallet: false,
});

const [account] = wallet.getAccounts();
if (account?.address && account?.pubkey) {
Expand Down
1 change: 1 addition & 0 deletions src/types/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { PvtKeyWallet, Wallet } from '../key/wallet';
export type WalletOptions = {
paths: string[];
addressPrefix: string;
pubKeyBech32Address?: boolean;
};

export type LeapSigner = EthWallet | Wallet | PvtKeyWallet;
25 changes: 23 additions & 2 deletions tests/wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,39 @@ describe('generateMnemonic', () => {
).toThrow('Invalid private key');
});
test('generateWalletFromMnemonic', () => {
const wallet = generateWalletFromMnemonic(mnemonic, "m/44'/118'/0'/0/1", 'cosmos');
const wallet = generateWalletFromMnemonic(mnemonic, {
hdPath: "m/44'/118'/0'/0/1",
addressPrefix: 'cosmos',
ethWallet: false,
});
const accounts = wallet.getAccounts();
expect(accounts[0]?.address).toBe(referenceWallets.ref2.addresses.cosmos);
});
test('generateWalletFromMnemonic for cointype=60', () => {
const wallet = generateWalletFromMnemonic(mnemonic, {
hdPath: "m/44'/60'/0'/0/1",
addressPrefix: 'evmos',
ethWallet: false,
});
const accounts = wallet.getAccounts();
expect(accounts[0]?.address).toBe(referenceWallets.ref2.addresses.evmos);
});
test('generateWalletsFromMnemonic', async () => {
const wallet = generateWalletsFromMnemonic(mnemonic, ["m/44'/118'/0'/0/0", "m/44'/118'/0'/0/1"], 'cosmos');
const accounts = wallet.getAccounts();
expect(accounts[0]?.address).toBe(referenceWallets.ref1.addresses.cosmos);
expect(accounts[1]?.address).toBe(referenceWallets.ref2.addresses.cosmos);
});
test('generateWalletsFromMnemonic for cointype=60', async () => {
const wallet = generateWalletsFromMnemonic(mnemonic, ["m/44'/60'/0'/0/0", "m/44'/60'/0'/0/1"], 'evmos');
const accounts = wallet.getAccounts();
expect(accounts[0]?.address).toBe(referenceWallets.ref1.addresses.evmos);
expect(accounts[1]?.address).toBe(referenceWallets.ref2.addresses.evmos);
});
test('generateWalletFromMnemonic throws error if mnemonic is invalid', () => {
expect(() => generateWalletFromMnemonic('', "m/44'/118'/0'/0/0", 'cosmos')).toThrow('Invalid mnemonic');
expect(() =>
generateWalletFromMnemonic('', { hdPath: "m/44'/118'/0'/0/0", addressPrefix: 'cosmos', ethWallet: false }),
).toThrow('Invalid mnemonic');
});
test('generateWalletsFromMnemonic throws error if mnemonic is invalid', () => {
expect(() => generateWalletsFromMnemonic('', ["m/44'/118'/0'/0/0"], 'cosmos')).toThrow('Invalid mnemonic');
Expand Down

0 comments on commit b2f0160

Please sign in to comment.