From dc5a82eb307b57632b6f975af05263464a6d2dec Mon Sep 17 00:00:00 2001 From: Leirbag Date: Thu, 4 Jan 2024 15:56:36 +0100 Subject: [PATCH] Integrate C-Chain Support in Avalanche Library (#157) * Avalanche NewAccount creation concerning c-chain addresses * declared but never read KeyChain import: fixed * Add evm address like for avalanche account * avalanche account code cleaner --- src/accounts/avalanche.ts | 75 ++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/src/accounts/avalanche.ts b/src/accounts/avalanche.ts index a66d2315..fb52e396 100644 --- a/src/accounts/avalanche.ts +++ b/src/accounts/avalanche.ts @@ -3,32 +3,31 @@ import { ECIESAccount } from "./account"; import { GetVerificationBuffer } from "../messages"; import { BaseMessage, Chain } from "../messages/types"; import { decrypt as secp256k1_decrypt, encrypt as secp256k1_encrypt } from "eciesjs"; -import { KeyPair } from "avalanche/dist/apis/avm"; +import { KeyPair, KeyChain } from "avalanche/dist/apis/avm"; +import { KeyPair as EVMKeyPair } from "avalanche/dist/apis/evm"; import { Avalanche, BinTools, Buffer as AvaBuff } from "avalanche"; import { ChangeRpcParam, JsonRPCWallet, RpcChainType } from "./providers/JsonRPCWallet"; import { BaseProviderWallet } from "./providers/BaseProviderWallet"; import { providers } from "ethers"; - +import { privateToAddress } from "ethereumjs-util"; import { ProviderEncryptionLabel, ProviderEncryptionLib } from "./providers/ProviderEncryptionLib"; - /** * AvalancheAccount implements the Account class for the Avalanche protocol. * It is used to represent an Avalanche account when publishing a message on the Aleph network. */ export class AvalancheAccount extends ECIESAccount { - private signer?: KeyPair; + private signer?: KeyPair | EVMKeyPair; private provider?: BaseProviderWallet; - - constructor(signerOrProvider: KeyPair | BaseProviderWallet, address: string, publicKey?: string) { + constructor(signerOrProvider: KeyPair | EVMKeyPair | BaseProviderWallet, address: string, publicKey?: string) { super(address, publicKey); - - if (signerOrProvider instanceof KeyPair) this.signer = signerOrProvider; + if (signerOrProvider instanceof KeyPair || signerOrProvider instanceof EVMKeyPair) + this.signer = signerOrProvider; if (signerOrProvider instanceof BaseProviderWallet) this.provider = signerOrProvider; } override GetChain(): Chain { if (this.signer) return Chain.AVAX; - if (this.provider) return Chain.ETH; + if (this.provider) return Chain.AVAX; throw new Error("Cannot determine chain"); } @@ -139,15 +138,22 @@ export class AvalancheAccount extends ECIESAccount { } } -async function getKeyChain() { - const ava = new Avalanche(); - const xChain = ava.XChain(); +export enum ChainType { + C_CHAIN = "C", + X_CHAIN = "X", +} - return xChain.keyChain(); +/** + * Get Key Chains + * @param chain Avalanche chain type: c-chain | x-chain + * @returns key chains + */ +async function getKeyChain(chain = ChainType.X_CHAIN) { + return new KeyChain(new Avalanche().getHRP(), chain); } -export async function getKeyPair(privateKey?: string): Promise { - const keyChain = await getKeyChain(); +export async function getKeyPair(privateKey?: string, chain = ChainType.X_CHAIN): Promise { + const keyChain = await getKeyChain(chain); const keyPair = keyChain.makeKey(); if (privateKey) { @@ -165,14 +171,17 @@ export async function getKeyPair(privateKey?: string): Promise { } /** - * Imports an Avalanche account given a private key. + * Imports an Avalanche account given a private key and chain * * It creates an Avalanche keypair containing information about the account, extracted in the AvalancheAccount constructor. * * @param privateKey The private key of the account to import. */ -export async function ImportAccountFromPrivateKey(privateKey: string): Promise { - const keyPair = await getKeyPair(privateKey); +export async function ImportAccountFromPrivateKey( + privateKey: string, + chain = ChainType.X_CHAIN, +): Promise { + const keyPair = await getKeyPair(privateKey, chain); return new AvalancheAccount(keyPair, keyPair.getAddressString(), keyPair.getPublicKey().toString("hex")); } @@ -197,16 +206,40 @@ export async function GetAccountFromProvider( throw new Error("Insufficient permissions"); } +/** + * Retrieves the EVM compatible address for the current account. + * This function works sspecifically with the C-Chain. + * + * If the current signer is not associated with the C-Chain, + * the function throws an error. + * + * @returns A Promise that resolves to the EVM-style address of the account + * @throws An error if the current signer is not associated with the C-Chain + */ +function getEVMAddress(keypair: EVMKeyPair): string { + const pkHex = keypair.getPrivateKey().toString("hex"); + const pkBuffNative = Buffer.from(pkHex, "hex"); + const ethAddress = privateToAddress(pkBuffNative).toString("hex"); + return `0x${ethAddress}`; +} + /** * Creates a new Avalanche account using a randomly generated privateKey * */ -export async function NewAccount(): Promise<{ account: AvalancheAccount; privateKey: string }> { - const keypair = await getKeyPair(); +export async function NewAccount( + chain = ChainType.X_CHAIN, +): Promise<{ account: AvalancheAccount; privateKey: string }> { + const keypair = await getKeyPair(undefined, chain); const privateKey = keypair.getPrivateKey().toString("hex"); + let address: string = keypair.getAddressString(); + if (chain === ChainType.C_CHAIN) { + address = getEVMAddress(keypair); + } + return { - account: new AvalancheAccount(keypair, keypair.getAddressString(), keypair.getPublicKey().toString("hex")), + account: new AvalancheAccount(keypair, address, keypair.getPublicKey().toString("hex")), privateKey, }; }