Skip to content

Commit

Permalink
add solana wallet to zetachain client, update deposit-solana
Browse files Browse the repository at this point in the history
  • Loading branch information
lukema95 committed Nov 22, 2024
1 parent 78fa8d7 commit 126bed5
Show file tree
Hide file tree
Showing 31 changed files with 1,519 additions and 757 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@solana/wallet-adapter-react": "^0.15.35",
"@solana/web3.js": "^1.95.3",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@zetachain/faucet-cli": "^4.1.1",
Expand Down
52 changes: 49 additions & 3 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type { Wallet as SolanaWallet } from "@coral-xyz/anchor";
import type { WalletContextState } from "@solana/wallet-adapter-react";
import { PublicKey } from "@solana/web3.js";
import { networks } from "@zetachain/networks";
import type { Signer, Wallet } from "ethers";
import merge from "lodash/merge";
Expand Down Expand Up @@ -35,16 +38,45 @@ export interface ZetaChainClientParamsBase {

export type ZetaChainClientParams = ZetaChainClientParamsBase &
(
| { signer: Signer; wallet?: never }
| { signer?: never; wallet: Wallet }
| { signer?: undefined; wallet?: undefined }
| {
signer: Signer;
solanaAdapter?: never;
solanaWallet?: never;
wallet?: never;
}
| {
signer?: never;
solanaAdapter: WalletContextState;
solanaWallet?: never;
wallet?: never;
}
| {
signer?: never;
solanaAdapter?: never;
solanaWallet: SolanaWallet;
wallet?: never;
}
| {
signer?: never;
solanaAdapter?: never;
solanaWallet?: never;
wallet: Wallet;
}
| {
signer?: undefined;
solanaAdapter?: undefined;
solanaWallet?: undefined;
wallet?: undefined;
}
);

export class ZetaChainClient {
public chains: { [key: string]: any };
public network: string;
public wallet: Wallet | undefined;
public signer: any | undefined;
public solanaWallet: SolanaWallet | undefined;
public solanaAdapter: WalletContextState | undefined;

/**
* Initializes ZetaChainClient instance.
Expand Down Expand Up @@ -98,6 +130,10 @@ export class ZetaChainClient {
this.wallet = params.wallet;
} else if (params.signer) {
this.signer = params.signer;
} else if (params.solanaWallet) {
this.solanaWallet = params.solanaWallet;
} else if (params.solanaAdapter) {
this.solanaAdapter = params.solanaAdapter;
}
this.chains = { ...networks };
this.network = params.network || "";
Expand All @@ -117,6 +153,16 @@ export class ZetaChainClient {
return this.chains;
}

public isSolanaWalletConnected(): boolean {
return this.solanaAdapter?.connected || this.solanaWallet !== undefined;
}

public getSolanaPublicKey(): PublicKey | null {
return (
this.solanaAdapter?.publicKey || this.solanaWallet?.publicKey || null
);
}

getEndpoint = getEndpoint;
getBalances = getBalances;
getForeignCoins = getForeignCoins;
Expand Down
114 changes: 68 additions & 46 deletions packages/client/src/solanaDeposit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as anchor from "@coral-xyz/anchor";
import { Keypair } from "@solana/web3.js";
import { TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import { Transaction } from "@solana/web3.js";
import { ethers } from "ethers";

import { ZetaChainClient } from "./client";
Expand All @@ -12,20 +13,50 @@ export const solanaDeposit = async function (
args: {
amount: number;
api: string;
idPath: string;
params: any[];
recipient: string;
}
) {
const keypair = await getKeypairFromFile(args.idPath);
const wallet = new anchor.Wallet(keypair);
if (!this.isSolanaWalletConnected()) {
throw new Error("Solana wallet not connected");
}

const connection = new anchor.web3.Connection(args.api);
const provider = new anchor.AnchorProvider(
connection,
wallet,
anchor.AnchorProvider.defaultOptions()
);

let provider;
if (this.solanaAdapter) {
const walletAdapter = {
publicKey: this.solanaAdapter.publicKey!,
signAllTransactions: async (txs: Transaction[]) => {
if (!this.solanaAdapter?.signAllTransactions) {
throw new Error(
"Wallet does not support signing multiple transactions"
);
}
return await this.solanaAdapter.signAllTransactions(txs);
},
signTransaction: async (tx: Transaction) => {
if (!this.solanaAdapter?.signTransaction) {
throw new Error("Wallet does not support transaction signing");
}
return await this.solanaAdapter.signTransaction(tx);
},
};

provider = new anchor.AnchorProvider(
connection,
walletAdapter as any,
anchor.AnchorProvider.defaultOptions()
);
} else if (this.solanaWallet) {
provider = new anchor.AnchorProvider(
connection,
this.solanaWallet,
anchor.AnchorProvider.defaultOptions()
);
} else {
throw new Error("No valid Solana wallet found");
}
anchor.setProvider(provider);

const programId = new anchor.web3.PublicKey(Gateway_IDL.address);
Expand Down Expand Up @@ -58,53 +89,44 @@ export const solanaDeposit = async function (
.deposit(depositAmount, m)
.accounts({
pda: pdaAccount,
signer: wallet.publicKey,
signer: this.solanaAdapter
? this.solanaAdapter.publicKey!
: this.solanaWallet!.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.instruction();

tx.add(depositInstruction);

// Send the transaction
const txSignature = await anchor.web3.sendAndConfirmTransaction(
connection,
tx,
[keypair]
);
let txSignature;
if (this.solanaAdapter) {
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash();
const messageLegacy = new TransactionMessage({
instructions: tx.instructions,
payerKey: this.solanaAdapter.publicKey!,
recentBlockhash: blockhash,
}).compileToV0Message();

console.log("Transaction signature:", txSignature);
} catch (error) {
console.error("Transaction failed:", error);
}
};
const versionedTransaction = new VersionedTransaction(messageLegacy);

const getKeypairFromFile = async (filepath: string) => {
const path = await import("path");
if (filepath[0] === "~") {
const home = process.env.HOME || null;
if (home) {
filepath = path.join(home, filepath.slice(1));
txSignature = await this.solanaAdapter.sendTransaction(
versionedTransaction,
connection
);
} else {
txSignature = await anchor.web3.sendAndConfirmTransaction(
connection,
tx,
[this.solanaWallet!.payer]
);
}
}
// Get contents of file
let fileContents;
try {
const { readFile } = await import("fs/promises");
const fileContentsBuffer = await readFile(filepath);
fileContents = fileContentsBuffer.toString();

console.log("Transaction signature:", txSignature);

return txSignature;
} catch (error) {
throw new Error(`Could not read keypair from file at '${filepath}'`);
}
// Parse contents of file
let parsedFileContents;
try {
parsedFileContents = Uint8Array.from(JSON.parse(fileContents));
} catch (thrownObject) {
const error: any = thrownObject;
if (!error.message.includes("Unexpected token")) {
throw error;
}
throw new Error(`Invalid secret key file at '${filepath}'!`);
console.error("Transaction failed:", error);
}
return Keypair.fromSecretKey(parsedFileContents);
};
42 changes: 40 additions & 2 deletions packages/tasks/src/solanaDeposit.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Wallet } from "@coral-xyz/anchor";
import { Keypair } from "@solana/web3.js";
import bech32 from "bech32";
import { utils } from "ethers";
import { task } from "hardhat/config";
Expand All @@ -9,7 +11,12 @@ export const solanaDeposit = async (
args: any,
hre: HardhatRuntimeEnvironment
) => {
const client = new ZetaChainClient({ network: "testnet" });
const keypair = await getKeypairFromFile(args.idPath);
const wallet = new Wallet(keypair);

const client = new ZetaChainClient({
solanaWallet: wallet,
});
let recipient;
try {
if ((bech32 as any).decode(args.recipient)) {
Expand All @@ -23,7 +30,7 @@ export const solanaDeposit = async (
}
const { amount, api, idPath } = args;
const params = [JSON.parse(args.types), args.values];
await client.solanaDeposit({ amount, api, idPath, params, recipient });
await client.solanaDeposit({ amount, api, params, recipient });
};

task("solana-deposit", "Solana deposit", solanaDeposit)
Expand All @@ -33,3 +40,34 @@ task("solana-deposit", "Solana deposit", solanaDeposit)
.addOptionalParam("idPath", "Path to id.json", "~/.config/solana/id.json")
.addParam("types", "The types of the parameters (example: ['string'])")
.addVariadicPositionalParam("values", "The values of the parameters");

export const getKeypairFromFile = async (filepath: string) => {
const path = await import("path");
if (filepath[0] === "~") {
const home = process.env.HOME || null;
if (home) {
filepath = path.join(home, filepath.slice(1));
}
}
// Get contents of file
let fileContents;
try {
const { readFile } = await import("fs/promises");
const fileContentsBuffer = await readFile(filepath);
fileContents = fileContentsBuffer.toString();
} catch (error) {
throw new Error(`Could not read keypair from file at '${filepath}'`);
}
// Parse contents of file
let parsedFileContents;
try {
parsedFileContents = Uint8Array.from(JSON.parse(fileContents));
} catch (thrownObject) {
const error: any = thrownObject;
if (!error.message.includes("Unexpected token")) {
throw error;
}
throw new Error(`Invalid secret key file at '${filepath}'!`);
}
return Keypair.fromSecretKey(parsedFileContents);
};
2 changes: 2 additions & 0 deletions typechain-types/@openzeppelin/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type * as interfaces from "./interfaces";
export type { interfaces };
import type * as token from "./token";
export type { token };
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type { BaseContract, Signer, utils } from "ethers";

import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "../../../../common";

export interface IERC1155ErrorsInterface extends utils.Interface {
functions: {};

events: {};
}

export interface IERC1155Errors extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;

interface: IERC1155ErrorsInterface;

queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;

listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;

functions: {};

callStatic: {};

filters: {};

estimateGas: {};

populateTransaction: {};
}
Loading

0 comments on commit 126bed5

Please sign in to comment.