Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate C-Chain Support in Avalanche Library #157

Merged
merged 4 commits into from
Jan 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
};
}
Loading