@bananahq/banana-sdk
Version:
Smart contract wallet sdk package by Banana Wallet
239 lines • 11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MyWalletApi = void 0;
// @ts-nocheck
const ethers_1 = require("ethers");
const utils_1 = require("ethers/lib/utils");
const sdk_1 = require("@account-abstraction/sdk");
const factories_1 = require("./types/factories");
const ethers_2 = require("ethers");
const contracts_1 = require("@account-abstraction/contracts");
/**
* An implementation of the BaseWalletAPI using the MyWallet contract.
* - contract deployer gets "entrypoint", "owner" addresses and "index" nonce
* - owner signs requests using normal "Ethereum Signed Message" (ether's signer.signMessage())
* - nonce method is "nonce()"
* - execute method is "execFromEntryPoint()"
*/
class MyWalletApi extends sdk_1.SimpleAccountAPI {
constructor(params) {
super(params);
this.getTouchIdSafeWalletContractInitializer = () => {
const TouchIdSafeWalletContractSingleton = factories_1.BananaAccount__factory.connect(this.singletonTouchIdSafeAddress, this.provider);
//@ts-ignore
const TouchIdSafeWalletContractInitializer = TouchIdSafeWalletContractSingleton.interface.encodeFunctionData("setupWithEntrypoint", [
this.ownerAddress,
1,
"0x0000000000000000000000000000000000000000",
"0x",
this.fallBackHandleraddress,
"0x0000000000000000000000000000000000000000",
0,
"0x0000000000000000000000000000000000000000",
this.entryPointAddress, // entrypoint
]);
return TouchIdSafeWalletContractInitializer;
};
this.singletonTouchIdSafeAddress = params._singletonTouchIdSafeAddress;
this.ownerAddress = params._ownerAddress;
this.fallBackHandleraddress = params._fallBackHandler;
this.saltNonce = params._saltNonce;
}
/**
*
* @param requestId - the has that your wallet must sign
* @returns the string that will used in userOp.signature & will be send to validateUserOp in your wallet's function
*/
async signRequestId(requestId) {
return await this.owner.signMessage((0, utils_1.arrayify)(requestId));
}
async _getAccountContract() {
if (this.accountContract == null) {
this.accountContract = factories_1.BananaAccount__factory.connect(await this.getAccountAddress(), this.provider);
}
return this.accountContract;
}
/**
* @method getTouchIdSafeWalletContractInitCode
* @params none
* @returns { string } TouchIdSafeWalletContractInitCode
* create initCode for TouchIdSafeWalletContract
*
* return the value to put into the "initCode" field, if the account is not yet deployed.
* this value holds the "factory" address, followed by this account's information
*/
async getAccountInitCode() {
if (this.factory == null) {
if (this.factoryAddress != null && this.factoryAddress !== "") {
this.factory = factories_1.BananaAccountProxyFactory__factory.connect(this.factoryAddress, this.provider);
}
else {
throw new Error("no factory to get initCode");
}
}
return (0, utils_1.hexConcat)([
this.factory.address,
this.factory.interface.encodeFunctionData("createProxyWithNonce", [
this.singletonTouchIdSafeAddress,
this.getTouchIdSafeWalletContractInitializer(),
this.saltNonce,
]),
]);
}
async getNonce() {
if (await this.checkAccountPhantom()) {
return ethers_1.BigNumber.from(0);
}
const entryPoint = contracts_1.EntryPoint__factory.connect(this.entryPointAddress, this.provider);
return await entryPoint.getNonce(this.getAccountAddress(), 0);
}
/**
* encode a method call from entryPoint to our contract
* @param target
* @param value
* @param data
*/
async encodeExecute(target, value, data) {
const accountContract = await this._getAccountContract();
const delegateCall = ethers_2.ethers.BigNumber.from("0");
return accountContract.interface.encodeFunctionData("execTransactionFromEntrypoint", [target, value, data, delegateCall]);
}
async encodeBatchExecute(info) {
const accountContract = await this._getAccountContract();
const delegateCall = ethers_2.ethers.BigNumber.from("0");
const target = info.map((data) => data.target);
const value = info.map((data) => data.value);
const data = info.map((data) => data.data);
return accountContract.interface.encodeFunctionData("execBatchTransactionFromEntrypoint", [target, value, data, delegateCall]);
}
async encodeUserOpCallDataAndGasLimitForBatchedTransaction(detailsForUserOp) {
function parseNumber(a) {
if (a == null || a === "")
return null;
return ethers_1.BigNumber.from(a.toString());
}
detailsForUserOp.map((op) => parseNumber(op.value) ?? ethers_1.BigNumber.from(0));
const callData = await this.encodeBatchExecute(detailsForUserOp);
let callGasLimit;
try {
callGasLimit =
parseNumber(detailsForUserOp.gasLimit) ??
(await this.provider.estimateGas({
from: this.entryPointAddress,
to: this.getAccountAddress(),
data: callData,
}));
}
catch (err) {
callGasLimit = ethers_2.ethers.BigNumber.from(1e6);
}
return {
callData,
callGasLimit,
};
}
async createUnsignedUserOpForBatchedTransaction(info) {
const { callData, callGasLimit } = await this.encodeUserOpCallDataAndGasLimitForBatchedTransaction(info);
const initCode = await this.getInitCode();
const initGas = await this.estimateCreationGas(initCode);
const verificationGasLimit = ethers_1.BigNumber.from(await this.getVerificationGasLimit()).add(initGas);
let { maxFeePerGas, maxPriorityFeePerGas } = info;
if (maxFeePerGas == null || maxPriorityFeePerGas == null) {
const feeData = await this.provider.getFeeData();
if (maxFeePerGas == null) {
maxFeePerGas = feeData.maxFeePerGas ?? undefined;
}
if (maxPriorityFeePerGas == null) {
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
}
}
const partialUserOp = {
sender: this.getAccountAddress(),
nonce: info.nonce ?? this.getNonce(),
initCode,
callData,
callGasLimit,
verificationGasLimit,
maxFeePerGas,
maxPriorityFeePerGas,
paymasterAndData: "0x",
};
let paymasterAndData;
if (this.paymasterAPI != null) {
// fill (partial) preVerificationGas (all except the cost of the generated paymasterAndData)
const userOpForPm = {
...partialUserOp,
preVerificationGas: await this.getPreVerificationGas(partialUserOp),
};
paymasterAndData = await this.paymasterAPI.getPaymasterAndData(userOpForPm);
}
partialUserOp.paymasterAndData = paymasterAndData ?? "0x";
return {
...partialUserOp,
preVerificationGas: this.getPreVerificationGas(partialUserOp),
signature: "",
};
}
async signUserOpHash(userOpHash) {
return await this.owner.signMessage((0, utils_1.arrayify)(userOpHash));
}
async getAccountAddress() {
const TouchIdSafeWalletContractProxyFactory = factories_1.BananaAccountProxyFactory__factory.connect(this.factoryAddress, this.provider);
const TouchIdSafeWalletContractInitializer = this.getTouchIdSafeWalletContractInitializer();
const TouchIdSafeWalletContractAddress = await TouchIdSafeWalletContractProxyFactory.getAddress(this.singletonTouchIdSafeAddress, this.saltNonce, TouchIdSafeWalletContractInitializer);
return TouchIdSafeWalletContractAddress;
}
async createUnsignedUserOpForBatchedTransaction(info) {
const { callData, callGasLimit } = await this.encodeUserOpCallDataAndGasLimitForBatchedTransaction(info);
const initCode = await this.getInitCode();
const initGas = await this.estimateCreationGas(initCode);
const verificationGasLimit = ethers_1.BigNumber.from(await this.getVerificationGasLimit()).add(initGas);
let { maxFeePerGas, maxPriorityFeePerGas } = info;
if (maxFeePerGas == null || maxPriorityFeePerGas == null) {
const feeData = await this.provider.getFeeData();
if (maxFeePerGas == null) {
maxFeePerGas = feeData.maxFeePerGas ?? undefined;
}
if (maxPriorityFeePerGas == null) {
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
}
}
const partialUserOp = {
sender: this.getAccountAddress(),
nonce: info.nonce ?? this.getNonce(),
initCode,
callData,
callGasLimit,
verificationGasLimit,
maxFeePerGas,
maxPriorityFeePerGas,
paymasterAndData: "0x",
};
let paymasterAndData;
if (this.paymasterAPI != null) {
// fill (partial) preVerificationGas (all except the cost of the generated paymasterAndData)
const userOpForPm = {
...partialUserOp,
preVerificationGas: await this.getPreVerificationGas(partialUserOp),
};
paymasterAndData = await this.paymasterAPI.getPaymasterAndData(userOpForPm);
}
partialUserOp.paymasterAndData = paymasterAndData ?? "0x";
return {
...partialUserOp,
preVerificationGas: this.getPreVerificationGas(partialUserOp),
signature: "",
};
}
async signUserOpHash(userOpHash) {
return await this.owner.signMessage((0, utils_1.arrayify)(userOpHash));
}
async getAccountAddress() {
const TouchIdSafeWalletContractProxyFactory = factories_1.BananaAccountProxyFactory__factory.connect(this.factoryAddress, this.provider);
const TouchIdSafeWalletContractInitializer = this.getTouchIdSafeWalletContractInitializer();
const TouchIdSafeWalletContractAddress = await TouchIdSafeWalletContractProxyFactory.getAddress(this.singletonTouchIdSafeAddress, this.saltNonce, TouchIdSafeWalletContractInitializer);
return TouchIdSafeWalletContractAddress;
}
}
exports.MyWalletApi = MyWalletApi;
//# sourceMappingURL=MyWalletApi.js.map