Skip to content
Open
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
4 changes: 2 additions & 2 deletions src/accounts/actions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Address as ViemAddress, PublicClient, TransactionReceipt} from "viem";
import {GenLayerClient, TransactionHash, GenLayerChain, Address} from "../types";
import {ClientRequester, TransactionHash, Address} from "../types";
import {localnet} from "../chains";

export function accountActions(client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) {
export function accountActions(client: ClientRequester, publicClient: PublicClient) {
return {
fundAccount: async ({address, amount}: {address: Address; amount: number}): Promise<TransactionHash> => {
if (client.chain?.id !== localnet.id) {
Expand Down
4 changes: 2 additions & 2 deletions src/chains/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {GenLayerClient, GenLayerChain} from "@/types";
import {ChainSwitcher} from "@/types";

export function chainActions(_client: GenLayerClient<GenLayerChain>) {
export function chainActions(_client: ChainSwitcher) {
return {
/**
* @deprecated This method is deprecated and will be removed in a future release.
Expand Down
34 changes: 14 additions & 20 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ import {localnet} from "@/chains";

// Define the configuration interface for the client
interface ClientConfig {
chain?: {
id: number;
name: string;
rpcUrls: {default: {http: readonly string[]}};
nativeCurrency: {name: string; symbol: string; decimals: number};
blockExplorers?: {default: {name: string; url: string}};
};
chain?: GenLayerChain;
endpoint?: string; // Custom RPC endpoint
account?: Account | Address;
provider?: EthereumProvider; // Custom provider for wallet framework integration
Expand Down Expand Up @@ -148,32 +142,32 @@ export const createClient = (config: ClientConfig = {chain: localnet}): GenLayer
const clientWithBasicActions = baseClient
.extend(publicActions)
.extend(walletActions)
.extend(client => accountActions(client as unknown as GenLayerClient<GenLayerChain>, publicClient));
.extend(client => accountActions(client, publicClient));

const clientWithTransactionActions = {
...clientWithBasicActions,
...transactionActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
...chainActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>),
...genlayerWalletActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>),
} as unknown as GenLayerClient<GenLayerChain>;
...transactionActions(clientWithBasicActions, publicClient),
...chainActions(clientWithBasicActions),
...genlayerWalletActions(clientWithBasicActions),
};

const clientWithAllActions = {
...clientWithTransactionActions,
...contractActions(clientWithTransactionActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
} as unknown as GenLayerClient<GenLayerChain>;
...contractActions(clientWithTransactionActions, publicClient),
};

const clientWithReceiptActions = {
...clientWithAllActions,
...receiptActions(clientWithAllActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
} as unknown as GenLayerClient<GenLayerChain>;
...receiptActions(clientWithAllActions, publicClient),
};

const finalClient = {
...clientWithReceiptActions,
...stakingActions(clientWithReceiptActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
...vestingActions(clientWithReceiptActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
} as unknown as GenLayerClient<GenLayerChain>;
...stakingActions(clientWithReceiptActions, publicClient),
...vestingActions(clientWithReceiptActions, publicClient),
};

return finalClient;
return finalClient as GenLayerClient<GenLayerChain>;
};

export const createPublicClient = (
Expand Down
30 changes: 15 additions & 15 deletions src/contracts/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
Account,
ContractSchema,
DeveloperNft,
GenLayerChain,
GenLayerClient,
ClientRequester,
ContractWriter,
CalldataEncodable,
Address,
TransactionHash,
Expand Down Expand Up @@ -133,7 +133,7 @@ function transactionFeesToRpc(fees?: TransactionFeeOptions) {
};
}

export const contractActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => {
export const contractActions = (client: ContractWriter, publicClient: PublicClient) => {
const estimateFeeValue = async (
distribution: FeesDistribution,
policy?: FeePolicyQuote,
Expand Down Expand Up @@ -1310,7 +1310,7 @@ const _resolveAddressManagerAddress = async ({
client,
publicClient,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
}): Promise<`0x${string}`> => {
const consensusMainContract = client.chain.consensusMainContract;
Expand Down Expand Up @@ -1346,7 +1346,7 @@ const _resolveNftMinterAddress = async ({
client,
publicClient,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
}): Promise<`0x${string}`> => {
const addressManagerAddress = await _resolveAddressManagerAddress({client, publicClient});
Expand Down Expand Up @@ -1533,7 +1533,7 @@ const extractStudioFeePolicy = (config: unknown): FeePolicyQuote => {
};

const readCurrentFeePolicy = async (
client: GenLayerClient<GenLayerChain>,
client: ClientRequester,
publicClient: PublicClient,
): Promise<FeePolicyQuote> => {
if (client.chain.isStudio) {
Expand Down Expand Up @@ -2037,7 +2037,7 @@ const _resolveTransactionFees = async ({
fees,
numOfInitialValidators,
}: {
client: GenLayerClient<GenLayerChain>;
client: ClientRequester;
publicClient: PublicClient;
fees?: TransactionFeeOptions;
numOfInitialValidators: number;
Expand Down Expand Up @@ -2095,7 +2095,7 @@ const _encodeAddTransactionData = ({
userValue = 0n,
transactionFees,
}: {
client: GenLayerClient<GenLayerChain>;
client: ClientRequester;
senderAccount?: Account;
recipient?: `0x${string}`;
data?: `0x${string}`;
Expand Down Expand Up @@ -2252,7 +2252,7 @@ const _readLifecycleIdentity = async ({
blockNumber,
blockTimestamp,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
txId: `0x${string}`;
blockNumber?: bigint;
Expand Down Expand Up @@ -2327,7 +2327,7 @@ const _readAppealContext = async ({
txId,
includeQuote = true,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
txId: `0x${string}`;
includeQuote?: boolean;
Expand Down Expand Up @@ -2418,7 +2418,7 @@ const _waitForSentEnvelope = async ({
operationName,
revertDetails,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
evmHash: `0x${string}`;
operationName: string;
Expand Down Expand Up @@ -2468,7 +2468,7 @@ const _sendEvmContractCall = async ({
value = 0n,
operationName = "Contract call",
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
to: Address;
encodedData: `0x${string}`;
Expand Down Expand Up @@ -2546,7 +2546,7 @@ const _sendConsensusCall = async ({
value = 0n,
operationName = "Consensus call",
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
encodedData: `0x${string}`;
senderAccount?: Account;
Expand Down Expand Up @@ -2613,7 +2613,7 @@ const _sendConsensusCall = async ({
* NewTransaction (immediately activated) or CreatedTransaction (queued) events.
*/
const extractTxIdFromLogs = (
client: GenLayerClient<GenLayerChain>,
client: ClientRequester,
logs: any[],
): `0x${string}` | null => {
const newTxEvents = parseEventLogs({
Expand Down Expand Up @@ -2645,7 +2645,7 @@ const _sendTransaction = async ({
transactionVariants,
senderAccount,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractWriter;
publicClient: PublicClient;
transactionVariants: EncodedTransactionVariant[];
senderAccount?: Account;
Expand Down
4 changes: 2 additions & 2 deletions src/staking/actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {getContract, decodeEventLog, PublicClient, Client, Transport, Chain, Account, Address as ViemAddress, GetContractReturnType, toHex, encodeFunctionData, BaseError, ContractFunctionRevertedError, decodeErrorResult, RawContractError, zeroAddress} from "viem";
import {GenLayerClient, GenLayerChain, Address} from "@/types";
import {ClientRequester, Address} from "@/types";
import {STAKING_ABI, VALIDATOR_WALLET_ABI} from "@/abi/staking";
import {ADDRESS_MANAGER_ABI, CONSENSUS_ADDRESS_MANAGER_ABI} from "@/abi/vesting";
import {parseStakingAmount, formatStakingAmount} from "./utils";
Expand Down Expand Up @@ -104,7 +104,7 @@ function extractRevertReason(err: unknown): string {
}

export const stakingActions = (
client: GenLayerClient<GenLayerChain>,
client: ClientRequester,
publicClient: PublicClient,
) => {
const executeWrite = async (options: {
Expand Down
9 changes: 4 additions & 5 deletions src/transactions/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {GenLayerClient} from "../types/clients";
import {TransactionActionInput, TransactionReader} from "../types/clients";
import {
TransactionHash,
TransactionStatus,
Expand All @@ -19,7 +19,6 @@ import {
} from "../types/transactions";
import {transactionsConfig} from "../config/transactions";
import {sleep} from "../utils/async";
import {GenLayerChain} from "@/types";
import {Abi, PublicClient, Address, keccak256, concat, stringToBytes, toBytes, zeroAddress, parseAbiItem} from "viem";
import {decodeLocalnetTransaction, decodeTransaction, simplifyTransactionReceipt} from "./decoders";
import {isMethodNotFoundError, readStudioLifecycleFallback} from "./lifecycleFallback";
Expand Down Expand Up @@ -231,7 +230,7 @@ export const isSuccessful = (transaction: GenLayerTransaction): boolean => {
);
};

export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => ({
export const receiptActions = (client: TransactionReader, publicClient: PublicClient) => ({
/** Polls until a transaction reaches the specified status. Returns the transaction receipt. */
waitForTransactionReceipt: async ({
hash,
Expand Down Expand Up @@ -321,7 +320,7 @@ export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClie
}),
});

export const transactionActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => ({
export const transactionActions = (client: TransactionActionInput, publicClient: PublicClient) => ({
advanced: {
/**
* `advanced.getTransactionLifecycle` exposes stored/projected status,
Expand Down Expand Up @@ -399,7 +398,7 @@ export const transactionActions = (client: GenLayerClient<GenLayerChain>, public
transaction.statusName = localnetStatus as TransactionStatus;
transaction.lifecycle = transactionLifecycleFromStoredStatus(
localnetStatus as TransactionStatus,
transaction.result,
transaction.result as number,
);
return decodeLocalnetTransaction(transaction as unknown as GenLayerTransaction);
}
Expand Down
9 changes: 4 additions & 5 deletions src/transactions/lifecycleFallback.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {MethodNotFoundRpcError} from "viem";

import {GenLayerClient} from "../types/clients";
import {GenLayerChain} from "../types/chains";
import {RawTransactionRead, TransactionActionInput} from "../types/clients";
import {
GenLayerTransaction,
TransactionHash,
Expand Down Expand Up @@ -68,7 +67,7 @@ const UNSPECIFIED_RESOLUTION_SOURCE_CODE = 0;
* while `contractActions` hands over the SDK transaction, whose `status` is
* already the numeric code and whose `statusName` is already normalized.
*/
const storedStatusName = (transaction: GenLayerTransaction): TransactionStatus | undefined => {
const storedStatusName = (transaction: GenLayerTransaction | RawTransactionRead): TransactionStatus | undefined => {
const raw: unknown = transaction.statusName ?? transaction.status;
const byCode = (code: string): TransactionStatus | undefined =>
Object.prototype.hasOwnProperty.call(transactionsStatusNumberToName, code)
Expand Down Expand Up @@ -102,12 +101,12 @@ export const readStudioLifecycleFallback = async ({
timestamp,
cause,
}: {
client: GenLayerClient<GenLayerChain>;
client: TransactionActionInput;
hash: TransactionHash;
timestamp?: number;
cause: unknown;
}): Promise<StudioLifecycleFallback> => {
let transaction: GenLayerTransaction;
let transaction: RawTransactionRead;
try {
transaction = await client.getTransaction({hash});
} catch {
Expand Down
69 changes: 69 additions & 0 deletions src/types/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,75 @@ export type GenLayerMethod =
| {method: "sim_cancelTransaction"; params: [hash: TransactionHash, signature?: string, adminKey?: string]}
| {method: "sim_getFeeConfig"; params: []};

/*
The action factories below describe the slice of the client each one actually
touches, so the composition in `createClient` type-checks step by step instead
of being laundered through `as unknown as GenLayerClient<GenLayerChain>` at
every hop.
*/

/**
* The RPC surface the action factories call.
*
* This is deliberately *not* `GenLayerClient["request"]`: that type is an
* intersection with a GenLayer-specific overload, and no viem client satisfies
* it structurally, which is why every call site previously needed a cast. The
* factories cast their own results anyway, so accepting both call shapes here
* keeps the checks that do matter (method names, argument shapes) while letting
* the chain compose.
*/
export type ClientRequestFn = {
<TMethod extends GenLayerMethod>(
args: Extract<GenLayerMethod, {method: TMethod["method"]}>,
): Promise<unknown>;
(args: {method: string; params?: unknown}): Promise<unknown>;
};

/** `request`, `account` and `chain`: enough for read-only RPC-backed actions. */
export type ClientRequester = {
request: ClientRequestFn;
account?: GenLayerClient<GenLayerChain>["account"];
chain: GenLayerChain;
};

/** Adds the GenLayer-typed `getTransaction` the receipt actions poll with. */
export type TransactionReader = ClientRequester & {
getTransaction: GenLayerClient<GenLayerChain>["getTransaction"];
};

/**
* What `transactionActions` reads from the client it is handed.
*
* It calls the underlying `getTransaction` and then installs its own
* GenLayer-typed override as a returned method, so the input cannot be typed as
* the override it produces. Studio answers with a mutable record carrying a
* string `status` and a `result`, not viem's decoded transaction, and the
* action rewrites those fields in place before decoding.
*/
export type RawTransactionRead = {
status?: unknown;
statusName?: unknown;
result?: unknown;
triggered_transactions?: unknown;
[key: string]: unknown;
};

export type TransactionActionInput = ClientRequester & {
getTransaction: (args: {hash: TransactionHash}) => Promise<RawTransactionRead>;
};

/** Adds the custom write helpers used by the contract actions. */
export type ContractWriter = ClientRequester & {
estimateTransactionGas: GenLayerClient<GenLayerChain>["estimateTransactionGas"];
getCurrentNonce: GenLayerClient<GenLayerChain>["getCurrentNonce"];
sendRawTransaction: WalletActions<GenLayerChain>["sendRawTransaction"];
};

/** `connect` reassigns `chain`, so the wallet actions need it writable. */
export type ChainSwitcher = {
chain: GenLayerChain;
};

/*
Take all the properties from Client<Transport, TGenLayerChain>
Remove getTransaction and readContract because they are redefined with custom implementations.
Expand Down
4 changes: 2 additions & 2 deletions src/vesting/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "viem";
import {ADDRESS_MANAGER_ABI, CONSENSUS_ADDRESS_MANAGER_ABI, VESTING_ABI, VESTING_FACTORY_ABI} from "@/abi/vesting";
import {STAKING_ABI} from "@/abi/staking";
import {Address, GenLayerChain, GenLayerClient} from "@/types";
import {Address, ClientRequester} from "@/types";
import {
VestingCategory,
VestingContract,
Expand Down Expand Up @@ -104,7 +104,7 @@ function encodeExtraCid(extraCid?: string): `0x${string}` {
}

export const vestingActions = (
client: GenLayerClient<GenLayerChain>,
client: ClientRequester,
publicClient: PublicClient,
) => {
const executeWrite = async (options: {
Expand Down
Loading