From 0d558095c4c420a63d7e0e222327236762b7297e Mon Sep 17 00:00:00 2001 From: Musa AbdulKareem Date: Sun, 16 Mar 2025 17:14:17 +0100 Subject: [PATCH 01/10] feat(paymaster): this adds paymaster features using a normalized api --- app/starkweb-client/src/index.ts | 85 +++++++--------- app/starkweb-client/src/paymaster.ts | 12 +++ .../src/actions/paymaster/buildTypedData.ts | 27 ++++++ .../paymaster/checkAccountCompatibility.ts | 23 +++++ .../actions/paymaster/executeTransaction.ts | 27 ++++++ .../actions/paymaster/getAccountRewards.ts | 24 +++++ .../actions/paymaster/getGasTokenPrices.ts | 21 ++++ .../actions/paymaster/getPaymasterStatus.ts | 21 ++++ .../starkweb/src/actions/paymaster/index.ts | 53 ++++++++++ .../src/clients/createPaymasterClient.ts | 48 ++++++++++ .../src/clients/decorators/paymaster.ts | 82 ++++++++++++++++ packages/starkweb/src/exports/starkweb.ts | 12 +++ packages/starkweb/src/types/paymaster.ts | 96 +++++++++++++++++++ packages/starkweb/src/types/snip1193.ts | 54 ++++++++++- 14 files changed, 534 insertions(+), 51 deletions(-) create mode 100644 app/starkweb-client/src/paymaster.ts create mode 100644 packages/starkweb/src/actions/paymaster/buildTypedData.ts create mode 100644 packages/starkweb/src/actions/paymaster/checkAccountCompatibility.ts create mode 100644 packages/starkweb/src/actions/paymaster/executeTransaction.ts create mode 100644 packages/starkweb/src/actions/paymaster/getAccountRewards.ts create mode 100644 packages/starkweb/src/actions/paymaster/getGasTokenPrices.ts create mode 100644 packages/starkweb/src/actions/paymaster/getPaymasterStatus.ts create mode 100644 packages/starkweb/src/actions/paymaster/index.ts create mode 100644 packages/starkweb/src/clients/createPaymasterClient.ts create mode 100644 packages/starkweb/src/clients/decorators/paymaster.ts create mode 100644 packages/starkweb/src/types/paymaster.ts diff --git a/app/starkweb-client/src/index.ts b/app/starkweb-client/src/index.ts index acfd4d3..599d2c6 100644 --- a/app/starkweb-client/src/index.ts +++ b/app/starkweb-client/src/index.ts @@ -1,54 +1,41 @@ -/** - * StarkWeb Client - A client for interacting with Starknet - */ +import { createPaymasterClient, http, parseEther } from "starkweb" +import { mainnet, sepolia } from "starkweb/chains" -// Import from the starkweb workspace package -import { - createPublicClient, - createWalletClient, - custom, - erc20Abi, - http, -} from 'starkweb'; -import { getStarknetId, getStarknetIdName } from 'starkweb/actions'; -import { mainnet, sepolia } from 'starkweb/chains'; +const paymasterClient = createPaymasterClient({ + chain: sepolia, + transport: http('http://localhost:3000/gasless'), + type: 'paymasterClient', +}) +// get paymaster status +const status = await paymasterClient.getPaymasterStatus() +console.log(status) -const publicClient = createPublicClient({ - chain: mainnet, - transport: http('https://starknet-mainnet.infura.io/v3/db72641028ee47f5b18bcbb791a3f829'), -}); -const id = await getStarknetId(publicClient, { - domain: 'solene.stark', -}) -// const name = await getStarknetIdName(publicClient, { -// address: '0x061b6c0a78f9edf13cea17b50719f3344533fadd470b8cb29c2b4318014f52d3', +// // check account compatibility +// const compatibility = await paymasterClient.checkAccountCompatibility({ +// accountAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", // }) -console.log(id) -// console.log(name) +// console.log(compatibility) + +// get gas token prices +const gasTokenPrices = await paymasterClient.getGasTokenPrices() +console.log(gasTokenPrices) + +// get account rewards +const accountRewards = await paymasterClient.getAccountRewards({ + accountAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", +}) +console.log(accountRewards) -// const result = await publicClient.readContracts({ -// contracts: [ -// { -// abi: erc20Abi, -// functionName: 'balanceOf', -// args: -// { -// account: '0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91', -// }, -// address: -// '0x04718f5a0Fc34cC1AF16A1cdee98fFB20C31f5cD61D6Ab07201858f4287c938D', -// }, -// { -// abi: erc20Abi, -// functionName: 'balanceOf', -// args: -// { -// account: '0x034abecf49cedc634d0c3145da7b1caea99d8d4f2da5b5d41e532ea05192d523', -// }, -// address: -// '0x04718f5a0Fc34cC1AF16A1cdee98fFB20C31f5cD61D6Ab07201858f4287c938D', -// }, -// ], -// }) -// console.log(result); +// build transaction +const transaction = await paymasterClient.buildTypedData({ + userAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", + calls: [ + { + entry_point: "approve", + contractAddress: '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7', + calldata: ['0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91', '0xf', '0x0'], + }, + ], +}) +console.log(transaction) diff --git a/app/starkweb-client/src/paymaster.ts b/app/starkweb-client/src/paymaster.ts new file mode 100644 index 0000000..f0ec9fc --- /dev/null +++ b/app/starkweb-client/src/paymaster.ts @@ -0,0 +1,12 @@ +import { createPaymasterClient, http } from "starkweb" +import { mainnet } from "starkweb/chains" + +const paymasterClient = createPaymasterClient({ + chain: mainnet, + transport: http(), + type: 'paymasterClient', +}) + +const status = await paymasterClient.getPaymasterStatus() +console.log(status) + diff --git a/packages/starkweb/src/actions/paymaster/buildTypedData.ts b/packages/starkweb/src/actions/paymaster/buildTypedData.ts new file mode 100644 index 0000000..d9575b4 --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/buildTypedData.ts @@ -0,0 +1,27 @@ +import type { Client } from '../../clients/createClient.js' +import type { Transport } from '../../clients/transports/createTransport.js' +import type { Chain } from '../../types/chain.js' +import type { Call, TypedData } from '@starknet-io/types-js' + +export type BuildTypedDataParameters = { + userAddress: string + calls: Call[] + gasTokenAddress?: string + maxGasTokenAmount?: string + accountClassHash?: string +} + +export type BuildTypedDataReturnType = TypedData + +export type BuildTypedDataErrorType = Error + +export async function buildTypedData( + client: Client, + parameters: BuildTypedDataParameters +): Promise { + const response = await client.request({ + method: 'pm_buildTypedData', + params: parameters + }) + return response as BuildTypedDataReturnType +} \ No newline at end of file diff --git a/packages/starkweb/src/actions/paymaster/checkAccountCompatibility.ts b/packages/starkweb/src/actions/paymaster/checkAccountCompatibility.ts new file mode 100644 index 0000000..9d300d0 --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/checkAccountCompatibility.ts @@ -0,0 +1,23 @@ +import type { Client } from '../../clients/createClient.js' +import type { Transport } from '../../clients/transports/createTransport.js' +import type { Chain } from '../../types/chain.js' +import type { GaslessCompatibility } from '../../types/paymaster.js' +import type { ADDRESS } from '../../types/components.js' +export type CheckAccountCompatibilityParameters = { + accountAddress: ADDRESS +} + +export type CheckAccountCompatibilityReturnType = GaslessCompatibility + +export type CheckAccountCompatibilityErrorType = Error + +export async function checkAccountCompatibility( + client: Client, + { accountAddress }: CheckAccountCompatibilityParameters +): Promise { + const response = await client.request({ + method: 'pm_checkAccountCompatibility', + params: { accountAddress } + }) + return response as CheckAccountCompatibilityReturnType +} \ No newline at end of file diff --git a/packages/starkweb/src/actions/paymaster/executeTransaction.ts b/packages/starkweb/src/actions/paymaster/executeTransaction.ts new file mode 100644 index 0000000..c8b6d81 --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/executeTransaction.ts @@ -0,0 +1,27 @@ +import type { Signature } from "@starknet-io/types-js" +import type { DeploymentData, InvokeResponse } from "../../types/paymaster.js" +import type { Transport } from "../../clients/transports/createTransport.js" +import type { Client } from "../../clients/createClient.js" +import type { Chain } from "../../types/chain.js" + +export type ExecuteTransactionParameters = { + userAddress: string + typedData: string + signature: Signature + deploymentData?: DeploymentData +} + +export type ExecuteTransactionReturnType = InvokeResponse + +export type ExecuteTransactionErrorType = Error + +export async function executeTransaction( + client: Client, + parameters: ExecuteTransactionParameters +): Promise { + const response = await client.request({ + method: 'pm_executeTransaction', + params: parameters + }) + return response as ExecuteTransactionReturnType +} \ No newline at end of file diff --git a/packages/starkweb/src/actions/paymaster/getAccountRewards.ts b/packages/starkweb/src/actions/paymaster/getAccountRewards.ts new file mode 100644 index 0000000..0875bda --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/getAccountRewards.ts @@ -0,0 +1,24 @@ +import type { PaymasterReward } from '../../types/paymaster.js' +import type { Client } from '../../clients/createClient.js' +import type { Transport } from '../../clients/transports/createTransport.js' +import type { Chain } from '../../types/chain.js' +import type { ADDRESS } from '../../types/components.js' + +export type GetAccountRewardsParameters = { + accountAddress: ADDRESS +} + +export type GetAccountRewardsReturnType = PaymasterReward[] + +export type GetAccountRewardsErrorType = Error + +export async function getAccountRewards( + client: Client, + { accountAddress }: GetAccountRewardsParameters +): Promise { + const response = await client.request({ + method: 'pm_getAccountRewards', + params: { accountAddress } + }) + return response as GetAccountRewardsReturnType +} \ No newline at end of file diff --git a/packages/starkweb/src/actions/paymaster/getGasTokenPrices.ts b/packages/starkweb/src/actions/paymaster/getGasTokenPrices.ts new file mode 100644 index 0000000..325532d --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/getGasTokenPrices.ts @@ -0,0 +1,21 @@ +import type { Client } from '../../clients/createClient.js' +import type { Transport } from '../../clients/transports/createTransport.js' +import type { Chain } from '../../types/chain.js' +import type { GasTokenPrice } from '../../types/paymaster.js' + +export type GetGasTokenPricesParameters = undefined + +export type GetGasTokenPricesReturnType = GasTokenPrice[] + +export type GetGasTokenPricesErrorType = Error + +export async function getGasTokenPrices( + client: Client, + args: GetGasTokenPricesParameters, +): Promise { + const response = await client.request({ + method: 'pm_getGasTokenPrices', + params: args, + }) + return response as GetGasTokenPricesReturnType +} \ No newline at end of file diff --git a/packages/starkweb/src/actions/paymaster/getPaymasterStatus.ts b/packages/starkweb/src/actions/paymaster/getPaymasterStatus.ts new file mode 100644 index 0000000..307cf82 --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/getPaymasterStatus.ts @@ -0,0 +1,21 @@ + + +import type { Client } from '../../clients/createClient.js'; +import type { Transport } from '../../clients/transports/createTransport.js'; +import type { Chain } from '../../types/chain.js'; +import type { GaslessStatus } from '../../types/paymaster.js'; + +export type GetPaymasterStatusParameters = undefined +export type GetPaymasterStatusReturnType = GaslessStatus +export type GetPaymasterStatusErrorType = any + +export async function getPaymasterStatus( + client: Client, + args: GetPaymasterStatusParameters, +): Promise { + const result = await client.request({ + method: 'pm_getPaymasterStatus', + params: args, + }) + return result +} diff --git a/packages/starkweb/src/actions/paymaster/index.ts b/packages/starkweb/src/actions/paymaster/index.ts new file mode 100644 index 0000000..637229c --- /dev/null +++ b/packages/starkweb/src/actions/paymaster/index.ts @@ -0,0 +1,53 @@ +export { + getPaymasterStatus, + type GetPaymasterStatusParameters, + type GetPaymasterStatusReturnType, + type GetPaymasterStatusErrorType, +} from './getPaymasterStatus.js' + +export { + checkAccountCompatibility, + type CheckAccountCompatibilityParameters, + type CheckAccountCompatibilityReturnType, + type CheckAccountCompatibilityErrorType, +} from './checkAccountCompatibility.js' + +export { + getGasTokenPrices, + type GetGasTokenPricesParameters, + type GetGasTokenPricesReturnType, + type GetGasTokenPricesErrorType, +} from './getGasTokenPrices.js' + +export { + getAccountRewards, + type GetAccountRewardsParameters, + type GetAccountRewardsReturnType, + type GetAccountRewardsErrorType, +} from './getAccountRewards.js' + +export { + buildTypedData, + type BuildTypedDataParameters, + type BuildTypedDataReturnType, + type BuildTypedDataErrorType, +} from './buildTypedData.js' + +export { + executeTransaction, + type ExecuteTransactionParameters, + type ExecuteTransactionReturnType, + type ExecuteTransactionErrorType, +} from './executeTransaction.js' + +// export type { +// ADDRESS, +// GaslessStatus, +// GaslessCompatibility, +// GasTokenPrice, +// PaymasterReward, +// TypedData, +// DeploymentData, +// InvokeResponse, +// Signature, +// } from './types.js' \ No newline at end of file diff --git a/packages/starkweb/src/clients/createPaymasterClient.ts b/packages/starkweb/src/clients/createPaymasterClient.ts new file mode 100644 index 0000000..1c63e28 --- /dev/null +++ b/packages/starkweb/src/clients/createPaymasterClient.ts @@ -0,0 +1,48 @@ +import type { Address } from 'abitype' +import type { Account, ParseAccount } from '../types/account.js' +import type { Chain } from '../types/chain.js' +import type { RpcSchema } from '../types/eip1193.js' +import type { Prettify } from '../types/utils.js' +import { + type Client, + type ClientConfig, + createClient, + type CreateClientErrorType, +} from './createClient.js' +import { type PaymasterActions, paymasterActions } from './decorators/paymaster.js' +import type { Transport } from './transports/createTransport.js' +import type { ErrorType } from '../errors/utils.js' + +export type PaymasterClientConfig< + transport extends Transport, + chain extends Chain, + accountOrAddress extends Account | Address | undefined = undefined, + rpcSchema extends RpcSchema | undefined = undefined, +> = ClientConfig & { + type: 'paymasterClient' +} + +export type PaymasterClient< + transport extends Transport, + chain extends Chain, + accountOrAddress extends Account | Address | undefined = undefined, + rpcSchema extends RpcSchema | undefined = undefined, +> = Client, rpcSchema, PaymasterActions> + +export type CreatePaymasterClientErrorType = CreateClientErrorType | ErrorType + +export function createPaymasterClient< + transport extends Transport, + chain extends Chain, + accountOrAddress extends Account | Address | undefined = undefined, + rpcSchema extends RpcSchema | undefined = undefined, +>( + parameters: PaymasterClientConfig, +): Prettify< + Client, rpcSchema, PaymasterActions> +> { + return createClient({ + ...parameters, + type: 'paymasterClient', + }).extend(paymasterActions) +} \ No newline at end of file diff --git a/packages/starkweb/src/clients/decorators/paymaster.ts b/packages/starkweb/src/clients/decorators/paymaster.ts new file mode 100644 index 0000000..a7cf30d --- /dev/null +++ b/packages/starkweb/src/clients/decorators/paymaster.ts @@ -0,0 +1,82 @@ +import type { Client } from '../createClient.js' +import type { GaslessStatus, GaslessCompatibility, GasTokenPrice, DeploymentData, InvokeResponse, PaymasterReward } from '../../types/paymaster.js' +import type { Signature } from '@starknet-io/types-js' +import type { ADDRESS } from '../../types/components.js' + +import { + getPaymasterStatus, + checkAccountCompatibility, + getGasTokenPrices, + getAccountRewards, + buildTypedData, + executeTransaction, + type BuildTypedDataParameters, + type BuildTypedDataReturnType, +} from '../../actions/paymaster/index.js' + + +export type PaymasterActions = { + /** + * Gets the current status of the paymaster service. + * + * @returns The paymaster service status. + */ + getPaymasterStatus: () => Promise + + /** + * Checks if an account is compatible with the paymaster service. + * + * @param args - The account address to check. + * @returns The compatibility status of the account. + */ + checkAccountCompatibility: (args: { accountAddress: ADDRESS }) => Promise + + /** + * Gets the current gas token prices supported by the paymaster. + * + * @returns An array of gas token prices. + */ + getGasTokenPrices: () => Promise + + /** + * Gets the rewards available for an account. + * + * @param args - The account address to check. + * @returns The rewards available for the account. + */ + getAccountRewards: (args: { accountAddress: ADDRESS }) => Promise + + /** + * Builds typed data for a transaction to be executed via the paymaster. + * + * @param args - The parameters for building typed data. + * @returns The typed data for the transaction. + */ + buildTypedData: (args: BuildTypedDataParameters) => Promise + + /** + * Executes a transaction via the paymaster service. + * + * @param args - The transaction parameters including user address, typed data, and signature. + * @returns The transaction response. + */ + executeTransaction: (args: { + userAddress: string + typedData: string + signature: Signature + deploymentData?: DeploymentData + }) => Promise +} + +export function paymasterActions( + client: Client +): PaymasterActions { + return { + getPaymasterStatus: () => getPaymasterStatus(client, undefined), + checkAccountCompatibility: (args) => checkAccountCompatibility(client, args), + getGasTokenPrices: () => getGasTokenPrices(client, undefined), + getAccountRewards: (args) => getAccountRewards(client, args), + buildTypedData: (args) => buildTypedData(client, args), + executeTransaction: (args) => executeTransaction(client, args), + } +} diff --git a/packages/starkweb/src/exports/starkweb.ts b/packages/starkweb/src/exports/starkweb.ts index bdc95fc..01e5226 100644 --- a/packages/starkweb/src/exports/starkweb.ts +++ b/packages/starkweb/src/exports/starkweb.ts @@ -110,6 +110,13 @@ export { type CreateWalletClientErrorType, createWalletClient, } from '../clients/createWalletClient.js' + + export { + type PaymasterClient, + type PaymasterClientConfig, + type CreatePaymasterClientErrorType, + createPaymasterClient, + } from '../clients/createPaymasterClient.js' export { type PublicActions, @@ -120,6 +127,11 @@ export { type WalletActions, walletActions, } from '../clients/decorators/wallet.js' + + export { + type PaymasterActions, + paymasterActions, + } from '../clients/decorators/paymaster.js' export { type Transport, diff --git a/packages/starkweb/src/types/paymaster.ts b/packages/starkweb/src/types/paymaster.ts new file mode 100644 index 0000000..abc08c3 --- /dev/null +++ b/packages/starkweb/src/types/paymaster.ts @@ -0,0 +1,96 @@ +export interface GaslessStatus { + //The gasless status + status: boolean; + } + + export interface GaslessCompatibility { + //Indicates if the account is compatible with the gasless service + isCompatible: boolean; + //The validation's gas consumed overhead + gasConsumedOverhead: bigint; + //The validation's data gas consumed overhead + dataGasConsumedOverhead: bigint; + } + + export interface GasTokenPrice { + // The gas token's address + tokenAddress: string; + // The price of 1 token in ETH + priceInETH: bigint; + // The price of 1 token in USD + priceInUSD: number; + // The token's number of decimals + decimals: number; + } + + export interface PaymasterReward { + // Reward's creation date + date: Date; + // The user's address + address: string; + // The company that will pay the gas fees + sponsor: string; + // The name of the company's campaign + campaign: string; + // The protocol where the reward can be used + protocol: string | undefined; + // The number of free transaction + freeTx: number; + // The number of remaining transactions + remainingTx: number; + // Reward's expiration date + expirationDate: Date | undefined; + // The list of whitelisted calls + whitelistedCalls: WhitelistedCall[]; + } + + export interface WhitelistedCall { + // The value can be '*' if all contracts are whitelisted or can be the contract address (hex format) + contractAddress: string; + // The value can be '*' if all entrypoint are whitelisted or can be the entrypoint name (string format) + entrypoint: string; + } + + export interface AccountsRewardsOptions { + sponsor?: string; + campaign?: string; + protocol?: string; + } + + export interface DeploymentData { + class_hash: string; + salt: string; + unique: string; + calldata: string[]; + sigdata?: string[]; + } + + export interface ExecuteCallsOptions { + gasTokenAddress?: string; + maxGasTokenAmount?: bigint; + deploymentData?: DeploymentData; + } + + export interface GaslessOptions { + baseUrl?: string; + // The api key allows you to sponsor the gas fees for your users + apiKey?: string; + abortSignal?: AbortSignal; + apiPublicKey?: string; + } + + export interface RequestError { + messages: string[]; + revertError: string | undefined; + } + + export interface InvokeResponse { + transactionHash: string; + } + + export class ContractError { + constructor( + public message: string, + public revertError: string, + ) {} + } \ No newline at end of file diff --git a/packages/starkweb/src/types/snip1193.ts b/packages/starkweb/src/types/snip1193.ts index 69d9419..702e123 100644 --- a/packages/starkweb/src/types/snip1193.ts +++ b/packages/starkweb/src/types/snip1193.ts @@ -1,4 +1,4 @@ -import type { PADDED_FELT, PADDED_TXN_HASH } from '@starknet-io/types-js' +import type { Call, PADDED_FELT, PADDED_TXN_HASH, Signature, TypedData } from '@starknet-io/types-js' import type { AddInvokeTransactionParameters, AddInvokeTransactionResult, @@ -54,11 +54,12 @@ import type { TXN_STATUS, TYPED_DATA, } from './components.js' +import type { DeploymentData, GaslessCompatibility, GasTokenPrice, GaslessStatus, PaymasterReward, InvokeResponse } from './paymaster.js' ////////////////////////////////////////////////// // Provider -export type SNIP1474Methods = [...PublicRpcSchema, ...WalletRpcSchema] +export type SNIP1474Methods = [...PublicRpcSchema, ...WalletRpcSchema, ...PaymasterRpcSchema] export type SNIP1193Provider = Prettify< SNIP1193Events & { @@ -645,6 +646,55 @@ export type WalletRpcSchema = [ }, ] +export type PaymasterRpcSchema = [ + { + Method: 'pm_getPaymasterStatus' + Parameters?: undefined + ReturnType: GaslessStatus + }, + { + Method: 'pm_checkAccountCompatibility' + Parameters?: { + accountAddress: ADDRESS + } + ReturnType: GaslessCompatibility + }, + { + Method: 'pm_getGasTokenPrices' + Parameters?: undefined + ReturnType: GasTokenPrice[] + }, + { + Method: 'pm_getAccountRewards' + Parameters?: { + accountAddress: ADDRESS + } + ReturnType: PaymasterReward[] + }, + { + Method: 'pm_buildTypedData' + Parameters?: { + userAddress: string, + calls: Call[], + gasTokenAddress?: string, + maxGasTokenAmount?: string, + accountClassHash?: string + } + ReturnType: TypedData + }, + { + Method: 'pm_executeTransaction' + Parameters?: { + userAddress: string, + typedData: string, + signature: Signature, + deploymentData?: DeploymentData + } + ReturnType: InvokeResponse + }, + +] + // export type TestRpcSchema = [ // ] From cd65327eb414ba9e477e850c706fe4e9ec7ab839 Mon Sep 17 00:00:00 2001 From: Musa AbdulKareem Date: Mon, 17 Mar 2025 14:38:46 +0100 Subject: [PATCH 02/10] fix(paymaster) all the actions are working --- app/next/package.json | 2 +- app/next/pages/paymaster.tsx | 332 ++++++++++++++++++ app/starkweb-client/src/index.ts | 16 +- .../src/actions/paymaster/buildTypedData.ts | 3 +- .../src/clients/createPaymasterClient.ts | 14 +- packages/starkweb/src/types/snip1193.ts | 3 +- 6 files changed, 357 insertions(+), 13 deletions(-) create mode 100644 app/next/pages/paymaster.tsx diff --git a/app/next/package.json b/app/next/package.json index c15df1b..f772b19 100644 --- a/app/next/package.json +++ b/app/next/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --experimental-https --hostname wisemrmusa.fun", + "dev": "next dev --experimental-https --hostname wisemrmusa.fun --port 3005", "build": "next build", "start": "next start", "lint": "next lint" diff --git a/app/next/pages/paymaster.tsx b/app/next/pages/paymaster.tsx new file mode 100644 index 0000000..7720e96 --- /dev/null +++ b/app/next/pages/paymaster.tsx @@ -0,0 +1,332 @@ +"use client" + +import { useState, useEffect } from 'react'; +import { createPaymasterClient, createWalletClient, custom, http, Signature, WalletClient, withRetry } from 'starkweb'; +import { mainnet, sepolia } from 'starkweb/chains'; +import 'starkweb/window' + + +interface TransactionCall { + entrypoint: string; + contractAddress: string; + calldata: string[]; +} + +interface BuildTransactionResult { + types: any; // Update this type based on actual response structure + primaryType: any; // Update this type based on actual response structure + domain: any; // Update this type based on actual response structure + message: any; // Update this type based on actual response structure +} + +export default function PaymasterPage() { + + + + + // Group all useState hooks at the top + const [status, setStatus] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [network, setNetwork] = useState<'mainnet' | 'sepolia'>('sepolia'); + const [accountAddress, setAccountAddress] = useState(''); + const [accountRewards, setAccountRewards] = useState(null); + const [rewardsLoading, setRewardsLoading] = useState(false); + const [rewardsError, setRewardsError] = useState(null); + const [transactionResult, setTransactionResult] = useState(null); + const [buildingTransaction, setBuildingTransaction] = useState(false); + const [transactionError, setTransactionError] = useState(null); + const [signingTransaction, setSigningTransaction] = useState(false); + const [walletClient, setWalletClient] = useState(null); + const [ signature, setSignature] = useState(null); + + useEffect(() => { + // Only run on client-side + if (typeof window === 'undefined') return; + const walletClient = createWalletClient({ + chain: network === 'mainnet' ? mainnet : sepolia, + transport: custom(window.starknet_argentX!), + }); + setWalletClient(walletClient); + + async function fetchPaymasterStatus() { + try { + setLoading(true); + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`http://localhost:3003/paymaster/${network}`), + }); + + const paymasterStatus = await paymasterClient.getPaymasterStatus(); + console.log(paymasterStatus); + setStatus(paymasterStatus); + setError(null); + } catch (err) { + console.error('Error fetching paymaster status:', err); + setError('Failed to fetch paymaster status'); + } finally { + setLoading(false); + } + } + + fetchPaymasterStatus(); + }, [network]); + + const fetchAccountRewards = async () => { + if (!accountAddress) { + setRewardsError('Please enter an account address'); + return; + } + + try { + setRewardsLoading(true); + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`http://localhost:3003/paymaster/${network}`), + }); + + const rewards = await paymasterClient.getAccountRewards({ + accountAddress, + }); + + console.log('Account rewards:', rewards); + setAccountRewards(rewards); + setRewardsError(null); + } catch (err) { + console.error('Error fetching account rewards:', err); + setRewardsError('Failed to fetch account rewards'); + setAccountRewards(null); + } finally { + setRewardsLoading(false); + } + }; + + const buildTransaction = async () => { + // Check if we're on client-side + if (typeof window === 'undefined') return; + + try { + setBuildingTransaction(true); + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`http://localhost:3003/paymaster/${network}`), + }); + + const transaction = await paymasterClient.buildTypedData({ + userAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", + calls: [ + { + entrypoint: "approve", + contractAddress: '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7', + calldata: ['0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91', '0xf', '0x0'], + }, + ], + }); + + console.log('Built transaction:', transaction); + setTransactionResult(transaction as unknown as BuildTransactionResult); + setTransactionError(null); + } catch (err) { + console.error('Error building transaction:', err); + setTransactionError('Failed to build transaction'); + setTransactionResult(null); + } finally { + setBuildingTransaction(false); + } + }; + + const signTransaction = async () => { + if (!transactionResult) { + setTransactionError('No transaction to sign'); + return; + } + + try { + setSigningTransaction(true); + // const signature = await useSignTypedData(transactionResult.typedData); + const permissions = await walletClient?.getPermissions({}); + console.log('Permissions:', permissions); + const signature = await walletClient?.signTypedData({ + typed_data: transactionResult, + } ); + // const signature = await walletClient?.signTypedData({ + // typed_data: { + // types: transactionResult.types, + // primaryType: transactionResult.primaryType, + // domain: transactionResult.domain, + // message: transactionResult.message + // } + // } ); + console.log('Signed transaction:', signature); + setSignature(signature); + } catch (err) { + console.error('Error signing transaction:', err); + setTransactionError('Failed to sign transaction'); + setTransactionResult(null); + } finally { + setSigningTransaction(false); + } + }; + + const executeTransaction = async () => { + if (!walletClient || !signature || !transactionResult) { + setTransactionError('Missing required data for execution'); + return; + } + + try { + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`http://localhost:3003/paymaster/${network}`), + }); + + const executionResult = await paymasterClient.executeTransaction({ + userAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", + typedData: JSON.stringify(transactionResult), + signature: signature, + + }); + + console.log('Transaction executed:', executionResult); + return executionResult; + } catch (err) { + console.error('Error executing transaction:', err); + setTransactionError('Failed to execute transaction'); + throw err; + } + }; + + return ( +
+

Starknet Paymaster

+ +
+ +
+ + +
+
+ +
+

Paymaster Status

+ + {loading ? ( +
Loading...
+ ) : error ? ( +
{error}
+ ) : ( +
+            {JSON.stringify(status, null, 2)}
+          
+ )} +
+ +
+

Account Rewards

+ +
+ +
+ setAccountAddress(e.target.value)} + className="flex-1 px-4 py-2 border rounded dark:bg-gray-700" + placeholder="Enter account address" + /> + +
+
+ + {rewardsError && ( +
{rewardsError}
+ )} + + {accountRewards && ( +
+            {JSON.stringify(accountRewards, null, 2)}
+          
+ )} +
+ +
+

Build Transaction

+ + + + {transactionError && ( +
{transactionError}
+ )} + + {transactionResult && ( + <> +
+
+              {JSON.stringify(transactionResult, null, 2)}
+            
+ +
+ + {signature && ( +
+              {JSON.stringify(signature, null, 2)}
+            
+ )} + + )} +
+ + {signature && ( + + )} +
+ ); +} diff --git a/app/starkweb-client/src/index.ts b/app/starkweb-client/src/index.ts index 599d2c6..01209e5 100644 --- a/app/starkweb-client/src/index.ts +++ b/app/starkweb-client/src/index.ts @@ -3,12 +3,12 @@ import { mainnet, sepolia } from "starkweb/chains" const paymasterClient = createPaymasterClient({ chain: sepolia, - transport: http('http://localhost:3000/gasless'), + transport: http('http://localhost:3000/gasless/sepolia'), type: 'paymasterClient', }) // get paymaster status const status = await paymasterClient.getPaymasterStatus() -console.log(status) +// console.log(status) // // check account compatibility // const compatibility = await paymasterClient.checkAccountCompatibility({ @@ -18,13 +18,19 @@ console.log(status) // get gas token prices const gasTokenPrices = await paymasterClient.getGasTokenPrices() -console.log(gasTokenPrices) +// console.log(gasTokenPrices) // get account rewards const accountRewards = await paymasterClient.getAccountRewards({ accountAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", }) -console.log(accountRewards) +// console.log(accountRewards) + +// Check if account is compatible with paymaster +const isCompatible = await paymasterClient.checkAccountCompatibility({ + accountAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", +}) +console.log(isCompatible) // build transaction @@ -32,7 +38,7 @@ const transaction = await paymasterClient.buildTypedData({ userAddress: "0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91", calls: [ { - entry_point: "approve", + entrypoint: "approve", contractAddress: '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7', calldata: ['0x005c475b6089156c0CD4Fc9d64De149992431c442AF882d6332e7c736c99DE91', '0xf', '0x0'], }, diff --git a/packages/starkweb/src/actions/paymaster/buildTypedData.ts b/packages/starkweb/src/actions/paymaster/buildTypedData.ts index d9575b4..86a5274 100644 --- a/packages/starkweb/src/actions/paymaster/buildTypedData.ts +++ b/packages/starkweb/src/actions/paymaster/buildTypedData.ts @@ -1,7 +1,8 @@ +import type { Call } from '../../strk-types/lib.js' import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { Chain } from '../../types/chain.js' -import type { Call, TypedData } from '@starknet-io/types-js' +import type { TypedData } from '@starknet-io/types-js' export type BuildTypedDataParameters = { userAddress: string diff --git a/packages/starkweb/src/clients/createPaymasterClient.ts b/packages/starkweb/src/clients/createPaymasterClient.ts index 1c63e28..f40ed5f 100644 --- a/packages/starkweb/src/clients/createPaymasterClient.ts +++ b/packages/starkweb/src/clients/createPaymasterClient.ts @@ -18,9 +18,7 @@ export type PaymasterClientConfig< chain extends Chain, accountOrAddress extends Account | Address | undefined = undefined, rpcSchema extends RpcSchema | undefined = undefined, -> = ClientConfig & { - type: 'paymasterClient' -} +> = ClientConfig export type PaymasterClient< transport extends Transport, @@ -41,8 +39,14 @@ export function createPaymasterClient< ): Prettify< Client, rpcSchema, PaymasterActions> > { - return createClient({ + const { key = 'paymaster', name = 'Paymaster Client' } = parameters + const client = createClient({ ...parameters, + key, + name, type: 'paymasterClient', - }).extend(paymasterActions) + }) + return client.extend(paymasterActions) as Prettify< + Client, rpcSchema, PaymasterActions> + > } \ No newline at end of file diff --git a/packages/starkweb/src/types/snip1193.ts b/packages/starkweb/src/types/snip1193.ts index 702e123..508012b 100644 --- a/packages/starkweb/src/types/snip1193.ts +++ b/packages/starkweb/src/types/snip1193.ts @@ -1,4 +1,4 @@ -import type { Call, PADDED_FELT, PADDED_TXN_HASH, Signature, TypedData } from '@starknet-io/types-js' +import type { PADDED_FELT, PADDED_TXN_HASH, Signature, TypedData } from '@starknet-io/types-js' import type { AddInvokeTransactionParameters, AddInvokeTransactionResult, @@ -55,6 +55,7 @@ import type { TYPED_DATA, } from './components.js' import type { DeploymentData, GaslessCompatibility, GasTokenPrice, GaslessStatus, PaymasterReward, InvokeResponse } from './paymaster.js' +import type { Call } from '../strk-types/lib.js' ////////////////////////////////////////////////// // Provider From ca240798f03b435f539bb007b6b94477317cce67 Mon Sep 17 00:00:00 2001 From: goodness5 Date: Thu, 20 Mar 2025 10:22:10 +0100 Subject: [PATCH 03/10] feat: added react hooks for paymaster use and server side usecases --- .../core/actions/checkAccountCompatibility.ts | 30 ++++++ .../src/core/actions/fetchAccountRewards.ts | 30 ++++++ .../src/core/actions/getPaymasterStatus.ts | 36 +++++++ packages/starkweb/src/core/exports/actions.ts | 13 +++ packages/starkweb/src/exports/chains/utils.ts | 2 + packages/starkweb/src/react/exports/index.ts | 9 ++ .../starkweb/src/react/hooks/usePaymaster.ts | 94 +++++++++++++++++++ 7 files changed, 214 insertions(+) create mode 100644 packages/starkweb/src/core/actions/checkAccountCompatibility.ts create mode 100644 packages/starkweb/src/core/actions/fetchAccountRewards.ts create mode 100644 packages/starkweb/src/core/actions/getPaymasterStatus.ts create mode 100644 packages/starkweb/src/react/hooks/usePaymaster.ts diff --git a/packages/starkweb/src/core/actions/checkAccountCompatibility.ts b/packages/starkweb/src/core/actions/checkAccountCompatibility.ts new file mode 100644 index 0000000..9fea018 --- /dev/null +++ b/packages/starkweb/src/core/actions/checkAccountCompatibility.ts @@ -0,0 +1,30 @@ +import { useState } from 'react'; +import { createPaymasterClient } from '../../exports/starkweb.js'; +import { http } from '../../exports/starkweb.js'; +import { mainnet, sepolia } from '../../exports/chains.js'; +import type { GaslessCompatibility } from '../../types/paymaster.js'; +import type { ADDRESS } from '../../types/components.js'; + +export const checkAccountCompatibility = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS) => { + const [compatibility, setCompatibility] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`http://localhost:3003/paymaster/${network}`), + }); + + setLoading(true); + try { + const compatibilityStatus = await paymasterClient.checkAccountCompatibility({ accountAddress }); + setCompatibility(compatibilityStatus); + } catch (err) { + setError('Failed to check account compatibility'); + } finally { + setLoading(false); + } + + return { compatibility, loading, error }; +}; \ No newline at end of file diff --git a/packages/starkweb/src/core/actions/fetchAccountRewards.ts b/packages/starkweb/src/core/actions/fetchAccountRewards.ts new file mode 100644 index 0000000..809220b --- /dev/null +++ b/packages/starkweb/src/core/actions/fetchAccountRewards.ts @@ -0,0 +1,30 @@ +import { useState } from 'react'; +import { createPaymasterClient } from '../../exports/starkweb.js'; +import { http } from '../../exports/starkweb.js'; +import { mainnet, sepolia } from '../../exports/chains.js'; +import type { PaymasterReward } from '../../types/paymaster.js'; +import type { ADDRESS } from '../../types/components.js'; + +export const fetchAccountRewards = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS) => { + const [rewards, setRewards] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`http://localhost:3003/paymaster/${network}`), + }); + + setLoading(true); + try { + const accountRewards = await paymasterClient.getAccountRewards({ accountAddress }); + setRewards(accountRewards); + } catch (err) { + setError('Failed to fetch account rewards'); + } finally { + setLoading(false); + } + + return { rewards, loading, error }; +}; \ No newline at end of file diff --git a/packages/starkweb/src/core/actions/getPaymasterStatus.ts b/packages/starkweb/src/core/actions/getPaymasterStatus.ts new file mode 100644 index 0000000..5c20684 --- /dev/null +++ b/packages/starkweb/src/core/actions/getPaymasterStatus.ts @@ -0,0 +1,36 @@ +import { useState } from 'react'; +import { createPaymasterClient } from '../../exports/starkweb.js'; +import { http } from '../../exports/starkweb.js'; +import { mainnet, sepolia } from '../../exports/chains.js'; +import type { GaslessStatus } from '../../types/paymaster.js'; + +/** + * Fetches the current status of the Paymaster. + * + * @param {('mainnet' | 'sepolia')} network - The network to connect to, either 'mainnet' or 'sepolia'. + * @param {string} [url='https://avnu.fi'] - Optional URL for the Paymaster service. Defaults to 'https://avnu.fi'. + * @returns {Object} An object containing the status, loading state, and error message. + */ +export const fetchPaymasterStatus = async (network: 'mainnet' | 'sepolia', url: string = 'https://avnu.fi') => { + const [status, setStatus] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(`${url}/paymaster/${network}`), + }); + + setLoading(true); + try { + const paymasterStatus = await paymasterClient.getPaymasterStatus(); + setStatus(paymasterStatus); + } catch (err) { + setError('Failed to fetch paymaster status'); + } finally { + setLoading(false); + } + + return { status, loading, error }; +}; \ No newline at end of file diff --git a/packages/starkweb/src/core/exports/actions.ts b/packages/starkweb/src/core/exports/actions.ts index 5a8f2e9..048e82f 100644 --- a/packages/starkweb/src/core/exports/actions.ts +++ b/packages/starkweb/src/core/exports/actions.ts @@ -411,3 +411,16 @@ export { type VerifyTypedDataParameters, type VerifyTypedDataReturnType, } from "../actions/verifyTypedData.js"; + + +export { + fetchAccountRewards +} from "../actions/fetchAccountRewards.js"; + +export { + checkAccountCompatibility +} from "../actions/checkAccountCompatibility.js"; + +export { + fetchPaymasterStatus, +} from "../actions/getPaymasterStatus.js"; \ No newline at end of file diff --git a/packages/starkweb/src/exports/chains/utils.ts b/packages/starkweb/src/exports/chains/utils.ts index 489cb78..08ac4af 100644 --- a/packages/starkweb/src/exports/chains/utils.ts +++ b/packages/starkweb/src/exports/chains/utils.ts @@ -16,3 +16,5 @@ export { getChainContractAddress, } from '../../utils/chain/getChainContractAddress.js' export { type AddStarknetChainParameters } from '../../utils/chain/addStarknetChainParameters.js' +export { mainnet} from "../../chains/definitions/mainnet.js" +export { sepolia} from "../../chains/definitions/sepolia.js" diff --git a/packages/starkweb/src/react/exports/index.ts b/packages/starkweb/src/react/exports/index.ts index 35b050e..69bb444 100644 --- a/packages/starkweb/src/react/exports/index.ts +++ b/packages/starkweb/src/react/exports/index.ts @@ -373,6 +373,15 @@ export { Hydrate, } from '../hydrate.js' + +//////////////////////////////////////////////////////////////////////////////// +// Paymaster +//////////////////////////////////////////////////////////////////////////////// + +export { + usePaymaster +} from '../hooks/usePaymaster.js' + //////////////////////////////////////////////////////////////////////////////// // @wagmi/core //////////////////////////////////////////////////////////////////////////////// diff --git a/packages/starkweb/src/react/hooks/usePaymaster.ts b/packages/starkweb/src/react/hooks/usePaymaster.ts new file mode 100644 index 0000000..e6ea83d --- /dev/null +++ b/packages/starkweb/src/react/hooks/usePaymaster.ts @@ -0,0 +1,94 @@ +"use client" +import { useState, useEffect } from 'react'; +import {fetchAccountRewards} from '../../core/exports/actions.js' +import { checkAccountCompatibility } from '../../core/exports/actions.js' +import { fetchPaymasterStatus } from '../../core/exports/actions.js' +import type { GaslessStatus, GaslessCompatibility, PaymasterReward } from '../../types/paymaster.js'; +import type { ADDRESS } from '../../types/components.js'; + +export type UsePaymasterProps = { + network: 'mainnet' | 'sepolia'; + accountAddress?: ADDRESS; +}; + +export type UsePaymasterReturn = { + status: GaslessStatus | null; + compatibility: GaslessCompatibility | null; + rewards: PaymasterReward[]; + loading: boolean; + error: string | null; + refetch: { + fetchPaymasterStatus: () => Promise; + checkAccountCompatibility: () => Promise; + fetchAccountRewards: () => Promise; + }; +}; + +/** + * Custom hook to interact with the Paymaster service. + * + * @param {('mainnet' | 'sepolia')} network - The network to connect to, either 'mainnet' or 'sepolia'. + * @param {ADDRESS} [accountAddress] - Optional account address to check compatibility and fetch rewards. + * @returns {Object} An object containing the following properties and methods: + * - `status` {GaslessStatus | null} - The current status of the Paymaster. + * - `compatibility` {GaslessCompatibility | null} - The compatibility status of the provided account address. + * - `rewards` {PaymasterReward[]} - The rewards associated with the provided account address. + * - `loading` {boolean} - Indicates if a request is currently in progress. + * - `error` {string | null} - Error message if any request fails. + * - `refetch` {Object} - An object containing methods to manually refetch data: + * - `fetchPaymasterStatus` {Function} - Method to refetch the Paymaster status. + * - `checkAccountCompatibility` {Function} - Method to refetch the account compatibility status. + * - `fetchAccountRewards` {Function} - Method to refetch the account rewards. + */ +export const usePaymaster = ({ network, accountAddress }: UsePaymasterProps): UsePaymasterReturn => { + const [status, setStatus] = useState(null); + const [compatibility, setCompatibility] = useState(null); + const [rewards, setRewards] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchData = async () => { + const { status, loading: paymasterLoading, error: paymasterError } = await fetchPaymasterStatus(network); + setStatus(status); + setLoading(paymasterLoading); + setError(paymasterError); + + if (accountAddress) { + const { compatibility, loading: compatibilityLoading, error: compatibilityError } = await checkAccountCompatibility(network, accountAddress); + setCompatibility(compatibility); + setLoading(compatibilityLoading); + setError(compatibilityError); + + const { rewards, loading: rewardsLoading, error: rewardsError } = await fetchAccountRewards(network, accountAddress); + setRewards(rewards); + setLoading(rewardsLoading); + setError(rewardsError); + } + }; + + fetchData(); + }, [network, accountAddress]); + + return { + status, + compatibility, + rewards, + loading, + error, + refetch: { + fetchPaymasterStatus: async () => { + const { status } = await fetchPaymasterStatus(network); + return status; + }, + checkAccountCompatibility: async () => { + const { compatibility } = await checkAccountCompatibility(network, accountAddress!); + return compatibility; + }, + fetchAccountRewards: async () => { + const { rewards } = await fetchAccountRewards(network, accountAddress!); + return rewards; + }, + }, + }; +}; \ No newline at end of file From 93943ec82e57f00387551eaf57d457e77d4782c7 Mon Sep 17 00:00:00 2001 From: goodness5 Date: Thu, 20 Mar 2025 10:30:31 +0100 Subject: [PATCH 04/10] feat: added react hooks for paymaster use and server side usecases --- .../src/core/actions/checkAccountCompatibility.ts | 4 ++-- .../starkweb/src/core/actions/fetchAccountRewards.ts | 4 ++-- packages/starkweb/src/core/actions/getPaymasterStatus.ts | 4 ++-- packages/starkweb/src/react/hooks/usePaymaster.ts | 9 +++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/starkweb/src/core/actions/checkAccountCompatibility.ts b/packages/starkweb/src/core/actions/checkAccountCompatibility.ts index 9fea018..e4d81e6 100644 --- a/packages/starkweb/src/core/actions/checkAccountCompatibility.ts +++ b/packages/starkweb/src/core/actions/checkAccountCompatibility.ts @@ -5,7 +5,7 @@ import { mainnet, sepolia } from '../../exports/chains.js'; import type { GaslessCompatibility } from '../../types/paymaster.js'; import type { ADDRESS } from '../../types/components.js'; -export const checkAccountCompatibility = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS) => { +export const checkAccountCompatibility = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS, url: string = `http://localhost:3003/paymaster/${network}`) => { const [compatibility, setCompatibility] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -13,7 +13,7 @@ export const checkAccountCompatibility = async (network: 'mainnet' | 'sepolia', const chain = network === 'mainnet' ? mainnet : sepolia; const paymasterClient = createPaymasterClient({ chain, - transport: http(`http://localhost:3003/paymaster/${network}`), + transport: http(url), }); setLoading(true); diff --git a/packages/starkweb/src/core/actions/fetchAccountRewards.ts b/packages/starkweb/src/core/actions/fetchAccountRewards.ts index 809220b..60ea4ed 100644 --- a/packages/starkweb/src/core/actions/fetchAccountRewards.ts +++ b/packages/starkweb/src/core/actions/fetchAccountRewards.ts @@ -5,7 +5,7 @@ import { mainnet, sepolia } from '../../exports/chains.js'; import type { PaymasterReward } from '../../types/paymaster.js'; import type { ADDRESS } from '../../types/components.js'; -export const fetchAccountRewards = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS) => { +export const fetchAccountRewards = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS, url: string = `http://localhost:3003/paymaster/${network}`) => { const [rewards, setRewards] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -13,7 +13,7 @@ export const fetchAccountRewards = async (network: 'mainnet' | 'sepolia', accoun const chain = network === 'mainnet' ? mainnet : sepolia; const paymasterClient = createPaymasterClient({ chain, - transport: http(`http://localhost:3003/paymaster/${network}`), + transport: http(url), }); setLoading(true); diff --git a/packages/starkweb/src/core/actions/getPaymasterStatus.ts b/packages/starkweb/src/core/actions/getPaymasterStatus.ts index 5c20684..a22dc7d 100644 --- a/packages/starkweb/src/core/actions/getPaymasterStatus.ts +++ b/packages/starkweb/src/core/actions/getPaymasterStatus.ts @@ -8,10 +8,10 @@ import type { GaslessStatus } from '../../types/paymaster.js'; * Fetches the current status of the Paymaster. * * @param {('mainnet' | 'sepolia')} network - The network to connect to, either 'mainnet' or 'sepolia'. - * @param {string} [url='https://avnu.fi'] - Optional URL for the Paymaster service. Defaults to 'https://avnu.fi'. + * @param {string} [url='http://localhost:3003/paymaster'] - Optional URL for the Paymaster service. Defaults to 'http://localhost:3003/paymaster. * @returns {Object} An object containing the status, loading state, and error message. */ -export const fetchPaymasterStatus = async (network: 'mainnet' | 'sepolia', url: string = 'https://avnu.fi') => { +export const fetchPaymasterStatus = async (network: 'mainnet' | 'sepolia', url: string = 'http://localhost:3003/paymaster') => { const [status, setStatus] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); diff --git a/packages/starkweb/src/react/hooks/usePaymaster.ts b/packages/starkweb/src/react/hooks/usePaymaster.ts index e6ea83d..3aceafa 100644 --- a/packages/starkweb/src/react/hooks/usePaymaster.ts +++ b/packages/starkweb/src/react/hooks/usePaymaster.ts @@ -9,6 +9,7 @@ import type { ADDRESS } from '../../types/components.js'; export type UsePaymasterProps = { network: 'mainnet' | 'sepolia'; accountAddress?: ADDRESS; + clientUrl?: string; }; export type UsePaymasterReturn = { @@ -40,7 +41,7 @@ export type UsePaymasterReturn = { * - `checkAccountCompatibility` {Function} - Method to refetch the account compatibility status. * - `fetchAccountRewards` {Function} - Method to refetch the account rewards. */ -export const usePaymaster = ({ network, accountAddress }: UsePaymasterProps): UsePaymasterReturn => { +export const usePaymaster = ({ network, accountAddress, clientUrl }: UsePaymasterProps): UsePaymasterReturn => { const [status, setStatus] = useState(null); const [compatibility, setCompatibility] = useState(null); const [rewards, setRewards] = useState([]); @@ -49,18 +50,18 @@ export const usePaymaster = ({ network, accountAddress }: UsePaymasterProps): Us useEffect(() => { const fetchData = async () => { - const { status, loading: paymasterLoading, error: paymasterError } = await fetchPaymasterStatus(network); + const { status, loading: paymasterLoading, error: paymasterError } = await fetchPaymasterStatus(network, clientUrl); setStatus(status); setLoading(paymasterLoading); setError(paymasterError); if (accountAddress) { - const { compatibility, loading: compatibilityLoading, error: compatibilityError } = await checkAccountCompatibility(network, accountAddress); + const { compatibility, loading: compatibilityLoading, error: compatibilityError } = await checkAccountCompatibility(network, accountAddress, clientUrl); setCompatibility(compatibility); setLoading(compatibilityLoading); setError(compatibilityError); - const { rewards, loading: rewardsLoading, error: rewardsError } = await fetchAccountRewards(network, accountAddress); + const { rewards, loading: rewardsLoading, error: rewardsError } = await fetchAccountRewards(network, accountAddress, clientUrl); setRewards(rewards); setLoading(rewardsLoading); setError(rewardsError); From d9d97a731c39fb1892f5cb3cd8ae5bfea20f9e55 Mon Sep 17 00:00:00 2001 From: goodness5 Date: Thu, 20 Mar 2025 10:35:27 +0100 Subject: [PATCH 05/10] feat: added react hooks for paymaster use and server side usecases --- packages/starkweb/src/react/exports/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/starkweb/src/react/exports/index.ts b/packages/starkweb/src/react/exports/index.ts index 69bb444..30cff1c 100644 --- a/packages/starkweb/src/react/exports/index.ts +++ b/packages/starkweb/src/react/exports/index.ts @@ -379,6 +379,8 @@ export { //////////////////////////////////////////////////////////////////////////////// export { + type UsePaymasterProps, + type UsePaymasterReturn, usePaymaster } from '../hooks/usePaymaster.js' From b26cc0292a0adffba53a32b64557c324867600e0 Mon Sep 17 00:00:00 2001 From: goodness5 Date: Thu, 20 Mar 2025 11:31:32 +0100 Subject: [PATCH 06/10] feat: added react hooks for paymaster use and server side usecases --- packages/starkweb/src/core/exports/actions.ts | 12 +- packages/starkweb/src/react/exports/index.ts | 8 ++ .../src/react/hooks/useExecuteTransaction.ts | 58 ++++++++ .../starkweb/src/react/hooks/usePaymaster.ts | 124 +++++++++++------- 4 files changed, 150 insertions(+), 52 deletions(-) create mode 100644 packages/starkweb/src/react/hooks/useExecuteTransaction.ts diff --git a/packages/starkweb/src/core/exports/actions.ts b/packages/starkweb/src/core/exports/actions.ts index 048e82f..e672670 100644 --- a/packages/starkweb/src/core/exports/actions.ts +++ b/packages/starkweb/src/core/exports/actions.ts @@ -423,4 +423,14 @@ export { export { fetchPaymasterStatus, -} from "../actions/getPaymasterStatus.js"; \ No newline at end of file +} from "../actions/getPaymasterStatus.js"; + +export { + buildTypedData, +} from '../../actions/paymaster/buildTypedData.js'; +export { + getGasTokenPrices, +} from '../../actions/paymaster/getGasTokenPrices.js'; +export { + executeTransaction, +} from '../../actions/paymaster/executeTransaction.js'; \ No newline at end of file diff --git a/packages/starkweb/src/react/exports/index.ts b/packages/starkweb/src/react/exports/index.ts index 30cff1c..dee5b9b 100644 --- a/packages/starkweb/src/react/exports/index.ts +++ b/packages/starkweb/src/react/exports/index.ts @@ -364,6 +364,8 @@ export { useWriteContract, } from '../hooks/useWriteContract.js' + + //////////////////////////////////////////////////////////////////////////////// // Hydrate //////////////////////////////////////////////////////////////////////////////// @@ -384,6 +386,12 @@ export { usePaymaster } from '../hooks/usePaymaster.js' +export { + type UseExecuteTransactionProps, + type UseExecuteTransactionReturn, + useExecuteTransaction, +} from '../hooks/useExecuteTransaction.js' + //////////////////////////////////////////////////////////////////////////////// // @wagmi/core //////////////////////////////////////////////////////////////////////////////// diff --git a/packages/starkweb/src/react/hooks/useExecuteTransaction.ts b/packages/starkweb/src/react/hooks/useExecuteTransaction.ts new file mode 100644 index 0000000..e505c29 --- /dev/null +++ b/packages/starkweb/src/react/hooks/useExecuteTransaction.ts @@ -0,0 +1,58 @@ +import { useState } from 'react'; +import { createPaymasterClient } from '../../exports/starkweb.js'; +import { http } from '../../exports/starkweb.js'; +import { mainnet, sepolia } from '../../exports/chains.js'; +import type { InvokeResponse } from '../../types/paymaster.js'; +import type { ADDRESS, SIGNATURE } from '../../types/components.js'; + +export type UseExecuteTransactionProps = { + network: 'mainnet' | 'sepolia'; + userAddress: ADDRESS; + typedData: string; + signature: SIGNATURE; + clientUrl?: string; +}; + +export type UseExecuteTransactionReturn = { + loading: boolean; + error: string | null; + executeTransaction: () => Promise; +}; + +export const useExecuteTransaction = ({ + network, + userAddress, + typedData, + signature, + clientUrl, +}: UseExecuteTransactionProps): UseExecuteTransactionReturn => { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const executeTransaction = async (): Promise => { + setLoading(true); + setError(null); + const chain = network === 'mainnet' ? mainnet : sepolia; + const paymasterClient = createPaymasterClient({ + chain, + transport: http(clientUrl || `http://localhost:3003/paymaster/${network}`), + }); + + try { + const response = await paymasterClient.executeTransaction({ + userAddress, + typedData, + signature, + }); + return response; + } catch (err) { + setError('Failed to execute transaction'); + console.error(err); + return null; + } finally { + setLoading(false); + } + }; + + return { loading, error, executeTransaction }; +}; \ No newline at end of file diff --git a/packages/starkweb/src/react/hooks/usePaymaster.ts b/packages/starkweb/src/react/hooks/usePaymaster.ts index 3aceafa..f4ac4ac 100644 --- a/packages/starkweb/src/react/hooks/usePaymaster.ts +++ b/packages/starkweb/src/react/hooks/usePaymaster.ts @@ -1,17 +1,33 @@ -"use client" -import { useState, useEffect } from 'react'; -import {fetchAccountRewards} from '../../core/exports/actions.js' -import { checkAccountCompatibility } from '../../core/exports/actions.js' -import { fetchPaymasterStatus } from '../../core/exports/actions.js' -import type { GaslessStatus, GaslessCompatibility, PaymasterReward } from '../../types/paymaster.js'; -import type { ADDRESS } from '../../types/components.js'; +"use client"; +import { useState, useEffect, useCallback } from "react"; +import { + fetchAccountRewards, + checkAccountCompatibility, + fetchPaymasterStatus, + getGasTokenPrices, + buildTypedData, + executeTransaction, +} from "../../core/exports/actions.js"; +import type { + GaslessStatus, + GaslessCompatibility, + PaymasterReward, +} from "../../types/paymaster.js"; +import type { ADDRESS } from "../../types/components.js"; +import { createPaymasterClient } from "../../clients/createPaymasterClient.js"; +import { http } from "../../clients/transports/http.js"; +import { mainnet } from "../../chains/definitions/mainnet.js"; +import { sepolia } from "../../chains/definitions/sepolia.js"; +import { type BuildTypedDataParameters } from "../../actions/paymaster/buildTypedData.js"; +import { type ExecuteTransactionParameters } from "../../actions/paymaster/executeTransaction.js"; export type UsePaymasterProps = { - network: 'mainnet' | 'sepolia'; + network: "mainnet" | "sepolia"; accountAddress?: ADDRESS; - clientUrl?: string; + clientUrl?: string; }; + export type UsePaymasterReturn = { status: GaslessStatus | null; compatibility: GaslessCompatibility | null; @@ -19,28 +35,15 @@ export type UsePaymasterReturn = { loading: boolean; error: string | null; refetch: { - fetchPaymasterStatus: () => Promise; - checkAccountCompatibility: () => Promise; - fetchAccountRewards: () => Promise; + fetchPaymasterStatus: () => Promise; + checkAccountCompatibility: () => Promise; + fetchAccountRewards: () => Promise; + getGasTokenPrices: () => Promise; + buildTypedData: (params: BuildTypedDataParameters) => Promise; + executeTransaction: (params: ExecuteTransactionParameters) => Promise; }; }; -/** - * Custom hook to interact with the Paymaster service. - * - * @param {('mainnet' | 'sepolia')} network - The network to connect to, either 'mainnet' or 'sepolia'. - * @param {ADDRESS} [accountAddress] - Optional account address to check compatibility and fetch rewards. - * @returns {Object} An object containing the following properties and methods: - * - `status` {GaslessStatus | null} - The current status of the Paymaster. - * - `compatibility` {GaslessCompatibility | null} - The compatibility status of the provided account address. - * - `rewards` {PaymasterReward[]} - The rewards associated with the provided account address. - * - `loading` {boolean} - Indicates if a request is currently in progress. - * - `error` {string | null} - Error message if any request fails. - * - `refetch` {Object} - An object containing methods to manually refetch data: - * - `fetchPaymasterStatus` {Function} - Method to refetch the Paymaster status. - * - `checkAccountCompatibility` {Function} - Method to refetch the account compatibility status. - * - `fetchAccountRewards` {Function} - Method to refetch the account rewards. - */ export const usePaymaster = ({ network, accountAddress, clientUrl }: UsePaymasterProps): UsePaymasterReturn => { const [status, setStatus] = useState(null); const [compatibility, setCompatibility] = useState(null); @@ -48,28 +51,53 @@ export const usePaymaster = ({ network, accountAddress, clientUrl }: UsePaymaste const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - useEffect(() => { - const fetchData = async () => { - const { status, loading: paymasterLoading, error: paymasterError } = await fetchPaymasterStatus(network, clientUrl); + const paymasterClient = createPaymasterClient({ + chain: network === "mainnet" ? mainnet : sepolia, + transport: http(clientUrl || `http://localhost:3003/paymaster/${network}`), + }); + + const fetchData = useCallback(async () => { + setLoading(true); + setError(null); + try { + const { status, error: statusError } = await fetchPaymasterStatus(network); setStatus(status); - setLoading(paymasterLoading); - setError(paymasterError); + setError(statusError); + if (accountAddress) { - const { compatibility, loading: compatibilityLoading, error: compatibilityError } = await checkAccountCompatibility(network, accountAddress, clientUrl); + const { compatibility, error: compatibilityError } = await checkAccountCompatibility(network, accountAddress); setCompatibility(compatibility); - setLoading(compatibilityLoading); setError(compatibilityError); - const { rewards, loading: rewardsLoading, error: rewardsError } = await fetchAccountRewards(network, accountAddress, clientUrl); + const { rewards, error: rewardsError } = await fetchAccountRewards(network, accountAddress); setRewards(rewards); - setLoading(rewardsLoading); setError(rewardsError); + } - }; + } catch (err: any) { + setError(err.message || "An error occurred while fetching data"); + } finally { + setLoading(false); + } + }, [network, accountAddress]); + useEffect(() => { fetchData(); - }, [network, accountAddress]); + }, [fetchData]); + + const handleAsyncRequest = async (asyncFunc: () => Promise, setState: (data: any) => void) => { + setLoading(true); + setError(null); + try { + const response = await asyncFunc(); + setState(response); + } catch (err: any) { + setError(err.message || "An error occurred during request"); + } finally { + setLoading(false); + } + }; return { status, @@ -78,18 +106,12 @@ export const usePaymaster = ({ network, accountAddress, clientUrl }: UsePaymaste loading, error, refetch: { - fetchPaymasterStatus: async () => { - const { status } = await fetchPaymasterStatus(network); - return status; - }, - checkAccountCompatibility: async () => { - const { compatibility } = await checkAccountCompatibility(network, accountAddress!); - return compatibility; - }, - fetchAccountRewards: async () => { - const { rewards } = await fetchAccountRewards(network, accountAddress!); - return rewards; - }, + fetchPaymasterStatus: () => handleAsyncRequest(() => fetchPaymasterStatus(network), setStatus), + checkAccountCompatibility: () => handleAsyncRequest(() => checkAccountCompatibility(network, accountAddress!), setCompatibility), + fetchAccountRewards: () => handleAsyncRequest(() => fetchAccountRewards(network, accountAddress!), setRewards), + getGasTokenPrices: () => handleAsyncRequest(() => getGasTokenPrices(paymasterClient, undefined), () => {}), + buildTypedData: (params) => handleAsyncRequest(() => buildTypedData(paymasterClient, params), () => {}), + executeTransaction: (params) => handleAsyncRequest(() => executeTransaction(paymasterClient, params), () => {}), }, }; }; \ No newline at end of file From a68d57ce94857d782b59ac13b0cbf84e818c0687 Mon Sep 17 00:00:00 2001 From: Musa AbdulKareem Date: Mon, 24 Mar 2025 17:15:31 +0100 Subject: [PATCH 07/10] feat: added buildTypedData --- .../src/core/actions/buildTypedData.ts | 38 +++++++++++++++++++ packages/starkweb/src/core/utils/getAction.ts | 3 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 packages/starkweb/src/core/actions/buildTypedData.ts diff --git a/packages/starkweb/src/core/actions/buildTypedData.ts b/packages/starkweb/src/core/actions/buildTypedData.ts new file mode 100644 index 0000000..afebb0c --- /dev/null +++ b/packages/starkweb/src/core/actions/buildTypedData.ts @@ -0,0 +1,38 @@ +import { + type BuildTypedDataErrorType as strkjs_BuildTypedDataErrorType, + type BuildTypedDataParameters as strkjs_BuildTypedDataParameters, + type BuildTypedDataReturnType as strkjs_BuildTypedDataReturnType, + buildTypedData as strkjs_buildTypedData, +} from "../../actions/paymaster/buildTypedData.js"; +import type { Hex } from "../../types/misc.js"; + +import type { Config } from "../createConfig.js"; +import type { ChainIdParameter } from "../types/properties.js"; +import type { Evaluate } from "../types/utils.js"; +import { getAction } from "../utils/getAction.js"; + +export type BuildTypedDataParameters = Evaluate< + strkjs_BuildTypedDataParameters & ChainIdParameter +>; + +export type BuildTypedDataReturnType = Evaluate< + strkjs_BuildTypedDataReturnType & { + chainId: Hex; + } +>; + +export type BuildTypedDataErrorType = strkjs_BuildTypedDataErrorType; + +export async function buildTypedData( + config: Config, + parameters: BuildTypedDataParameters +): Promise { + const { chainId, ...rest } = parameters; + const client = config.getClient({ chainId }); + const action = getAction( + client, + strkjs_buildTypedData, + "buildTypedData" + ); + return action(rest) as Promise; +} diff --git a/packages/starkweb/src/core/utils/getAction.ts b/packages/starkweb/src/core/utils/getAction.ts index b8974c6..cb0960a 100644 --- a/packages/starkweb/src/core/utils/getAction.ts +++ b/packages/starkweb/src/core/utils/getAction.ts @@ -15,6 +15,7 @@ import type { RpcSchema } from '../../types/snip1193.js' import type { Client } from '../../clients/createClient.js' import type { WalletActions } from '../../clients/decorators/wallet.js' import type { PublicActions } from '../../clients/decorators/public.js' +import type { PaymasterActions } from 'src/exports/starkweb.js' /** * Retrieves and returns an action from the client (if exists), and falls @@ -38,7 +39,7 @@ export function getAction< // Some minifiers drop `Function.prototype.name`, or replace it with short letters, // meaning that `actionFn.name` will not always work. For that case, the consumer // needs to pass the name explicitly. - name: keyof PublicActions | keyof WalletActions, + name: keyof PublicActions | keyof WalletActions | keyof PaymasterActions, ): (parameters: parameters) => returnType { const action_implicit = client[actionFn.name] if (typeof action_implicit === 'function') From 4ca387418eb40c9aae6c01dbea5b83489fec84b9 Mon Sep 17 00:00:00 2001 From: Musa AbdulKareem Date: Mon, 24 Mar 2025 17:39:56 +0100 Subject: [PATCH 08/10] feat: added checkAccountCompatibility --- .../core/actions/checkAccountCompatibility.ts | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/starkweb/src/core/actions/checkAccountCompatibility.ts b/packages/starkweb/src/core/actions/checkAccountCompatibility.ts index e4d81e6..57ae66e 100644 --- a/packages/starkweb/src/core/actions/checkAccountCompatibility.ts +++ b/packages/starkweb/src/core/actions/checkAccountCompatibility.ts @@ -1,30 +1,39 @@ -import { useState } from 'react'; -import { createPaymasterClient } from '../../exports/starkweb.js'; -import { http } from '../../exports/starkweb.js'; -import { mainnet, sepolia } from '../../exports/chains.js'; -import type { GaslessCompatibility } from '../../types/paymaster.js'; -import type { ADDRESS } from '../../types/components.js'; +import { + type CheckAccountCompatibilityErrorType as strkjs_CheckAccountCompatibilityErrorType, + type CheckAccountCompatibilityParameters as strkjs_CheckAccountCompatibilityParameters, + type CheckAccountCompatibilityReturnType as strkjs_CheckAccountCompatibilityReturnType, + checkAccountCompatibility as strkjs_checkAccountCompatibility, +} from "../../actions/paymaster/checkAccountCompatibility.js"; +import type { Hex } from "../../types/misc.js"; -export const checkAccountCompatibility = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS, url: string = `http://localhost:3003/paymaster/${network}`) => { - const [compatibility, setCompatibility] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); +import type { Config } from "../createConfig.js"; +import type { ChainIdParameter } from "../types/properties.js"; +import type { Evaluate } from "../types/utils.js"; +import { getAction } from "../utils/getAction.js"; - const chain = network === 'mainnet' ? mainnet : sepolia; - const paymasterClient = createPaymasterClient({ - chain, - transport: http(url), - }); +export type CheckAccountCompatibilityParameters = Evaluate< + strkjs_CheckAccountCompatibilityParameters & ChainIdParameter +>; - setLoading(true); - try { - const compatibilityStatus = await paymasterClient.checkAccountCompatibility({ accountAddress }); - setCompatibility(compatibilityStatus); - } catch (err) { - setError('Failed to check account compatibility'); - } finally { - setLoading(false); +export type CheckAccountCompatibilityReturnType = Evaluate< + strkjs_CheckAccountCompatibilityReturnType & { + chainId: Hex; } +>; - return { compatibility, loading, error }; -}; \ No newline at end of file +export type CheckAccountCompatibilityErrorType = + strkjs_CheckAccountCompatibilityErrorType; + +export async function checkAccountCompatibility( + config: Config, + parameters: CheckAccountCompatibilityParameters +): Promise { + const { chainId, ...rest } = parameters; + const client = config.getClient({ chainId }); + const action = getAction( + client, + strkjs_checkAccountCompatibility, + "checkAccountCompatibility" + ); + return action(rest) as Promise; +} From a1edbba242665fcea9f406a30752139d7e90b10a Mon Sep 17 00:00:00 2001 From: Musa AbdulKareem Date: Mon, 24 Mar 2025 17:42:49 +0100 Subject: [PATCH 09/10] feat: added getAccountRewards --- .../src/core/actions/fetchAccountRewards.ts | 30 -------------- .../src/core/actions/getAccountRewards.ts | 39 +++++++++++++++++++ packages/starkweb/src/core/exports/actions.ts | 2 +- 3 files changed, 40 insertions(+), 31 deletions(-) delete mode 100644 packages/starkweb/src/core/actions/fetchAccountRewards.ts create mode 100644 packages/starkweb/src/core/actions/getAccountRewards.ts diff --git a/packages/starkweb/src/core/actions/fetchAccountRewards.ts b/packages/starkweb/src/core/actions/fetchAccountRewards.ts deleted file mode 100644 index 60ea4ed..0000000 --- a/packages/starkweb/src/core/actions/fetchAccountRewards.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useState } from 'react'; -import { createPaymasterClient } from '../../exports/starkweb.js'; -import { http } from '../../exports/starkweb.js'; -import { mainnet, sepolia } from '../../exports/chains.js'; -import type { PaymasterReward } from '../../types/paymaster.js'; -import type { ADDRESS } from '../../types/components.js'; - -export const fetchAccountRewards = async (network: 'mainnet' | 'sepolia', accountAddress: ADDRESS, url: string = `http://localhost:3003/paymaster/${network}`) => { - const [rewards, setRewards] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const chain = network === 'mainnet' ? mainnet : sepolia; - const paymasterClient = createPaymasterClient({ - chain, - transport: http(url), - }); - - setLoading(true); - try { - const accountRewards = await paymasterClient.getAccountRewards({ accountAddress }); - setRewards(accountRewards); - } catch (err) { - setError('Failed to fetch account rewards'); - } finally { - setLoading(false); - } - - return { rewards, loading, error }; -}; \ No newline at end of file diff --git a/packages/starkweb/src/core/actions/getAccountRewards.ts b/packages/starkweb/src/core/actions/getAccountRewards.ts new file mode 100644 index 0000000..0134f2d --- /dev/null +++ b/packages/starkweb/src/core/actions/getAccountRewards.ts @@ -0,0 +1,39 @@ +import { + type GetAccountRewardsErrorType as strkjs_GetAccountRewardsErrorType, + type GetAccountRewardsParameters as strkjs_GetAccountRewardsParameters, + type GetAccountRewardsReturnType as strkjs_GetAccountRewardsReturnType, + getAccountRewards as strkjs_getAccountRewards, +} from "../../actions/paymaster/getAccountRewards.js"; +import type { Hex } from "../../types/misc.js"; + +import type { Config } from "../createConfig.js"; +import type { ChainIdParameter } from "../types/properties.js"; +import type { Evaluate } from "../types/utils.js"; +import { getAction } from "../utils/getAction.js"; + +export type GetAccountRewardsParameters = Evaluate< + strkjs_GetAccountRewardsParameters & ChainIdParameter +>; + +export type GetAccountRewardsReturnType = Evaluate< + strkjs_GetAccountRewardsReturnType & { + chainId: Hex; + } +>; + +export type GetAccountRewardsErrorType = + strkjs_GetAccountRewardsErrorType; + +export async function getAccountRewards( + config: Config, + parameters: GetAccountRewardsParameters +): Promise { + const { chainId, ...rest } = parameters; + const client = config.getClient({ chainId }); + const action = getAction( + client, + strkjs_getAccountRewards, + "getAccountRewards" + ); + return action(rest) as Promise; +} diff --git a/packages/starkweb/src/core/exports/actions.ts b/packages/starkweb/src/core/exports/actions.ts index e672670..03eb395 100644 --- a/packages/starkweb/src/core/exports/actions.ts +++ b/packages/starkweb/src/core/exports/actions.ts @@ -415,7 +415,7 @@ export { export { fetchAccountRewards -} from "../actions/fetchAccountRewards.js"; +} from "../actions/getAccountRewards.js"; export { checkAccountCompatibility From 9557ca8d0adb9dcbe6d6d26af1cd4b728f446a13 Mon Sep 17 00:00:00 2001 From: Musa AbdulKareem Date: Mon, 24 Mar 2025 17:49:00 +0100 Subject: [PATCH 10/10] feat(query): added buildTypedData query --- .../starkweb/src/core/query/buildTypedData.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/starkweb/src/core/query/buildTypedData.ts diff --git a/packages/starkweb/src/core/query/buildTypedData.ts b/packages/starkweb/src/core/query/buildTypedData.ts new file mode 100644 index 0000000..27a6698 --- /dev/null +++ b/packages/starkweb/src/core/query/buildTypedData.ts @@ -0,0 +1,27 @@ + +import type { QueryOptions } from '@tanstack/query-core' + +import { + type BuildTypedDataErrorType, + type BuildTypedDataParameters, + type BuildTypedDataReturnType, + buildTypedData, +} from "../actions/buildTypedData.js"; +import type { Config } from "../createConfig.js"; +import { filterQueryOptions } from "./utils.js"; + +export function buildTypedDataQueryOptions( + config: Config, + parameters: BuildTypedDataParameters +): QueryOptions { + return { + queryKey: buildTypedDataQueryKey(parameters), + queryFn: () => buildTypedData(config, parameters), + } +} + +export function buildTypedDataQueryKey(parameters: BuildTypedDataParameters) { + return ['buildTypedData', filterQueryOptions(parameters)] as const +} + +export type BuildTypedDataQueryKey = ReturnType