@web3auth/no-modal
Version:
Multi chain wallet aggregator for web3Auth
171 lines (163 loc) • 6.76 kB
JavaScript
import { getBase58Encoder, address } from '@solana/kit';
import { encodeBase64Url } from '@toruslabs/metadata-helpers';
import { EVM_METHOD_TYPES } from '@web3auth/ws-embed';
import { toClientEvmSigner, ExactEvmScheme } from '@x402/evm';
import { x402Client, wrapFetchWithPayment } from '@x402/fetch';
import { ExactSvmScheme, toClientSvmSigner } from '@x402/svm';
import { createWalletClient, custom } from 'viem';
import { PAYMENT_REQUIRED_HEADER } from './interfaces.js';
import { walletSignTransaction } from '../base/wallet/solana.js';
const EVM_CAIP2_WILDCARD = "eip155:*";
const SOLANA_CAIP2_WILDCARD = "solana:*";
/**
* For 402 responses that carry v2 payment requirements in the JSON body instead
* of the PAYMENT-REQUIRED header, reads the body, promotes the data into the
* header, and reconstructs the response so downstream code can consume it normally.
*
* Returns the original response unchanged when the body is not JSON, is malformed,
* or does not carry an x402Version \>= 2 payload.
*/
async function normalizePaymentRequiredResponse(response) {
var _response$headers$get;
const contentType = (_response$headers$get = response.headers.get("Content-Type")) !== null && _response$headers$get !== void 0 ? _response$headers$get : "";
let body;
if (contentType.includes("application/json")) {
body = await response.json();
} else {
const data = await response.text();
body = JSON.parse(data);
}
try {
if (body.x402Version && body.x402Version >= 2) {
const newHeaders = new Headers(response.headers);
const jsonData = JSON.stringify(body);
newHeaders.set(PAYMENT_REQUIRED_HEADER, encodeBase64Url(jsonData));
return new Response(jsonData, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
});
}
} catch {
// Malformed JSON - fall through and return response with the already-consumed body
}
return new Response(JSON.stringify(body), {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
}
async function getEvmAddress(provider) {
var _ref;
const accounts = await provider.request({
method: EVM_METHOD_TYPES.GET_ACCOUNTS
});
return (_ref = accounts === null || accounts === void 0 ? void 0 : accounts[0]) !== null && _ref !== void 0 ? _ref : null;
}
function createProviderBackedEvmSigner(provider, address) {
const walletClient = createWalletClient({
account: address,
transport: custom(provider)
});
return walletClient;
}
/**
* Wraps a fetch function so that 402 responses whose v2 payment requirements
* arrive in the response body (instead of the PAYMENT-REQUIRED header) are
* normalised: the body JSON is base64-encoded and promoted into the
* PAYMENT-REQUIRED header so that the \@x402/fetch client library can process it.
*
* The \@x402/fetch library's body fallback only accepts x402Version === 1;
* this shim bridges the gap for servers that send v2 data in the body.
*
* @param baseFetch - The underlying fetch implementation to wrap
* @returns A fetch-compatible function with the shim applied
*/
function withX402BodyShim(baseFetch) {
return async (input, init) => {
const response = await baseFetch(input, init);
if (response.status === 402 && !response.headers.get(PAYMENT_REQUIRED_HEADER)) {
return normalizePaymentRequiredResponse(response);
}
return response;
};
}
/**
* Builds a `@x402/svm`-compatible `TransactionPartialSigner` from a web3auth
* Solana provider.
*
* The web3auth `signTransaction` call returns the signer's Ed25519 signature
* encoded in base58 (64 bytes). We decode it back to bytes and slot it into
* the signature dictionary that `@solana/kit`'s partial-signing helpers expect.
*/
function createSvmSigner(wallet, walletAddress) {
const base58Encoder = getBase58Encoder();
const signerAddress = address(walletAddress);
const clientSvmSigner = toClientSvmSigner({
address: signerAddress,
signTransactions: async transactions => {
const signatureDictionaries = await Promise.all(transactions.map(async tx => {
const signatureBase58 = await walletSignTransaction(wallet, tx);
return {
[signerAddress]: base58Encoder.encode(signatureBase58)
};
}));
return signatureDictionaries;
}
});
return clientSvmSigner;
}
/**
* Creates a payment-aware fetch function for a connected Solana wallet.
* Registers the exact SVM payment scheme and applies the body-shim so that
* servers returning v2 payment requirements in the response body are handled
* transparently.
*
* @param wallet - Connected Solana wallet (must have an account)
* @param walletAddress - The wallet's public key as a base58 address string
* @param rpcUrl - Optional custom Solana RPC URL (defaults to public cluster endpoints)
* @returns A fetch-compatible function that handles x402 payment flows
*/
function createSolanaX402Fetch(wallet, walletAddress, rpcUrl) {
if (!walletAddress) throw new Error("Wallet address is unavailable.");
const svmSigner = createSvmSigner(wallet, walletAddress);
const client = new x402Client().register(SOLANA_CAIP2_WILDCARD, new ExactSvmScheme(svmSigner, rpcUrl ? {
rpcUrl
} : undefined));
return wrapFetchWithPayment(withX402BodyShim(fetch), client);
}
/**
* Creates a payment-aware fetch function for an EVM typed-data signer.
* Registers the exact EVM payment scheme and applies the body-shim so that
* servers returning v2 payment requirements in the response body are handled
* transparently.
*
* @param signer - Minimal signer with an address and EIP-712 signTypedData implementation
* @returns A fetch-compatible function that handles x402 payment flows
*/
function createEvmX402Fetch(walletClient) {
var _walletClient$account;
const address = (_walletClient$account = walletClient.account) === null || _walletClient$account === void 0 ? void 0 : _walletClient$account.address;
if (!address) throw new Error("Wallet account is unavailable.");
const evmSigner = toClientEvmSigner({
address,
signTypedData: async params => {
const {
domain,
types,
primaryType,
message
} = params;
return walletClient.signTypedData({
account: address,
domain,
types,
primaryType,
message
});
}
});
const client = new x402Client().register(EVM_CAIP2_WILDCARD, new ExactEvmScheme(evmSigner));
return wrapFetchWithPayment(withX402BodyShim(fetch), client);
}
export { EVM_CAIP2_WILDCARD, PAYMENT_REQUIRED_HEADER, SOLANA_CAIP2_WILDCARD, createEvmX402Fetch, createProviderBackedEvmSigner, createSolanaX402Fetch, getEvmAddress, withX402BodyShim };