Skip to content
Merged
Show file tree
Hide file tree
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
52 changes: 1 addition & 51 deletions src/commands/staking/StakingAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,7 @@ import {createClient, createAccount, formatStakingAmount, parseStakingAmount, ab
import type {GenLayerClient, GenLayerChain, Address} from "genlayer-js/types";
import {readFileSync, existsSync} from "fs";
import {ethers, ZeroAddress} from "ethers";
import {
createPublicClient,
createWalletClient,
http,
type PublicClient,
type WalletClient,
type Chain,
type Account,
type TransactionReceipt,
} from "viem";
import {privateKeyToAccount} from "viem/accounts";
import {createPublicClient, http} from "viem";
import {glHttpConfig, type BrowserSession} from "../../lib/wallet/browserSend";
import {resolveBrowserWalletSession} from "../../lib/wallet/sessionResolver";

Expand Down Expand Up @@ -252,46 +242,6 @@ export class StakingAction extends BaseAction {
return (addr.startsWith("0x") ? addr : `0x${addr}`) as Address;
}

/**
* Get viem clients for direct contract interactions (e.g., ValidatorWallet calls)
* Future: can be extended to support hardware wallets
*/
protected async getViemClients(config: StakingConfig): Promise<{
walletClient: WalletClient<any, Chain, Account>;
publicClient: PublicClient;
signerAddress: Address;
}> {
if (config.account) {
this.accountOverride = config.account;
}
if (config.password) {
this._passwordOverride = config.password;
}

const network = this.getNetwork(config);
const rpcUrl = config.rpc || network.rpcUrls.default.http[0];

const privateKey = await this.getPrivateKeyForStaking();
const account = privateKeyToAccount(privateKey as `0x${string}`);

const publicClient = createPublicClient({
chain: network,
transport: http(rpcUrl, glHttpConfig),
});

const walletClient = createWalletClient({
chain: network,
transport: http(rpcUrl, glHttpConfig),
account,
});

return {
walletClient,
publicClient,
signerAddress: account.address as Address,
};
}

/**
* Get all validators by traversing the validator tree.
* This finds ALL validators including those not yet active/primed.
Expand Down
60 changes: 33 additions & 27 deletions src/commands/staking/setIdentity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import {StakingAction, StakingConfig} from "./StakingAction";
import type {Address} from "genlayer-js/types";
import {abi} from "genlayer-js";
import {toHex} from "viem";
import type {
Address,
GenLayerClient,
GenLayerChain,
SetIdentityOptions as SdkSetIdentityOptions,
StakingTransactionResult,
} from "genlayer-js/types";
import {buildSetIdentityTx} from "../../lib/wallet/txBuilders";

export interface SetIdentityOptions extends StakingConfig {
Expand Down Expand Up @@ -31,38 +35,40 @@ export class SetIdentityAction extends StakingAction {

try {
const validatorWallet = options.validator as Address;
const {walletClient, publicClient} = await this.getViemClients(options);

// Route through the SDK staking client rather than a raw viem
// writeContract. The SDK's executeWrite pins `type: "legacy"` and does
// manual nonce/gas + sign + sendRawTransaction, which the GenLayer
// consensus RPC requires (it has no EIP-1559 fee support, so viem's
// default fee/tx-type negotiation fails). The SDK owns the extraCid
// encoding (hex passthrough vs UTF-8 -> hex). `setIdentity` exists on the
// client at runtime but is missing from the installed genlayer-js
// StakingActions .d.ts — cast to bridge that type gap.
const client = (await this.getStakingClient(options)) as GenLayerClient<GenLayerChain> & {
setIdentity(o: SdkSetIdentityOptions): Promise<StakingTransactionResult>;
};

this.setSpinnerText(`Setting identity for ${validatorWallet}...`);

// Convert extraCid string to bytes (hex)
const extraCidBytes = options.extraCid ? toHex(new TextEncoder().encode(options.extraCid)) : "0x";

const hash = await walletClient.writeContract({
address: validatorWallet,
abi: abi.VALIDATOR_WALLET_ABI,
functionName: "setIdentity",
args: [
options.moniker,
options.logoUri || "",
options.website || "",
options.description || "",
options.email || "",
options.twitter || "",
options.telegram || "",
options.github || "",
extraCidBytes,
],
const result = await client.setIdentity({
validator: validatorWallet,
moniker: options.moniker,
logoUri: options.logoUri,
website: options.website,
description: options.description,
email: options.email,
twitter: options.twitter,
telegram: options.telegram,
github: options.github,
extraCid: options.extraCid,
});

const receipt = await publicClient.waitForTransactionReceipt({hash});

const output: Record<string, any> = {
transactionHash: receipt.transactionHash,
transactionHash: result.transactionHash,
validator: validatorWallet,
moniker: options.moniker,
blockNumber: receipt.blockNumber.toString(),
gasUsed: receipt.gasUsed.toString(),
blockNumber: result.blockNumber.toString(),
gasUsed: result.gasUsed.toString(),
};

// Add optional fields that were set
Expand Down
36 changes: 24 additions & 12 deletions src/commands/staking/setOperator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import {StakingAction, StakingConfig} from "./StakingAction";
import type {Address} from "genlayer-js/types";
import type {
Address,
GenLayerClient,
GenLayerChain,
SetOperatorOptions as SdkSetOperatorOptions,
StakingTransactionResult,
} from "genlayer-js/types";
import {abi} from "genlayer-js";
import {buildTx} from "../../lib/wallet/txBuilders";

Expand All @@ -22,25 +28,31 @@ export class SetOperatorAction extends StakingAction {

try {
const validatorWallet = options.validator as Address;
const {walletClient, publicClient} = await this.getViemClients(options);

// Route through the SDK staking client rather than a raw viem
// writeContract. The SDK's executeWrite pins `type: "legacy"` and does
// manual nonce/gas + sign + sendRawTransaction, which the GenLayer
// consensus RPC requires (it has no EIP-1559 fee support, so viem's
// default fee/tx-type negotiation fails). `setOperator` exists on the
// client at runtime but is missing from the installed genlayer-js
// StakingActions .d.ts — cast to bridge that type gap.
const client = (await this.getStakingClient(options)) as GenLayerClient<GenLayerChain> & {
setOperator(o: SdkSetOperatorOptions): Promise<StakingTransactionResult>;
};

this.setSpinnerText(`Setting operator to ${options.operator}...`);

const hash = await walletClient.writeContract({
address: validatorWallet,
abi: abi.VALIDATOR_WALLET_ABI,
functionName: "setOperator",
args: [options.operator as Address],
const result = await client.setOperator({
validator: validatorWallet,
operator: options.operator as Address,
});

const receipt = await publicClient.waitForTransactionReceipt({hash});

const output = {
transactionHash: receipt.transactionHash,
transactionHash: result.transactionHash,
validator: validatorWallet,
newOperator: options.operator,
blockNumber: receipt.blockNumber.toString(),
gasUsed: receipt.gasUsed.toString(),
blockNumber: result.blockNumber.toString(),
gasUsed: result.gasUsed.toString(),
};

this.succeedSpinner("Operator updated!", output);
Expand Down
28 changes: 16 additions & 12 deletions src/commands/staking/validatorClaim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,29 @@ export class ValidatorClaimAction extends StakingAction {

try {
const validatorWallet = options.validator as Address;
const {walletClient, publicClient} = await this.getViemClients(options);

this.setSpinnerText(`Claiming for validator ${validatorWallet}...`);
// Route through the SDK staking client rather than a raw viem
// writeContract. The SDK's executeWrite pins `type: "legacy"` and does
// manual nonce/gas + sign + sendRawTransaction, which the GenLayer
// consensus RPC requires (it has no EIP-1559 fee support, so viem's
// default fee/tx-type negotiation fails).
const client = await this.getStakingClient(options);

const hash = await walletClient.writeContract({
address: validatorWallet,
abi: abi.VALIDATOR_WALLET_ABI,
functionName: "validatorClaim",
});
this.setSpinnerText(`Claiming for validator ${validatorWallet}...`);

const receipt = await publicClient.waitForTransactionReceipt({hash});
const result = await client.validatorClaim({validator: validatorWallet});

const output = {
transactionHash: receipt.transactionHash,
const output: Record<string, any> = {
transactionHash: result.transactionHash,
validator: validatorWallet,
blockNumber: receipt.blockNumber.toString(),
gasUsed: receipt.gasUsed.toString(),
blockNumber: result.blockNumber.toString(),
gasUsed: result.gasUsed.toString(),
};

if (result.claimedAmount !== undefined) {
output.claimedAmount = this.formatAmount(result.claimedAmount);
}

this.succeedSpinner("Claim successful!", output);
} catch (error: any) {
this.failSpinner("Failed to claim", error.message || error);
Expand Down
7 changes: 1 addition & 6 deletions src/commands/transactions/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,7 @@ export class TraceAction extends BaseAction {
this.startSpinner(`Fetching execution trace for ${txId} (round: ${round})...`);

try {
const result = await client.request({
method: "gen_dbg_traceTransaction" as any,
params: [{txID: txId, round}],
});

const trace = (result as any);
const trace = await client.debugTraceTransaction({hash: txId, round});
if (!trace) {
this.failSpinner("No trace found", `No execution trace found for transaction ${txId}`);
return;
Expand Down
Loading
Loading