@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
270 lines (262 loc) • 9.91 kB
JavaScript
'use strict';
var defineProperty = require('./defineProperty-b749763b.cjs.prod.js');
var connector = require('./connector-1b2fa06d.cjs.prod.js');
var httpRpcClient = require('./http-rpc-client-2e1229be.cjs.prod.js');
var utils$1 = require('./utils-5241e2ba.cjs.prod.js');
var ethers = require('ethers');
var utils = require('ethers/lib/utils');
require('eventemitter3');
require('./url-8b7f19db.cjs.prod.js');
require('@thirdweb-dev/chains');
require('./headers-140df57f.cjs.prod.js');
require('../evm/wallets/abstract/dist/thirdweb-dev-wallets-evm-wallets-abstract.cjs.prod.js');
require('@thirdweb-dev/sdk');
require('@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 ethers.Signer {
constructor(signer, httpRpcClient) {
super();
this.signer = signer;
utils.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.ethers.BigNumber.from(transaction.gasLimit || (await this.provider.estimateGas(transaction))).mul(3);
const gasPrice = ethers.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.ethers.BigNumber.from(transaction.maxFeePerGas).toNumber(),
maxPriorityFeePerGas: ethers.ethers.BigNumber.from(transaction.maxPriorityFeePerGas).toNumber(),
paymaster: BigInt(pmDataResult.paymaster).toString(),
nonce: ethers.ethers.BigNumber.from(transaction.nonce).toNumber(),
value: ethers.ethers.BigNumber.from(transaction.value).toNumber(),
data: transaction.data || "0x",
factoryDeps: [],
paymasterInput: ethers.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 ethers.utils.serializeTransaction(transaction, signature);
}
if (!transaction.chainId) {
throw Error("Transaction chainId isn't set!");
}
function formatNumber(value, name) {
const result = ethers.utils.stripZeros(ethers.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 ? ethers.utils.getAddress(transaction.to) : "0x", formatNumber(transaction.value || 0, "value"), transaction.data || "0x"];
if (signature) {
const sig = ethers.utils.splitSignature(signature);
fields.push(formatNumber(sig.recoveryParam, "recoveryParam"));
fields.push(ethers.utils.stripZeros(sig.r));
fields.push(ethers.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(ethers.utils.getAddress(from));
// Add meta
fields.push(formatNumber(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT, "gasPerPubdata"));
fields.push((meta.factoryDeps ?? []).map(dep => ethers.utils.hexlify(dep)));
if (meta.customSignature && ethers.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.ethers.utils.hexlify(meta.paymasterParams.paymasterInput)]);
} else {
fields.push([]);
}
return ethers.utils.hexConcat([[EIP712_TX_TYPE], ethers.utils.RLP.encode(fields)]);
}
}
class ZkSyncConnector extends connector.Connector {
constructor(config) {
super();
defineProperty._defineProperty(this, "chainId", 1);
this.config = config;
}
async connect(args) {
this.personalWallet = args.personalWallet;
this.chainId = await (await this.personalWallet.getSigner()).getChainId();
if (!(await utils$1.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 || utils$1.ENTRYPOINT_ADDRESS;
this.httpRpcClient = new httpRpcClient.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;
}
}
exports.ZkSyncConnector = ZkSyncConnector;