@bananahq/banana-sdk
Version:
Smart contract wallet sdk package by Banana Wallet
193 lines • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BananaSigner = void 0;
const ethers_1 = require("ethers");
const sdk_1 = require("@account-abstraction/sdk");
const sendUserOp_1 = require("./bundler/sendUserOp");
const getRequestData_1 = require("./paymaster/getRequestData");
const getPaymasterAndData_1 = require("./paymaster/getPaymasterAndData");
const paymasterServiceStatus_1 = require("./utils/paymasterServiceStatus");
const Constants_1 = require("./Constants");
class BananaSigner extends sdk_1.ERC4337EthersSigner {
constructor(config, originalSigner, erc4337provider, httpRpcClient, smartAccountAPI, provider, _paymasterOption) {
super(config, originalSigner, erc4337provider, httpRpcClient, smartAccountAPI);
this.config = config;
this.originalSigner = originalSigner;
this.erc4337provider = erc4337provider;
this.httpRpcClient = httpRpcClient;
this.smartAccountAPI = smartAccountAPI;
this.jsonRpcProvider = provider;
this.paymasterOption = _paymasterOption;
this.ownerSigner = originalSigner;
this.getAddress();
}
// need to do some changes in it
async sendTransaction(transaction) {
const tx = await this.populateTransaction(transaction);
await this.verifyAllNecessaryFields(tx);
let userOperation = await this.smartAccountAPI.createUnsignedUserOp({
target: tx.to ?? "",
data: tx.data?.toString() ?? "",
value: tx.value,
gasLimit: tx.gasLimit,
});
userOperation.verificationGasLimit = 1.5e6;
userOperation.preVerificationGas = ethers_1.ethers.BigNumber.from(await userOperation.preVerificationGas).add(5000);
let minGasRequired = ethers_1.ethers.BigNumber.from(userOperation?.callGasLimit)
.add(ethers_1.ethers.BigNumber.from(userOperation?.verificationGasLimit))
.add(ethers_1.ethers.BigNumber.from(userOperation?.callGasLimit))
.add(ethers_1.ethers.BigNumber.from(userOperation?.preVerificationGas));
let currentGasPrice = await this.jsonRpcProvider.getGasPrice();
let minBalanceRequiredForGas = minGasRequired.mul(currentGasPrice);
//@ts-ignore
let userBalance = await this.jsonRpcProvider.getBalance(userOperation?.sender);
const networkInfo = await this.jsonRpcProvider.getNetwork();
const paymasterStatus = await (0, paymasterServiceStatus_1.paymasterServiceStatus)(this.paymasterOption);
if (this.paymasterOption && paymasterStatus) {
const requestData = await (0, getRequestData_1.getRequestDataForPaymaster)(userOperation, this.paymasterOption);
const paymasterAndData = await (0, getPaymasterAndData_1.getPaymasterAndData)(this.paymasterOption, requestData);
(userOperation || { paymasterAndData: null }).paymasterAndData =
paymasterAndData || "";
}
;
let fundForTransaction = minBalanceRequiredForGas;
let fundsForTxn = parseFloat(ethers_1.ethers.utils.formatEther(fundForTransaction));
let factor = 0;
if (userOperation.initCode === '0x') {
factor = Constants_1.SCALING_FACTOR.find(scalingFactor => scalingFactor.chainId === networkInfo.chainId)?.txnFactor;
}
else {
factor = Constants_1.SCALING_FACTOR.find(scalingFactor => scalingFactor.chainId === networkInfo.chainId)?.deploymentTxnFactor;
}
;
//@ts-ignore
fundsForTxn = fundsForTxn + (fundsForTxn * factor);
fundForTransaction = ethers_1.ethers.utils.parseEther(fundsForTxn.toFixed(18));
if (!paymasterStatus && (userBalance.lt(minBalanceRequiredForGas))) {
const fundTransaction = {
to: userOperation.sender,
value: ethers_1.ethers.utils.parseEther(String(parseInt(fundForTransaction._hex) / 10 ** 18)),
data: '0x',
gasLimit: '0x55555',
};
const txnResponse = await this.originalSigner.sendTransaction(fundTransaction);
const txnReceipt = await txnResponse.wait();
}
const message = await this.smartAccountAPI.getUserOpHash(userOperation);
let signatureObtained;
try {
signatureObtained = await this.smartAccountAPI.signUserOpHash(message);
userOperation.signature = signatureObtained;
}
catch (err) {
return Promise.reject(err);
}
let transactionResponse = await this.erc4337provider.constructUserOpTransactionResponse(userOperation);
try {
if (networkInfo.chainId === 81 || networkInfo.chainId === 592) {
//! sending UserOp directly to ep for shibuya
const receipt = await (0, sendUserOp_1.sendTransaction)(userOperation, this.jsonRpcProvider);
transactionResponse = receipt;
}
else {
await this.httpRpcClient.sendUserOpToBundler(userOperation);
const userOpHash = transactionResponse.hash;
let transactionReceipt = await this.erc4337provider.waitForTransaction(userOpHash, 0, 10000000);
transactionResponse.hash = transactionReceipt.logs[0].transactionHash;
}
}
catch (error) {
console.error("sendUserOpToBundler failed", error);
throw this.unwrapError(error);
}
// TODO: handle errors - transaction that is "rejected" by bundler is _not likely_ to ever resolve its "wait()"
return transactionResponse;
}
async sendBatchTransaction(transactions) {
let txns = [];
for (let i = 0; i < transactions.length; i++) {
const txn = await this.populateTransaction(transactions[i]);
await this.verifyAllNecessaryFields(txn);
txns.push(txn);
}
const info = txns.map((txn) => {
return {
target: txn.to ?? "",
data: txn.data?.toString() ?? "",
value: txn.value,
gasLimit: txn.gasLimit,
};
});
let userOperation = await this.smartAccountAPI.createUnsignedUserOpForBatchedTransaction(info);
let minGasRequired = ethers_1.ethers.BigNumber.from(userOperation?.callGasLimit)
.add(ethers_1.ethers.BigNumber.from(userOperation?.verificationGasLimit))
.add(ethers_1.ethers.BigNumber.from(userOperation?.callGasLimit))
.add(ethers_1.ethers.BigNumber.from(userOperation?.preVerificationGas));
let currentGasPrice = await this.jsonRpcProvider.getGasPrice();
let minBalanceRequiredForGas = minGasRequired.mul(currentGasPrice);
//@ts-ignore
let userBalance = await this.jsonRpcProvider.getBalance(userOperation?.sender);
const networkInfo = await this.jsonRpcProvider.getNetwork();
const paymasterStatus = await (0, paymasterServiceStatus_1.paymasterServiceStatus)(this.paymasterOption);
if (this.paymasterOption && paymasterStatus) {
const requestData = await (0, getRequestData_1.getRequestDataForPaymaster)(userOperation, this.paymasterOption);
const paymasterAndData = await (0, getPaymasterAndData_1.getPaymasterAndData)(this.paymasterOption, requestData);
(userOperation || { paymasterAndData: null }).paymasterAndData =
paymasterAndData || "";
}
let fundForTransaction = minBalanceRequiredForGas;
let fundsForTxn = parseFloat(ethers_1.ethers.utils.formatEther(fundForTransaction));
let factor = 0;
if (userOperation.initCode === '0x') {
factor = Constants_1.SCALING_FACTOR.find(scalingFactor => scalingFactor.chainId === networkInfo.chainId)?.txnFactor;
}
else {
factor = Constants_1.SCALING_FACTOR.find(scalingFactor => scalingFactor.chainId === networkInfo.chainId)?.deploymentTxnFactor;
}
;
//@ts-ignore
fundsForTxn = fundsForTxn + (fundsForTxn * factor);
fundForTransaction = ethers_1.ethers.utils.parseEther(fundsForTxn.toFixed(18));
if (!paymasterStatus && (userBalance.lt(minBalanceRequiredForGas))) {
const fundTransaction = {
to: userOperation.sender,
value: ethers_1.ethers.utils.parseEther(String(parseInt(fundForTransaction._hex) / 10 ** 18)),
data: '0x',
gasLimit: '0x55555',
};
const txnResponse = await this.originalSigner.sendTransaction(fundTransaction);
const txnReceipt = await txnResponse.wait();
}
const message = await this.smartAccountAPI.getUserOpHash(userOperation);
let signatureObtained;
try {
signatureObtained = await this.smartAccountAPI.signUserOpHash(message);
userOperation.signature = signatureObtained;
}
catch (err) {
return Promise.reject(err);
}
let transactionResponse = await this.erc4337provider.constructUserOpTransactionResponse(userOperation);
try {
if (networkInfo.chainId === 81 || networkInfo.chainId === 592) {
//! sending UserOp directly to ep for shibuya
const receipt = await (0, sendUserOp_1.sendTransaction)(userOperation, this.jsonRpcProvider);
transactionResponse = receipt;
}
else {
await this.httpRpcClient.sendUserOpToBundler(userOperation);
const userOpHash = transactionResponse.hash;
let transactionReceipt = await this.erc4337provider.waitForTransaction(userOpHash, 0, 10000000);
transactionResponse.hash = transactionReceipt.logs[0].transactionHash;
}
}
catch (error) {
console.error("sendUserOpToBundler failed", error);
throw this.unwrapError(error);
}
// TODO: handle errors - transaction that is "rejected" by bundler is _not likely_ to ever resolve its "wait()"
return transactionResponse;
}
}
exports.BananaSigner = BananaSigner;
//# sourceMappingURL=BananaSigner.js.map