Skip to content

Commit

Permalink
Merge pull request #20 from leapwallet/add/evm-118-wallet
Browse files Browse the repository at this point in the history
Add/evm 118 wallet
  • Loading branch information
baryon2 authored Feb 19, 2024
2 parents 655185b + e80ef18 commit 40ea6ad
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 5 deletions.
1 change: 1 addition & 0 deletions .github/workflows/publish-beta.workflow.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
on:
push:
branches: [staging]
jobs:
jobs:
lint:
runs-on: ubuntu-latest
Expand Down
21 changes: 20 additions & 1 deletion src/key/eth-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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';

export class EthWallet {
Expand All @@ -19,6 +20,7 @@ export class EthWallet {
private pvtKey: string,
private walletType: 'mnemonic' | 'pvtKey',
private options: WalletOptions,
private provider?: Provider,
) {}

/**
Expand All @@ -32,6 +34,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 @@ -67,7 +73,7 @@ export class EthWallet {

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);
return {
algo: 'ethsecp256k1',
address: bech32Address,
Expand All @@ -87,6 +93,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 +121,7 @@ export class EthWallet {
throw new Error(`Address ${signerAddress} not found in wallet`);
}
const { ethWallet } = account;

return ethWallet.signTransaction(transaction);
}

Expand All @@ -117,6 +135,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
9 changes: 7 additions & 2 deletions src/key/wallet-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ 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: string,
addressPrefix: string,
ethWallet?: boolean,
) {
const bip39 = Container.get(bip39Token);
bip39.mnemonicToEntropy(mnemonic);
const hdPathParams = hdPath.split('/');
const coinType = hdPathParams[2];
if (coinType?.replace("'", '') === '60') {
if (coinType?.replace("'", '') === '60' || ethWallet) {
return EthWallet.generateWalletFromMnemonic(mnemonic, { paths: [hdPath], addressPrefix });
}
return Wallet.generateWallet(mnemonic, { paths: [hdPath], addressPrefix });
Expand Down
5 changes: 3 additions & 2 deletions src/keychain/keychain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ export class KeyChain {
password: string,
addressPrefix: string,
coinType: string,
ethWallet?: boolean,
) {
const storage = Container.get(storageToken);
const keychain = (await storage.get(KEYCHAIN)) as unknown as Keystore<T>;
Expand All @@ -222,14 +223,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 PvtKeyWallet.generateWallet(secret, addressPrefix);
} else {
const hdPath = getHDPath(coinType, walletData.addressIndex.toString());
return generateWalletFromMnemonic(secret, hdPath, addressPrefix);
return generateWalletFromMnemonic(secret, hdPath, addressPrefix, ethWallet);
}
}

Expand Down
11 changes: 11 additions & 0 deletions tests/wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,23 @@ describe('generateMnemonic', () => {
const accounts = wallet.getAccounts();
expect(accounts[0]?.address).toBe(referenceWallets.ref2.addresses.cosmos);
});
test('generateWalletFromMnemonic for cointype=60', () => {
const wallet = generateWalletFromMnemonic(mnemonic, "m/44'/60'/0'/0/1", 'evmos');
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');
});
Expand Down

0 comments on commit 40ea6ad

Please sign in to comment.