Skip to content

Commit

Permalink
Integrate C-Chain Support in Avalanche Library (#157)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
leirbag95 authored Jan 4, 2024
1 parent 887e2cf commit dc5a82e
Showing 1 changed file with 54 additions and 21 deletions.
75 changes: 54 additions & 21 deletions src/accounts/avalanche.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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<KeyPair> {
const keyChain = await getKeyChain();
export async function getKeyPair(privateKey?: string, chain = ChainType.X_CHAIN): Promise<KeyPair> {
const keyChain = await getKeyChain(chain);
const keyPair = keyChain.makeKey();

if (privateKey) {
Expand All @@ -165,14 +171,17 @@ export async function getKeyPair(privateKey?: string): Promise<KeyPair> {
}

/**
* 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<AvalancheAccount> {
const keyPair = await getKeyPair(privateKey);
export async function ImportAccountFromPrivateKey(
privateKey: string,
chain = ChainType.X_CHAIN,
): Promise<AvalancheAccount> {
const keyPair = await getKeyPair(privateKey, chain);
return new AvalancheAccount(keyPair, keyPair.getAddressString(), keyPair.getPublicKey().toString("hex"));
}

Expand All @@ -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,
};
}

0 comments on commit dc5a82e

Please sign in to comment.