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,7 +1,7 @@
import {GenLayerClient, TransactionHash, GenLayerChain, Address} from "../types";
import {TransactionHash, GenLayerChain, Address, BaseActionsClient} from "../types";
import {localnet} from "../chains";

export function accountActions(client: GenLayerClient<GenLayerChain>) {
export function accountActions<TChain extends GenLayerChain>(client: BaseActionsClient<TChain>) {
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,7 +1,7 @@
import {GenLayerClient, GenLayerChain} from "@/types";
import {GenLayerChain, BaseActionsClient} from "@/types";
import {testnetAsimov} from "./testnetAsimov";

export function chainActions(client: GenLayerClient<GenLayerChain>) {
export function chainActions<TChain extends GenLayerChain>(client: BaseActionsClient<TChain>) {
return {
initializeConsensusSmartContract: async (forceReset: boolean = false): Promise<void> => {
if (client.chain?.id === testnetAsimov.id) {
Expand Down
41 changes: 17 additions & 24 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ import {accountActions} from "../accounts/actions";
import {contractActions} from "../contracts/actions";
import {receiptActions, transactionActions} from "../transactions/actions";
import {walletActions as genlayerWalletActions} from "../wallet/actions";
import {GenLayerClient, GenLayerChain} from "@/types";
import {BaseActionsClient, GenLayerClient, GenLayerChain} from "@/types";
import {chainActions} from "@/chains/actions";
import {localnet} from "@/chains";

function mergeActions<TBase extends object, TExt extends object>(base: TBase, ext: TExt): TBase & TExt {
return Object.assign({}, base, ext) as TBase & TExt;
}

// Define the configuration interface for the client
interface ClientConfig {
chain?: {
Expand Down Expand Up @@ -84,7 +88,7 @@ const getCustomTransportConfig = (config: ClientConfig) => {
};

export const createClient = (config: ClientConfig = {chain: localnet}): GenLayerClient<GenLayerChain> => {
const chainConfig = config.chain || localnet;
const chainConfig = (config.chain || localnet) as GenLayerChain;
if (config.endpoint) {
chainConfig.rpcUrls.default.http = [config.endpoint];
}
Expand All @@ -100,32 +104,21 @@ export const createClient = (config: ClientConfig = {chain: localnet}): GenLayer
...(config.account ? {account: config.account} : {}),
});

// First extend with basic actions
const clientWithBasicActions = baseClient
.extend(publicActions)
.extend(walletActions)
.extend(client => accountActions(client as unknown as GenLayerClient<GenLayerChain>));

// Create a client with all actions except transaction actions
const clientWithAllActions = {
...clientWithBasicActions,
...contractActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
...chainActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>),
...genlayerWalletActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>),
...transactionActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
} as unknown as GenLayerClient<GenLayerChain>;

// Add transaction actions last, after all other actions are in place
const finalClient = {
...clientWithAllActions,
...receiptActions(clientWithAllActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
} as unknown as GenLayerClient<GenLayerChain>;
// Extend only with viem actions, then merge custom actions to avoid protected name conflicts
const baseWithViem = baseClient.extend(publicActions).extend(walletActions) as BaseActionsClient<GenLayerChain>;
const withAccounts = mergeActions(baseWithViem, accountActions(baseWithViem));
const withWallet = mergeActions(withAccounts, genlayerWalletActions(withAccounts));
const withChain = mergeActions(withWallet, chainActions(withWallet));
const withContracts = mergeActions(withChain, contractActions(withChain, publicClient));
const withTx = mergeActions(withContracts, transactionActions(withContracts, publicClient));
const finalClient = mergeActions(withTx, receiptActions(withTx, publicClient));
const finalClientTyped: GenLayerClient<GenLayerChain> = finalClient as GenLayerClient<GenLayerChain>;

// Initialize in the background
finalClient.initializeConsensusSmartContract().catch(error => {
finalClientTyped.initializeConsensusSmartContract().catch(error => {
console.error("Failed to initialize consensus smart contract:", error);
});
return finalClient;
return finalClientTyped;
};

export const createPublicClient = (
Expand Down
20 changes: 16 additions & 4 deletions src/contracts/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ import {
} from "@/types";
import {fromHex, toHex, zeroAddress, encodeFunctionData, PublicClient, parseEventLogs} from "viem";

export const contractActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => {
type ContractCapabilities = {
chain: GenLayerChain;
account?: Account;
request: GenLayerClient<GenLayerChain>["request"];
prepareTransactionRequest: GenLayerClient<GenLayerChain>["prepareTransactionRequest"];
sendRawTransaction: GenLayerClient<GenLayerChain>["sendRawTransaction"];
getCurrentNonce: GenLayerClient<GenLayerChain>["getCurrentNonce"];
};

export const contractActions = (
client: ContractCapabilities,
publicClient: PublicClient,
) => {
return {
getContractSchema: async (address: Address): Promise<ContractSchema> => {
if (client.chain.id !== localnet.id) {
Expand Down Expand Up @@ -186,7 +198,7 @@ const _encodeAddTransactionData = ({
data,
consensusMaxRotations = client.chain.defaultConsensusMaxRotations,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractCapabilities;
senderAccount?: Account;
recipient?: `0x${string}`;
data?: `0x${string}`;
Expand All @@ -210,7 +222,7 @@ const _encodeSubmitAppealData = ({
client,
txId,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractCapabilities;
txId: `0x${string}`;
}): `0x${string}` => {
return encodeFunctionData({
Expand All @@ -227,7 +239,7 @@ const _sendTransaction = async ({
senderAccount,
value = 0n,
}: {
client: GenLayerClient<GenLayerChain>;
client: ContractCapabilities;
publicClient: PublicClient;
encodedData: `0x${string}`;
senderAccount?: Account;
Expand Down
38 changes: 26 additions & 12 deletions src/transactions/actions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import {GenLayerClient} from "../types/clients";
import {
TransactionHash,
TransactionStatus,
GenLayerTransaction,
GenLayerRawTransaction,
transactionsStatusNameToNumber,
} from "../types/transactions";
import {BaseActionsClient} from "../types/clients";
import {TransactionHash, TransactionStatus, GenLayerTransaction, GenLayerRawTransaction, transactionsStatusNameToNumber} from "@/types";
import {transactionsConfig} from "../config/transactions";
import {sleep} from "../utils/async";
import {GenLayerChain} from "@/types";
Expand All @@ -15,7 +9,14 @@ import {decodeLocalnetTransaction, decodeTransaction, simplifyTransactionReceipt



export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => ({
type ClientWithGetTransaction<TChain extends GenLayerChain> = BaseActionsClient<TChain> & {
getTransaction: (args: {hash: TransactionHash}) => Promise<GenLayerTransaction>;
};

export const receiptActions = <TChain extends GenLayerChain>(
client: ClientWithGetTransaction<TChain>,
publicClient: PublicClient,
) => ({
waitForTransactionReceipt: async ({
hash,
status = TransactionStatus.ACCEPTED,
Expand Down Expand Up @@ -68,16 +69,29 @@ export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClie
},
});

export const transactionActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => ({
type TransactionCapabilities<TChain extends GenLayerChain> = BaseActionsClient<TChain> & {
// using viem public client for remote branch
};

export const transactionActions = <TChain extends GenLayerChain>(
client: TransactionCapabilities<TChain>,
publicClient: PublicClient,
) => ({
getTransaction: async ({hash}: {hash: TransactionHash}): Promise<GenLayerTransaction> => {
if (client.chain.id === localnet.id) {
const transaction = await client.getTransaction({hash});
// Not using viem's getTransaction here: its protected action signature and return type
// differ from our GenLayerTransaction (localnet adds consensus fields). Direct RPC avoids
// signature conflicts and preserves our expected types.
const transaction = (await client.request({
method: "eth_getTransactionByHash",
params: [hash],
})) as GenLayerTransaction;
const localnetStatus =
(transaction.status as string) === "ACTIVATED" ? TransactionStatus.PENDING : transaction.status;

transaction.status = Number(transactionsStatusNameToNumber[localnetStatus as TransactionStatus]);
transaction.statusName = localnetStatus as TransactionStatus;
return decodeLocalnetTransaction(transaction as unknown as GenLayerTransaction);
return decodeLocalnetTransaction(transaction);
}
const transaction = (await publicClient.readContract({
address: client.chain.consensusDataContract?.address as Address,
Expand Down
14 changes: 14 additions & 0 deletions src/types/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,17 @@ export type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<
txId: `0x${string}`;
}) => Promise<any>;
};

// Base client shape after applying viem public and wallet actions, used by action factories
export type BaseActionsClient<TGenLayerChain extends GenLayerChain> = Client<
Transport,
TGenLayerChain
> &
Omit<PublicActions<Transport, TGenLayerChain>, "getTransaction" | "readContract" | "waitForTransactionReceipt"> &
WalletActions<TGenLayerChain> & {
request: Client<Transport, TGenLayerChain>["request"] & {
<TMethod extends GenLayerMethod>(
args: Extract<GenLayerMethod, {method: TMethod["method"]}>,
): Promise<unknown>;
};
};
4 changes: 2 additions & 2 deletions src/wallet/actions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {connect} from "./connect";
import {GenLayerClient, GenLayerChain, Network, SnapSource} from "@/types";
import {GenLayerChain, Network, SnapSource, BaseActionsClient} from "@/types";
import {metamaskClient} from "@/wallet/metamaskClient";

export function walletActions(client: GenLayerClient<GenLayerChain>) {
export function walletActions<TChain extends GenLayerChain>(client: BaseActionsClient<TChain>) {
return {
connect: (network: Network, snapSource: SnapSource) => connect(client, network, snapSource),
metamaskClient: (snapSource: SnapSource = "npm") => metamaskClient(snapSource),
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/connect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {localnet} from "@/chains/localnet";
import {studionet} from "@/chains/studionet";
import {testnetAsimov} from "@/chains/testnetAsimov";
import {GenLayerClient, GenLayerChain} from "@/types";
import {GenLayerChain} from "@/types";
import {Network} from "@/types/network";
import {SnapSource} from "@/types/snapSource";
import {snapID} from "@/config/snapID";
Expand All @@ -13,7 +13,7 @@ const networks = {
};

export const connect = async (
client: GenLayerClient<GenLayerChain>,
client: { chain?: GenLayerChain },
network: Network = "studionet",
snapSource: SnapSource = "npm",
): Promise<void> => {
Expand Down