Skip to content

Commit

Permalink
Merge branch 'main' into update/version
Browse files Browse the repository at this point in the history
  • Loading branch information
baryon2 authored Jan 3, 2025
2 parents ac18277 + 240e23f commit 0e11ad7
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 25 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"doc:build": "yarn doc",
"doc:watch": "yarn doc --watch",
"prepublish": "yarn build",
"build": "tsc -p tsconfig-browser.json && tsc -p tsconfig-node.json",
"test:coverage": "nyc"
"build": "tsc -p tsconfig-browser.json && tsc -p tsconfig-node.json"
},
"types": "dist/browser/index.d.ts",
"license": "MIT",
Expand Down
14 changes: 14 additions & 0 deletions src/key/eth-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
import { TransactionRequest, Provider } from '@ethersproject/abstract-provider';
import Container from 'typedi';
import { pubkeyToAddress } from './wallet';
import { hex } from '@scure/base';

export class EthWallet {
private constructor(
Expand Down Expand Up @@ -84,11 +85,24 @@ export class EthWallet {
algo: 'ethsecp256k1',
address: bech32Address,
ethWallet: ethWallet,
hexAddress: `0x${hex.encode(ethAddr)}`,
pubkey,
};
});
}

public getAccountWithHexAddress() {
const accounts = this.getAccountsWithPrivKey();
return accounts.map((account) => {
return {
algo: account.algo,
address: account.hexAddress,
pubkey: account.pubkey,
bech32Address: account.address,
};
});
}

public sign(signerAddress: string, signBytes: string | Uint8Array) {
const accounts = this.getAccountsWithPrivKey();
const account = accounts.find(({ address }) => address === signerAddress);
Expand Down
14 changes: 8 additions & 6 deletions src/key/wallet-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,24 +101,26 @@ export function generateWalletFromPrivateKey(
hdPath: string,
prefix: string,
btcNetwork?: typeof NETWORK,
ethWallet?: boolean,
pubKeyBech32Address?: boolean,
) {
const hdPathParams = hdPath.split('/');
const coinType = hdPathParams[2];
let wallet;
if (coinType === "60'") {
wallet = EthWallet.generateWalletFromPvtKey(privateKey, {

if (coinType === "60'" || ethWallet) {
return EthWallet.generateWalletFromPvtKey(privateKey, {
paths: [hdPath],
addressPrefix: prefix,
pubKeyBech32Address,
});
} else if (coinType === "0'" || coinType === "1'") {
if (!btcNetwork) throw new Error('Unable to generate key. Please provide btc network in chain info config');
wallet = new BtcWalletPk(privateKey, {
return new BtcWalletPk(privateKey, {
paths: [hdPath],
addressPrefix: prefix,
network: btcNetwork,
});
} else {
wallet = PvtKeyWallet.generateWallet(privateKey, prefix);
return PvtKeyWallet.generateWallet(privateKey, prefix);
}
return wallet;
}
78 changes: 61 additions & 17 deletions test/mockdata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ export const btcPrivatekey = 'a29da5e16cf8a7a4fe90dc9287c3338e6726b8bd4c1e9a8ab4
export const sbtcPrivatekey = 'acc42e2d3fb504915180bae5d001e818f3304b970b2a145ebc252faf2fbd8867';

export const addresses = {
secret: 'secret18qya73yjlt28hhc3yefu3j97kgpyjj9rfd46d6',
bitcoin: 'bc1qd5xpfp9zp8q696pu3sz7ej2wrk2wn634dlnhfa',
cosmos: 'cosmos1uput06d0xac525sdmtf4h5d8dy9d8x3u07smz9',
osmosis: 'osmo1uput06d0xac525sdmtf4h5d8dy9d8x3u89rt5h',
juno: 'juno1uput06d0xac525sdmtf4h5d8dy9d8x3uevnq9e',
evmos: 'evmos1reqqp5jum0lgyjh33nyvus8dlyzspk9aw50w3r',
injective: 'inj1reqqp5jum0lgyjh33nyvus8dlyzspk9axufyen',
bitcoin: 'bc1qd5xpfp9zp8q696pu3sz7ej2wrk2wn634dlnhfa',
juno: 'juno1uput06d0xac525sdmtf4h5d8dy9d8x3uevnq9e',
osmosis: 'osmo1uput06d0xac525sdmtf4h5d8dy9d8x3u89rt5h',
secret: 'secret18qya73yjlt28hhc3yefu3j97kgpyjj9rfd46d6',
sei: 'sei1nqcal4r4qgfj9hhazrfpu72fx0ccdv35rk53ms',
signet: 'tb1qp63mr992cpwspcjnysdfhstj6jaqykrs64yywn',
};

Expand All @@ -27,50 +28,78 @@ export const referenceWallets = {
juno: 'juno1nqcal4r4qgfj9hhazrfpu72fx0ccdv35cgxu6d',
osmosis: 'osmo1nqcal4r4qgfj9hhazrfpu72fx0ccdv35xpkhtr',
secret: 'secret1gcf3qag3zf0k9sd759ttuuq287p00g4kewjdwc',
sei: 'sei1nqcal4r4qgfj9hhazrfpu72fx0ccdv35rk53ms',
signet: 'tb1qp63mr992cpwspcjnysdfhstj6jaqykrs64yywn',
},
colorIndex: 0,
name: 'testwallet',
pubKeys: {
bitcoin: 'AjQmZKXn3epwg10mUNuHUF1SxWLS+06AA1v4um9x8//2',
cosmos: 'AwxYytPNgUq91tLoRiGBP6MGEcsghnVTeMcLKcoPSfjW',
evmos: 'A71glojh4VpiwcFufLabwSTfkLB6lnJ3i6EAp7oOac0h',
injective: 'A71glojh4VpiwcFufLabwSTfkLB6lnJ3i6EAp7oOac0h',
juno: 'AwxYytPNgUq91tLoRiGBP6MGEcsghnVTeMcLKcoPSfjW',
osmosis: 'AwxYytPNgUq91tLoRiGBP6MGEcsghnVTeMcLKcoPSfjW',
secret: 'AnQhTZmbQZXa9MY3KhYdEE1OabdLBtEAbG/wgj0SzBEV',
bitcoin: 'AjQmZKXn3epwg10mUNuHUF1SxWLS+06AA1v4um9x8//2',
sei: 'AwxYytPNgUq91tLoRiGBP6MGEcsghnVTeMcLKcoPSfjW',
signet: 'AuLI5ATf2xgkFbHojyMsSQ2qbnvZoVyfu9JACxiZz8ji',
},
walletType: 0,
},
ref2: {
addressIndex: 1,
addresses: {
bitcoin: 'bc1qpx6cas6wg4gtpcmfke626va4kpx9m4s5cx03ff',
cosmos: 'cosmos1rjtukzmqtlh2u20atc9pjefk55y0h6j2y7atrj',
evmos: 'evmos1wdlne45wt60w68pu08u2w9cyzamxglz79npune',
injective: 'inj1wdlne45wt60w68pu08u2w9cyzamxglz7dm8kmf',
juno: 'juno1rjtukzmqtlh2u20atc9pjefk55y0h6j2jv7syw',
osmosis: 'osmo1rjtukzmqtlh2u20atc9pjefk55y0h6j2v9wm4q',
secret: 'secret1v7pzm2xdytc75dxx893fnd4te80qh6nw2k9czh',
bitcoin: 'bc1qpx6cas6wg4gtpcmfke626va4kpx9m4s5cx03ff',
sei: 'sei1rjtukzmqtlh2u20atc9pjefk55y0h6j2fjva9n',
signet: 'tb1qh9d6qr7twdk7dh5tsw7mqxl5yvxskrcs80gppq',
},
colorIndex: 1,
name: 'Wallet 2',
pubKeys: {
bitcoin: 'A3hQ226XkTCvpEXC3KqxLEZANyNjyMmJhIm8rDwX/EKE',
cosmos: 'AqYABZ4+Zqqbx7zZfctmtRQs882J15WfRz3Go9QggsIA',
evmos: 'AiuOnXAg+hvUimpEDchQfP9NmiKxgw1WaXpnQL/dvDMS',
injective: 'AiuOnXAg+hvUimpEDchQfP9NmiKxgw1WaXpnQL/dvDMS',
juno: 'AqYABZ4+Zqqbx7zZfctmtRQs882J15WfRz3Go9QggsIA',
osmosis: 'AqYABZ4+Zqqbx7zZfctmtRQs882J15WfRz3Go9QggsIA',
secret: 'AlvxQlaPKJI+25bX8I6TD6EaGbZl1f6Ngu/E9nO4KZCn',
bitcoin: 'A3hQ226XkTCvpEXC3KqxLEZANyNjyMmJhIm8rDwX/EKE',
sei: 'AqYABZ4+Zqqbx7zZfctmtRQs882J15WfRz3Go9QggsIA',
signet: 'A0Ygge2Va8zePPPtwVyb8dt5Lf5fH9SvPxUdVylD5+cC',
},
walletType: 0,
},
};

export const evmAddresses = [
{
addressIndex: 0,
address: '0xaaa7bc446be72afcdd6d6041fda037b8cda9b493',
},
{
addressIndex: 1,
address: '0x737f3cd68e5e9eed1c3c79f8a717041776647c5e',
},
];

export const altEvmAddresses = {
sei: {
'0': {
coinType: '118',
address: '0x84a07314cd082feaacbc49487eede93cba01ea00',
},
'1': {
coinType: '118',
address: '0x5b6a94c44843091692650719d95fee49081919fa',
},
},
};

export const addressPrefixes = {
cosmos: 'cosmos',
secret: 'secret',
Expand All @@ -80,12 +109,14 @@ export const addressPrefixes = {
injective: 'inj',
bitcoin: 'bc1q',
signet: 'tb1q',
sei: 'sei',
};

export const coinTypes = {
cosmos: 118,
juno: 118,
osmosis: 118,
sei: 118,
secret: 529,
evmos: 60,
injective: 60,
Expand All @@ -95,8 +126,21 @@ export const coinTypes = {

export const chainInfos: Record<
string,
{ addressPrefix: string; coinType: number; useBip84?: boolean; btcNetwork?: typeof NETWORK }
{
addressPrefix: string;
coinType: number;
useBip84?: boolean;
btcNetwork?: typeof NETWORK;
ethWallet?: boolean;
pubKeyBech32Address?: boolean;
}
> = {
bitcoin: {
addressPrefix: addressPrefixes.bitcoin,
coinType: coinTypes.bitcoin,
useBip84: true,
btcNetwork: NETWORK,
},
cosmos: {
addressPrefix: addressPrefixes.cosmos,
coinType: coinTypes.cosmos,
Expand All @@ -113,19 +157,19 @@ export const chainInfos: Record<
addressPrefix: addressPrefixes.juno,
coinType: coinTypes.juno,
},
secret: {
addressPrefix: addressPrefixes.secret,
coinType: coinTypes.secret,
},
osmosis: {
addressPrefix: addressPrefixes.osmosis,
coinType: coinTypes.osmosis,
},
bitcoin: {
addressPrefix: addressPrefixes.bitcoin,
coinType: coinTypes.bitcoin,
useBip84: true,
btcNetwork: NETWORK,
secret: {
addressPrefix: addressPrefixes.secret,
coinType: coinTypes.secret,
},
sei: {
addressPrefix: addressPrefixes.sei,
coinType: coinTypes.sei,
ethWallet: true,
pubKeyBech32Address: true,
},
signet: {
addressPrefix: addressPrefixes.signet,
Expand Down
127 changes: 127 additions & 0 deletions test/wallet-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { mnemonic, chainInfos, referenceWallets, privateKey, altEvmAddresses } from './mockdata';
import { generateWalletFromMnemonic, generateWalletFromPrivateKey } from '../src/key/wallet-utils';
import { EthWallet, getFullHDPath } from '../src';
import { NETWORK } from '@scure/btc-signer';
import expect from 'expect.js';

function getWalletOptions(addressIndex: number): Record<
string,
{
hdPath: string;
addressPrefix: string;
btcNetwork?: typeof NETWORK;
ethWallet: boolean;
pubKeyBech32Address?: boolean;
}
> {
return Object.entries(chainInfos).reduce((acc, [key, value]) => {
const purpose = value.useBip84 ? '84' : '44';
return {
...acc,
[key]: {
hdPath: getFullHDPath(purpose, value.coinType.toString(), addressIndex.toString()),
addressPrefix: value.addressPrefix,
btcNetwork: value.btcNetwork,
ethWallet: !!value.ethWallet,
pubKeyBech32Address: !!value.pubKeyBech32Address,
},
};
}, {});
}

describe('wallet utils', () => {
it('Generates correct wallets from mnemonic', () => {
const addressIndex = 0;
const walletOptions = getWalletOptions(addressIndex);
const accounts = Object.entries(walletOptions)
.filter(([key]) => key !== 'ethereum')
.map(([key, walletOption]) => {
const wallet = generateWalletFromMnemonic(mnemonic, walletOption);
const [account] = wallet.getAccounts();
if (!account) throw new Error();
return {
address: account.address,
pubkey: account.pubkey,
chain: key,
};
});

accounts.sort((a, b) => (a.chain > b.chain ? 1 : -1));
const addresses = accounts.reduce((acc, account) => {
return { ...acc, [account.chain]: account.address };
}, {});

const { ref1 } = referenceWallets;
expect(addresses).to.eql(ref1.addresses);
});
it('Generates correct wallets from private key', () => {
const addressIndex = 0;
const walletOptions = getWalletOptions(addressIndex);

const accounts = Object.entries(walletOptions)
.filter(([key]) => chainInfos[key]?.coinType === 118)
.map(([key, walletOption]) => {
const { hdPath, addressPrefix, btcNetwork, ethWallet, pubKeyBech32Address } = walletOption;

const wallet = generateWalletFromPrivateKey(
privateKey,
hdPath,
addressPrefix,
btcNetwork,
ethWallet,
pubKeyBech32Address,
);
const [account] = wallet.getAccounts();
if (!account) throw new Error();
return {
address: account.address,
pubkey: account.pubkey,
chain: key,
};
});

accounts.sort((a, b) => (a.chain > b.chain ? 1 : -1));
const addresses = accounts.reduce((acc, account) => {
return { ...acc, [account.chain]: account.address };
}, {});

const { ref1 } = referenceWallets;
for (let [key, value] of Object.entries(addresses)) {
//@ts-expect-error: index error
expect(value).to.eql(ref1.addresses[key]);
}
});
it('Generates correct alternate addresses from mnemonic', () => {
const walletOptions = getWalletOptions(0);
const option = walletOptions.sei;
if (option === undefined) throw new Error();

const wallet = generateWalletFromMnemonic(mnemonic, option);
const [account] = wallet.getAccounts();
const [hexAddressAccount] = (wallet as EthWallet).getAccountWithHexAddress();

if (!account || !hexAddressAccount) throw new Error();

expect(account.address).to.equal(referenceWallets.ref1.addresses.sei);
expect(hexAddressAccount.address).to.equal(altEvmAddresses.sei[0].address);
});
it('Generates correct alternate addresses from private key', () => {
const walletOptions = getWalletOptions(0);
const option = walletOptions.sei;
if (!option) throw new Error();
const { hdPath, addressPrefix, ethWallet, pubKeyBech32Address } = option;
const wallet = generateWalletFromPrivateKey(
privateKey,
hdPath,
addressPrefix,
undefined,
ethWallet,
pubKeyBech32Address,
);
const [account] = wallet.getAccounts();
const [hexAddressAccount] = (wallet as EthWallet).getAccountWithHexAddress();
if (!account || !hexAddressAccount) throw new Error();
expect(account.address).to.equal(referenceWallets.ref1.addresses.sei);
expect(hexAddressAccount.address).to.equal(altEvmAddresses.sei[0].address);
});
});

0 comments on commit 0e11ad7

Please sign in to comment.