Skip to content
14 changes: 10 additions & 4 deletions demo/vue-app-new/src/components/X402Tester.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { useSwitchChain } from "@wagmi/vue";
import { useX402Fetch } from "@web3auth/modal/x402/vue";
import { useChain, useWeb3Auth } from "@web3auth/modal/vue";
import { CHAIN_NAMESPACES } from "@web3auth/no-modal";
import { ref, watch } from "vue";
import { computed, ref, watch } from "vue";

const BASE_SEPOLIA_CHAIN_ID = "0x14a34"; // 84532
const SOLANA_DEVNET_CHAIN_ID = "0x67"; // 103
const SOLANA_DEVNET_CAIP_CHAIN_ID = `solana:${Number(SOLANA_DEVNET_CHAIN_ID)}`;
const DEFAULT_X402_URL = import.meta.env.VITE_APP_X402_TEST_CONTENT_URL || "https://x402.org/protected";
const DEFAULT_X402_URL = "https://web3auth-dev-demo-x420.sapphire-dev-2-1.authnetwork.dev";
Comment thread
lwin-kyaw marked this conversation as resolved.

const { isConnected, connection, web3Auth } = useWeb3Auth();
const { chainId, chainNamespace } = useChain();
Expand All @@ -19,6 +19,8 @@ const emit = defineEmits<{
(e: "print-to-console", title: string, payload?: unknown): void;
}>();

const eip155Chains = computed(() => web3Auth.value?.coreOptions.chains?.filter((c) => c.chainNamespace === CHAIN_NAMESPACES.EIP155) || []);

const url = ref(DEFAULT_X402_URL);
const fetchLoading = ref(false);

Expand All @@ -45,9 +47,13 @@ watch(
const onSwitchToBaseSepolia = async () => {
fetchLoading.value = true;
try {
await switchChainAsync({ chainId: parseInt(BASE_SEPOLIA_CHAIN_ID, 16) });
const newChain = eip155Chains.value.find((c) => c.chainId === BASE_SEPOLIA_CHAIN_ID);
if (!newChain) throw new Error(`Unsupported chainId: ${BASE_SEPOLIA_CHAIN_ID}`);
const data = await switchChainAsync({ chainId: Number(newChain.chainId) });
emit("print-to-console", "switchedChain", { chainId: data.id });
} catch (err) {
emit("print-to-console", "x402 network error", err instanceof Error ? err.message : String(err));
console.error("Error", err);
emit("print-to-console", "switchedChain error", err instanceof Error ? err.message : String(err));
} finally {
fetchLoading.value = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ class MetaMaskConnector extends BaseConnector<void> {
},
});

this.evmProvider = this.evmClient.getProvider() as unknown as IProvider;
this.evmProvider = this.createEvmProviderBridge(this.evmClient.getProvider() as unknown as IProvider);
Comment thread
chaitanyapotti marked this conversation as resolved.
Outdated
}

// Create the Solana client only when Solana chains are configured
Expand Down Expand Up @@ -457,24 +457,7 @@ class MetaMaskConnector extends BaseConnector<void> {
throw WalletLoginError.unsupportedOperation("switchChain requires an EVM client, but no EVM chains are configured.");
}

const chainConfig = this.coreOptions.chains.find(
(x) => x.chainId === params.chainId && ([CHAIN_NAMESPACES.EIP155] as ChainNamespaceType[]).includes(x.chainNamespace)
);

const chainConfiguration = chainConfig
? {
chainId: params.chainId,
chainName: chainConfig.displayName,
rpcUrls: [chainConfig.rpcTarget],
blockExplorerUrls: chainConfig.blockExplorerUrl ? [chainConfig.blockExplorerUrl] : undefined,
nativeCurrency: {
name: chainConfig.tickerName,
symbol: chainConfig.ticker,
decimals: chainConfig.decimals || 18,
},
iconUrls: chainConfig.logo ? [chainConfig.logo] : undefined,
}
: undefined;
const chainConfiguration = this.getEvmChainConfiguration(params.chainId);

await this.evmClient.switchChain({ chainId: params.chainId as Hex, chainConfiguration });
}
Expand Down Expand Up @@ -550,6 +533,58 @@ class MetaMaskConnector extends BaseConnector<void> {
}
await this.initializationPromise;
}

private getEvmChainConfiguration(chainId: string) {
const chainConfig = this.coreOptions.chains.find(
(x) => x.chainId === chainId && ([CHAIN_NAMESPACES.EIP155] as ChainNamespaceType[]).includes(x.chainNamespace)
);

return chainConfig
? {
chainId,
chainName: chainConfig.displayName,
rpcUrls: [chainConfig.rpcTarget],
blockExplorerUrls: chainConfig.blockExplorerUrl ? [chainConfig.blockExplorerUrl] : undefined,
nativeCurrency: {
name: chainConfig.tickerName,
symbol: chainConfig.ticker,
decimals: chainConfig.decimals || 18,
},
iconUrls: chainConfig.logo ? [chainConfig.logo] : undefined,
}
: undefined;
}

private createEvmProviderBridge(provider: IProvider): IProvider {
return new Proxy(provider, {
get: (target, prop) => {
if (prop === "request") {
Comment thread
lwin-kyaw marked this conversation as resolved.
Outdated
return async <S, R>(args: { method: string; params?: S }) => {
// handle `wallet_switchEthereumChain` request from the clients which uses the EVM provider directly (e.g. wagmi actions)
// we already handle this method, `wallet_switchEthereumChain` in other connectors, but metamask lacks of this
if (args?.method === "wallet_switchEthereumChain") {
const chainParams = Array.isArray(args.params) ? args.params[0] : undefined;
const chainId =
chainParams && typeof chainParams === "object" && "chainId" in chainParams ? (chainParams.chainId as string | undefined) : undefined;

if (chainId && this.evmClient) {
const chainConfiguration = this.getEvmChainConfiguration(chainId);
await this.evmClient.switchChain({ chainId: chainId as Hex, chainConfiguration });
return null as R;
}
}

return target.request(args);
};
}

// Use the original provider as the receiver so SDK getters that rely on
// private fields (`#field`) keep the correct brand check context.
const value = Reflect.get(target, prop, target);
return typeof value === "function" ? value.bind(target) : value;
},
}) as IProvider;
}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { type BundlerClient, createBundlerClient, createPaymasterClient, type Pa

import { CHAIN_NAMESPACES, type CustomChainConfig, type IProvider, WalletInitializationError } from "../../../base";
import { BaseProvider, type BaseProviderConfig, type BaseProviderState } from "../../../providers/base-provider";
import { createAaMiddleware, createEoaMiddleware, providerAsMiddleware } from "../rpc/ethRpcMiddlewares";
import { createAaMiddleware, createEip7702And5792MiddlewareForAaProvider, createEoaMiddleware, providerAsMiddleware } from "../rpc/ethRpcMiddlewares";
import { getProviderHandlers } from "./utils";

interface AccountAbstractionProviderConfig extends BaseProviderConfig {
Expand Down Expand Up @@ -154,8 +154,12 @@ class AccountAbstractionProvider extends BaseProvider<AccountAbstractionProvider
eoaProvider,
handlers: providerHandlers,
});

// middleware to handle EIP-7702 and EIP-5792 methods,
// currently, we do not support EIP-7702 and EIP-5792 methods for account abstraction provider
const eip7702And5792Middleware = await createEip7702And5792MiddlewareForAaProvider();
const eoaMiddleware = providerAsMiddleware(eoaProvider);
const engine = JRPCEngineV2.create({ middleware: [aaMiddleware, eoaMiddleware] });
const engine = JRPCEngineV2.create({ middleware: [aaMiddleware, eip7702And5792Middleware, eoaMiddleware] });
const provider = providerFromEngineV2(engine);
this.updateProviderEngineProxy(provider);
eoaProvider.once("chainChanged", (chainId) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { METHOD_TYPES } from "@toruslabs/ethereum-controllers";
import { createScaffoldMiddlewareV2, type JRPCRequest, type MiddlewareConstraint, type MiddlewareParams, rpcErrors } from "@web3auth/auth";
import { EIP_5792_METHODS, EIP_7702_METHODS, METHOD_TYPES } from "@toruslabs/ethereum-controllers";
import {
createScaffoldMiddlewareV2,
type JRPCRequest,
type MiddlewareConstraint,
type MiddlewareParams,
providerErrors,
rpcErrors,
} from "@web3auth/auth";

import { IProvider } from "../../../base";
import { IEthProviderHandlers, MessageParams, TransactionParams, TypedMessageParams } from "../../ethereum-provider";
Expand Down Expand Up @@ -210,6 +217,18 @@ export async function createEoaMiddleware({ aaProvider }: { aaProvider: IProvide
});
}

export async function createEip7702And5792MiddlewareForAaProvider(): Promise<MiddlewareConstraint> {
const eip5792Methods = Object.values(EIP_5792_METHODS);
const eip7702Methods = Object.values(EIP_7702_METHODS);
const eip7702And5792Methods: string[] = [...eip5792Methods, ...eip7702Methods];
return async ({ request, next }) => {
if (eip7702And5792Methods.includes(request.method as string)) {
throw providerErrors.unsupportedMethod(`${request.method} is not supported for account abstraction provider`);
}
return next(request);
};
}

export function providerAsMiddleware(provider: IProvider): MiddlewareConstraint {
return async ({ request }) => {
return provider.request({ method: request.method, params: request.params });
Expand Down
Loading