soul-wallet-lib
Version:
The EIP-4337 library for Soul-Wallet
351 lines • 17.3 kB
JavaScript
;
/*
* @Description:
* @Version: 1.0
* @Autor: z.cejay@gmail.com
* @Date: 2022-08-05 16:08:23
* @LastEditors: cejay
* @LastEditTime: 2023-03-03 00:22:19
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserOperation = exports.SoulWalletLib = void 0;
const utils_1 = require("ethers/lib/utils");
const addressDefine = __importStar(require("../defines/address"));
const userOperation_1 = require("../entity/userOperation");
const soulWallet_1 = require("../contracts/soulWallet");
const walletProxy_1 = require("../contracts/walletProxy");
const tokenPaymaster_1 = require("../contracts/tokenPaymaster");
const decodeCallData_1 = require("../utils/decodeCallData");
const guardians_1 = require("../utils/guardians");
const tokens_1 = require("../utils/tokens");
const bundler_1 = require("../utils/bundler");
const converter_1 = require("../utils/converter");
const ethers_1 = require("ethers");
const gasFee_1 = require("../utils/gasFee");
const tokenAndPaymaster_1 = require("../utils/tokenAndPaymaster");
const deployFactory_1 = require("../utils/deployFactory");
const bytes32_1 = require("../defines/bytes32");
const walletFactory_1 = require("../contracts/walletFactory");
const address_1 = require("../defines/address");
const ABI_1 = require("../defines/ABI");
const eip1271_1 = require("../utils/eip1271");
class SoulWalletLib {
/**
* @constructor SoulWalletLib
* @param {String?} singletonFactory the singletonFactory address
* @returns {SoulWalletLib}
*/
constructor(singletonFactory) {
this.Bundler = bundler_1.Bundler;
this.EIP1271 = eip1271_1.EIP1271;
this.Tokens = {
ERC1155: new tokens_1.ERC1155(),
ERC20: new tokens_1.ERC20(),
ERC721: new tokens_1.ERC721(),
ETH: new tokens_1.ETH()
};
singletonFactory = singletonFactory || address_1.SingletonFactoryAddress;
this._singletonFactory = singletonFactory;
this._deployFactory = new deployFactory_1.DeployFactory(singletonFactory);
this.Utils = {
getNonce: this.getNonce,
DecodeCallData: decodeCallData_1.DecodeCallData,
suggestedGasFee: gasFee_1.CodefiGasFees,
tokenAndPaymaster: tokenAndPaymaster_1.TokenAndPaymaster,
deployFactory: this._deployFactory,
fromTransaction: new converter_1.Converter().fromTransaction
};
this.Guardian = new guardians_1.Guardian(this._singletonFactory);
}
/**
* get singletonFactory address
* @returns {String} address
*/
get singletonFactory() {
return this._singletonFactory;
}
/**
* get initialize data
* @param {String} entryPointAddress the entryPoint address
* @param {String} ownerAddress the owner address
* @param {Number} upgradeDelay the upgrade delay time
* @param {Number} guardianDelay the guardian delay time
* @param {String} guardianAddress the guardian contract address
* @returns {String} inithex
*/
getInitializeData(entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress) {
// function initialize(IEntryPoint anEntryPoint, address anOwner, IERC20 token,address paymaster)
// encodeFunctionData
let iface = new ethers_1.ethers.utils.Interface(soulWallet_1.SoulWalletContract.ABI);
let initializeData = iface.encodeFunctionData("initialize", [entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress]);
return initializeData;
}
/**
* get wallet code
*
* @param {string} walletLogicAddress the wallet logic contract address
* @param {string} entryPointAddress the entryPoint contract address
* @param {string} ownerAddress the owner address
* @param {number} upgradeDelay the upgrade delay time
* @param {number} guardianDelay the guardian delay time
* @param {string} guardianAddress the guardian contract address
* @param {({
* contractInterface: ContractInterface,
* bytecode: BytesLike | { object: string }
* })} [walletProxyConfig] the wallet proxy config
* @return {*} {string}
* @memberof SoulWalletLib
*/
getWalletCode(walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, walletProxyConfig) {
if (!walletProxyConfig) {
walletProxyConfig = {
contractInterface: walletProxy_1.WalletProxyContract.ABI,
bytecode: walletProxy_1.WalletProxyContract.bytecode
};
}
const initializeData = this.getInitializeData(entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress);
const factory = new ethers_1.ethers.ContractFactory(walletProxyConfig.contractInterface, walletProxyConfig.bytecode);
const walletBytecode = factory.getDeployTransaction(walletLogicAddress, initializeData).data;
return walletBytecode;
}
/**
* calculate wallet address by owner address
* @param {String} walletLogicAddress the wallet logic contract address
* @param {String} entryPointAddress the entryPoint address
* @param {String} ownerAddress the owner address
* @param {Number} upgradeDelay the upgrade delay time
* @param {Number} guardianDelay the guardian delay time
* @param {String} guardianAddress the guardian contract address
* @param {Number?} salt the salt number,default is 0
* @param {String?} singletonFactory the singletonFactory address,default is SingletonFactoryAddress
* @param {Object?} walletProxyConfig the wallet proxy config
* @returns {String} the wallet address
*/
calculateWalletAddress(walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, salt, singletonFactory, walletProxyConfig) {
const initCodeWithArgs = this.getWalletCode(walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, walletProxyConfig);
const initCodeHash = (0, utils_1.keccak256)(initCodeWithArgs);
const walletAddress = this.calculateWalletAddressByCodeHash(initCodeHash, salt, singletonFactory);
return walletAddress;
}
/**
* get the userOperation for active (first time) the wallet
* @param {String} walletLogicAddress the wallet logic contract address
* @param {String} entryPointAddress the entryPoint address
* @param {String} ownerAddress the owner address
* @param {Number} upgradeDelay the upgrade delay time
* @param {Number} guardianDelay the guardian delay time
* @param {String} guardianAddress the guardian contract address
* @param {String} paymasterAndData the paymaster address and data
* @param {NumberLike} maxFeePerGas the max fee per gas
* @param {NumberLike} maxPriorityFeePerGas the max priority fee per gas
* @param {Number?} salt the salt number,default is 0
* @param {String?} walletFactory the walletFactory contract address
* @param {String?} singletonFactory the singletonFactory contract address
* @param {Object?} walletProxyConfig the walletProxyConfig
* @returns {UserOperation} the userOperation
*/
activateWalletOp(walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, paymasterAndData, maxFeePerGas, maxPriorityFeePerGas, salt, walletFactory, singletonFactory, walletProxyConfig) {
const walletAddress = this.calculateWalletAddress(walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, salt, singletonFactory, walletProxyConfig);
const initCode = this.getPackedInitCodeUsingWalletFactory(walletFactory, walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, salt);
const userOperation = new userOperation_1.UserOperation(walletAddress, 0, initCode, undefined, undefined, maxFeePerGas, maxPriorityFeePerGas, paymasterAndData);
return userOperation;
}
getPackedInitCodeUsingWalletFactory(walletFactory, walletLogicAddress, entryPointAddress, ownerAddress, upgradeDelay, guardianDelay, guardianAddress, salt, walletFactoryInterface) {
if (!walletFactoryInterface) {
walletFactoryInterface = walletFactory_1.WalletFactoryContract.ABI;
}
let iface = new ethers_1.ethers.utils.Interface(walletFactoryInterface);
let packedInitCode = iface.encodeFunctionData("createWallet", [
entryPointAddress,
ownerAddress,
upgradeDelay,
guardianDelay,
guardianAddress,
this.number2Bytes32(salt)
]).substring(2);
if (!walletFactory) {
if (!walletLogicAddress) {
throw new Error("walletLogicAddress is undefined");
}
walletFactory = this._deployFactory.getAddress(walletLogicAddress);
}
return walletFactory.toLowerCase() + packedInitCode;
}
/**
* check if the token is supported by paymaster
* @param {ethers.providers.BaseProvider} etherProvider the ethers.js provider e.g. ethers.provider
* @param {String} payMasterAddress paymaster contract address
* @param {String[]} tokens token address list
* @returns {String[]} supported token address list
*/
paymasterSupportedToken(etherProvider, payMasterAddress, tokens) {
return __awaiter(this, void 0, void 0, function* () {
const paymaster = new ethers_1.ethers.Contract(payMasterAddress, tokenPaymaster_1.TokenPaymasterContract.ABI, etherProvider);
const reqs = [];
for (const token of tokens) {
reqs.push(paymaster.isSupportedToken(token));
}
const results = yield Promise.all(reqs);
const supportedTokens = [];
for (let i = 0; i < tokens.length; i++) {
if (results[i] === true) {
supportedTokens.push(tokens[i]);
}
}
return supportedTokens;
});
}
/**
* get paymaster exchange price
* @param {ethers.providers.BaseProvider} etherProvider the ethers.js provider e.g. ethers.provider
* @param {String} payMasterAddress paymaster contract address
* @param {String} token token address
* @param {Boolean?} fetchTokenDecimals fetch token decimals or not
* @returns {Object} exchange price
*/
getPaymasterExchangePrice(etherProvider, payMasterAddress, token, fetchTokenDecimals = false) {
return __awaiter(this, void 0, void 0, function* () {
const paymaster = new ethers_1.ethers.Contract(payMasterAddress, tokenPaymaster_1.TokenPaymasterContract.ABI, etherProvider);
if ((yield paymaster.isSupportedToken(token)) === true) {
const exchangePrice = yield paymaster.exchangePrice(token);
/*
exchangePrice.decimals
exchangePrice.price
*/
const price = exchangePrice.price;
const decimals = exchangePrice.decimals;
let tokenDecimals;
if (fetchTokenDecimals) {
const erc20Token = new ethers_1.ethers.Contract(token, ABI_1.ERC20, etherProvider);
tokenDecimals = yield erc20Token.decimals();
}
return {
price,
decimals,
tokenDecimals
};
}
else {
throw new Error("token is not supported");
}
});
}
/**
* get paymaster data
* @param {String} payMasterAddress paymaster contract address
* @param {String} token token address
* @param {BigNumber} maxCost token max cost
* @returns {String} paymasterAndData(hex string)
*/
getPaymasterData(payMasterAddress, token, maxCost) {
const enc = payMasterAddress.toLowerCase() + utils_1.defaultAbiCoder.encode(['address', 'uint256'], [token, maxCost]).substring(2);
return enc;
}
/**
* calculate wallet address
* @param {IContract} initContract the init Contract
* @param {any[] | undefined} initArgs the init args
* @param {Number} salt the salt number
* @returns {String} wallet address
*/
calculateWalletAddressByCode(initContract, initArgs, salt) {
const factory = new ethers_1.ethers.ContractFactory(initContract.ABI, initContract.bytecode);
const initCodeWithArgs = factory.getDeployTransaction(initArgs).data;
const initCodeHash = (0, utils_1.keccak256)(initCodeWithArgs);
return this.calculateWalletAddressByCodeHash(initCodeHash, salt);
}
/**
* convert number to bytes32
* @param {Number?} num the number
* @returns {String} bytes32
*/
number2Bytes32(num) {
if (num === undefined) {
return bytes32_1.bytes32_zero;
}
return (0, utils_1.hexZeroPad)((0, utils_1.hexlify)(num), 32);
}
/**
* calculate wallet address
* @param {String} initCodeHash the init code after keccak256
* @param {Number?} salt the salt number
* @param {String?} singletonFactory the singleton factory address
* @returns {String} the wallet address
*/
calculateWalletAddressByCodeHash(initCodeHash, salt, singletonFactory) {
return (0, utils_1.getCreate2Address)(singletonFactory || this._singletonFactory, this.number2Bytes32(salt), initCodeHash);
}
/**
* get nonce number from contract wallet
* @param {string} walletAddress same as userOperation.sender
* @param {ethers.providers.BaseProvider} etherProvider the ethers.js provider e.g. ethers.provider
* @param {String?} defaultBlock "earliest", "latest" and "pending"
* @returns {Number} the next nonce number
*/
getNonce(walletAddress, etherProvider, defaultBlock = 'latest') {
return __awaiter(this, void 0, void 0, function* () {
try {
const code = yield etherProvider.getCode(walletAddress, defaultBlock);
// check contract is exist
if (code === '0x') {
return 0;
}
else {
const contract = new ethers_1.ethers.Contract(walletAddress, [{ "inputs": [], "name": "nonce", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }], etherProvider);
const nonce = yield contract.nonce();
if (nonce === undefined) {
throw new Error('nonce is undefined');
}
return ethers_1.BigNumber.from(nonce).toNumber();
}
}
catch (error) {
throw error;
}
});
}
}
exports.SoulWalletLib = SoulWalletLib;
/**
*
*/
SoulWalletLib.Defines = {
AddressZero: addressDefine.AddressZero,
SingletonFactoryAddress: addressDefine.SingletonFactoryAddress,
bytes32_zero: bytes32_1.bytes32_zero
};
var userOperation_2 = require("../entity/userOperation");
Object.defineProperty(exports, "UserOperation", { enumerable: true, get: function () { return userOperation_2.UserOperation; } });
//# sourceMappingURL=soulWalletLib.js.map