@thirdweb-dev/wallets
Version:
<p align="center"> <br /> <a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/legacy_packages/sdk/logo.svg?raw=true" width="200" alt=""/></a> <br /> </p> <h1 align="center">thirdweb Wallet SDK</h1> <p align="center"> <a h
268 lines (261 loc) • 9.71 kB
JavaScript
import { _ as _defineProperty } from './defineProperty-7303a112.esm.js';
import { C as Connector } from './connector-20f7cf73.esm.js';
import { H as HttpRpcClient } from './http-rpc-client-25e8ff7a.esm.js';
import { e as isZkSyncChain, f as ENTRYPOINT_ADDRESS } from './utils-80af2010.esm.js';
import { Signer, ethers, utils, BigNumber } from 'ethers';
import { defineReadOnly } from 'ethers/lib/utils';
import 'eventemitter3';
import './url-0d129c6b.esm.js';
import '@thirdweb-dev/chains';
import './headers-31b6ef3b.esm.js';
import '../evm/wallets/abstract/dist/thirdweb-dev-wallets-evm-wallets-abstract.esm.js';
import '@thirdweb-dev/sdk';
import '@account-abstraction/contracts';
const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50000;
const EIP712_TX_TYPE = 0x71;
const EIP712_TYPES = {
Transaction: [{
name: "txType",
type: "uint256"
}, {
name: "from",
type: "uint256"
}, {
name: "to",
type: "uint256"
}, {
name: "gasLimit",
type: "uint256"
}, {
name: "gasPerPubdataByteLimit",
type: "uint256"
}, {
name: "maxFeePerGas",
type: "uint256"
}, {
name: "maxPriorityFeePerGas",
type: "uint256"
}, {
name: "paymaster",
type: "uint256"
}, {
name: "nonce",
type: "uint256"
}, {
name: "value",
type: "uint256"
}, {
name: "data",
type: "bytes"
}, {
name: "factoryDeps",
type: "bytes32[]"
}, {
name: "paymasterInput",
type: "bytes"
}]
};
class ZkWrappedSigner extends Signer {
constructor(signer, httpRpcClient) {
super();
this.signer = signer;
defineReadOnly(this, "provider", signer.provider);
this.httpRpcClient = httpRpcClient;
}
async getAddress() {
return await this.signer.getAddress();
}
async signMessage(message) {
return await this.signer.signMessage(message);
}
async signTransaction(transaction) {
return await this.signer.signTransaction(transaction);
}
connect(provider) {
return new ZkWrappedSigner(this.signer.connect(provider), this.httpRpcClient);
}
_signTypedData(domain, types, value) {
return this.signer._signTypedData(domain, types, value);
}
async sendTransaction(transaction) {
return await this.sendZkSyncTransaction(transaction);
}
async sendZkSyncTransaction(_transaction) {
let transaction = await this.populateTransaction(_transaction);
if (!transaction.chainId) {
throw new Error("ChainId is required to send a ZkSync transaction");
}
if (!this.provider) {
throw new Error("Provider is required to send a ZkSync transaction");
}
const address = await this.getAddress();
const gasLimit = ethers.BigNumber.from(transaction.gasLimit || (await this.provider.estimateGas(transaction))).mul(3);
const gasPrice = ethers.BigNumber.from(transaction.gasPrice || (await this.provider.getGasPrice())).mul(2);
if (!transaction.maxFeePerGas) {
transaction.maxFeePerGas = gasPrice;
} else {
transaction.maxFeePerGas = transaction.maxFeePerGas.mul(2);
}
if (!transaction.maxPriorityFeePerGas) {
transaction.maxPriorityFeePerGas = gasPrice;
} else {
transaction.maxPriorityFeePerGas = transaction.maxPriorityFeePerGas.mul(2);
}
transaction = {
...transaction,
from: address,
gasLimit,
gasPrice,
chainId: (await this.provider.getNetwork()).chainId,
nonce: await this.provider.getTransactionCount(address),
type: 113,
value: BigInt(0)
};
const pmDataResult = await this.httpRpcClient?.zkPaymasterData(transaction);
transaction.customData = {
gasPerPubdata: DEFAULT_GAS_PER_PUBDATA_LIMIT,
factoryDeps: [],
paymasterParams: {
paymaster: pmDataResult.paymaster,
paymasterInput: pmDataResult.paymasterInput
}
};
const eip712tx = {
txType: EIP712_TX_TYPE,
from: BigInt(transaction.from || (await this.getAddress())).toString(),
to: BigInt(transaction.to || "0x0").toString(),
gasLimit: transaction.gasLimit ? Number(transaction.gasLimit) : 0,
gasPerPubdataByteLimit: DEFAULT_GAS_PER_PUBDATA_LIMIT,
maxFeePerGas: ethers.BigNumber.from(transaction.maxFeePerGas).toNumber(),
maxPriorityFeePerGas: ethers.BigNumber.from(transaction.maxPriorityFeePerGas).toNumber(),
paymaster: BigInt(pmDataResult.paymaster).toString(),
nonce: ethers.BigNumber.from(transaction.nonce).toNumber(),
value: ethers.BigNumber.from(transaction.value).toNumber(),
data: transaction.data || "0x",
factoryDeps: [],
paymasterInput: ethers.utils.arrayify(pmDataResult.paymasterInput)
};
const signature = await this._signTypedData({
name: "zkSync",
version: "2",
chainId: transaction.chainId
}, EIP712_TYPES, eip712tx);
const serializedTx = this.serialize(transaction, signature);
const zkSignedTx = {
from: transaction.from?.toString() || (await this.getAddress()),
to: transaction.to?.toString() || "",
gas: transaction.gasLimit?.toString() || "",
maxFeePerGas: transaction.maxFeePerGas?.toString() || "0",
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas?.toString() || "0",
signedTransaction: serializedTx,
paymaster: pmDataResult.paymaster
};
const broadcastResult = await this.httpRpcClient?.zkBroadcastTransaction(zkSignedTx);
const hash = broadcastResult.transactionHash;
return await this.provider?.getTransaction(hash);
}
serialize(transaction, signature) {
if (!transaction.customData && transaction.type !== EIP712_TX_TYPE) {
return utils.serializeTransaction(transaction, signature);
}
if (!transaction.chainId) {
throw Error("Transaction chainId isn't set!");
}
function formatNumber(value, name) {
const result = utils.stripZeros(BigNumber.from(value).toHexString());
if (result.length > 32) {
throw new Error(`Invalid length for ${name}!`);
}
return result;
}
if (!transaction.from) {
throw new Error("Explicitly providing `from` field is required for EIP712 transactions!");
}
const from = transaction.from;
const meta = transaction.customData ?? {};
const maxFeePerGas = transaction.maxFeePerGas || transaction.gasPrice || 0;
const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
const fields = [formatNumber(transaction.nonce || 0, "nonce"), formatNumber(maxPriorityFeePerGas, "maxPriorityFeePerGas"), formatNumber(maxFeePerGas, "maxFeePerGas"), formatNumber(transaction.gasLimit || 0, "gasLimit"), transaction.to ? utils.getAddress(transaction.to) : "0x", formatNumber(transaction.value || 0, "value"), transaction.data || "0x"];
if (signature) {
const sig = utils.splitSignature(signature);
fields.push(formatNumber(sig.recoveryParam, "recoveryParam"));
fields.push(utils.stripZeros(sig.r));
fields.push(utils.stripZeros(sig.s));
} else {
fields.push(formatNumber(transaction.chainId, "chainId"));
fields.push("0x");
fields.push("0x");
}
fields.push(formatNumber(transaction.chainId, "chainId"));
fields.push(utils.getAddress(from));
// Add meta
fields.push(formatNumber(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT, "gasPerPubdata"));
fields.push((meta.factoryDeps ?? []).map(dep => utils.hexlify(dep)));
if (meta.customSignature && ethers.utils.arrayify(meta.customSignature).length === 0) {
throw new Error("Empty signatures are not supported!");
}
fields.push(meta.customSignature || "0x");
if (meta.paymasterParams) {
fields.push([meta.paymasterParams.paymaster, ethers.utils.hexlify(meta.paymasterParams.paymasterInput)]);
} else {
fields.push([]);
}
return utils.hexConcat([[EIP712_TX_TYPE], utils.RLP.encode(fields)]);
}
}
class ZkSyncConnector extends Connector {
constructor(config) {
super();
_defineProperty(this, "chainId", 1);
this.config = config;
}
async connect(args) {
this.personalWallet = args.personalWallet;
this.chainId = await (await this.personalWallet.getSigner()).getChainId();
if (!(await isZkSyncChain(this.chainId))) {
throw new Error("Invalid zksync chain id");
}
const bundlerUrl = this.config.bundlerUrl || `https://${this.chainId}.bundler.thirdweb.com`;
const entryPointAddress = this.config.entryPointAddress || ENTRYPOINT_ADDRESS;
this.httpRpcClient = new HttpRpcClient(bundlerUrl, entryPointAddress, this.chainId, this.config.clientId, this.config.secretKey);
return this.getAddress();
}
disconnect() {
throw new Error("Method not implemented.");
}
async getAddress() {
const signer = await this.getSigner();
return signer.getAddress();
}
async getSigner() {
if (!this.personalWallet) {
throw new Error("Wallet not connected");
}
return new ZkWrappedSigner(await this.personalWallet.getSigner(), this.httpRpcClient);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
switchChain(chainId) {
throw new Error("Method not implemented.");
}
isConnected() {
return Promise.resolve(!!this.personalWallet);
}
setupListeners() {
throw new Error("Method not implemented.");
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateChains(chains) {
throw new Error("Method not implemented.");
}
async getProvider() {
if (!this.getSigner()) {
throw new Error("Personal wallet not connected");
}
const signer = await this.getSigner();
if (!signer.provider) {
throw new Error("Provider not found");
}
return signer.provider;
}
}
export { ZkSyncConnector };