diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b249fd5..d7960137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [7.0.0] - 2025-06-03 +### Breaking Changes +- Updated HookMultiplexer, CredibleAccountModule and Bootstrap contract addresses for all chains +- getInitCode logic updated to include + - HookMultiplexer as defaultHook with CredibleAccountModule as globalHook in Multiplexer + - CredibleAccountModule to be installed as validator along with MultiSigECDSAModule +- Results in a change of precomputed modular account address. + + ## [6.1.0] - 2025-05-28 ### Breaking Changes - Changed contract address for Wallet Factory from `0x2A40091f044e48DEB5C0FCbc442E443F3341B451` to `0x38CC0EDdD3a944CA17981e0A19470d2298B8d43a`. diff --git a/examples/helpers/sdk-helper.ts b/examples/helpers/sdk-helper.ts index a019e4c7..2bbbaf31 100644 --- a/examples/helpers/sdk-helper.ts +++ b/examples/helpers/sdk-helper.ts @@ -9,7 +9,8 @@ export const generateModularSDKInstance = (privateKey: string, chainId: number, { chainId: chainId, bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey), - index: index + index: index, + accountAddress: process.env.MODULAR_ACCOUNT_ADDRESS }) return modularSdk; diff --git a/examples/modules/credible-account-modules/credible-account-ecosystem-install.ts b/examples/modules/credible-account-modules/credible-account-ecosystem-install.ts new file mode 100644 index 00000000..db8c773b --- /dev/null +++ b/examples/modules/credible-account-modules/credible-account-ecosystem-install.ts @@ -0,0 +1,114 @@ +import { printOp } from '../../../src/sdk/common/OperationUtils'; +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { encodeAbiParameters, encodeFunctionData, Hex, parseAbi } from 'viem'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { getHookMultiPlexerInitData } from '../../pulse/utils'; +import { accountAbi } from '../../../src/sdk/common/abis'; +import { NetworkConfig, Networks } from '../../../src'; +import { _makeBootstrapConfig } from '../../../src/sdk/base/Bootstrap'; + +dotenv.config(); + +const bundlerApiKey = process.env.API_KEY || "etherspot_public_key"; + +// tsx examples/modules/credible-account-modules/credible-account-ecosystem-install.ts +async function main() { + + const chainId = Number(process.env.CHAIN_ID); + + // Init SDK + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, + bundlerApiKey, + ); + + const networkConfig : NetworkConfig = Networks[chainId]; + + const HOOK_MULTIPLEXER_ADDRESS = networkConfig.contracts.hookMultiPlexer as Hex; + const RESOURCE_LOCK_VALIDATOR_ADDRESS = networkConfig.contracts.resourceLockValidator as Hex; + const CREDIBLE_ACCOUNT_MODULE_ADDRESS = networkConfig.contracts.credibleAccountModule as Hex; + + // Get counterfactual of ModularEtherspotWallet... + const address: Hex = (await modularSdk.getCounterFactualAddress()) as Hex; + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); + // Get native balance + const balance = await modularSdk.getNativeBalance(); + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet native balance: ${balance}`); + // Clear existing UserOps from batch + await modularSdk.clearUserOpsFromBatch(); + + /*////////////////////////////////////////////////////////////// + INSTALL HOOK MULTIPLEXER WITH CREDIBLE ACCOUNT MODULE - AS HOOK + //////////////////////////////////////////////////////////////*/ + + //Get HookMultiPlexer init data with CredibleAccountHook as global subhook + let hmpInitData = getHookMultiPlexerInitData([CREDIBLE_ACCOUNT_MODULE_ADDRESS]); + const config = _makeBootstrapConfig(HOOK_MULTIPLEXER_ADDRESS, hmpInitData); + console.log('\x1b[33m%s\x1b[0m', `Credible-Setup -> HmpInitData: ${hmpInitData}`); + const hmpInstallCalldata = encodeFunctionData({ + abi: parseAbi(accountAbi), + functionName: 'installModule', + args: [BigInt(MODULE_TYPE.HOOK), HOOK_MULTIPLEXER_ADDRESS, config.data], + }); + console.log('\x1b[33m%s\x1b[0m', `Credible-Setup -> HmpInstallCalldata: ${hmpInstallCalldata}`); + // Add UserOp to batch + await modularSdk.addUserOpsToBatch({ to: address, data: hmpInstallCalldata }); + + /*////////////////////////////////////////////////////////////// + INSTALL CREDIBLE ACCOUNT MODULE - AS VALIDATOR + //////////////////////////////////////////////////////////////*/ + + //Get CredibleAccountValidator init data + let cavInitData = encodeAbiParameters([{ type: 'uint256' }], [BigInt(MODULE_TYPE.VALIDATOR)]); + console.log('\x1b[33m%s\x1b[0m', `Credible-Setup -> CavInitData: ${cavInitData}`); + const cavInstallCalldata = encodeFunctionData({ + abi: parseAbi(accountAbi), + functionName: 'installModule', + args: [BigInt(MODULE_TYPE.VALIDATOR), CREDIBLE_ACCOUNT_MODULE_ADDRESS, cavInitData], + }); + console.log('\x1b[33m%s\x1b[0m', `Credible-Setup -> cavInstallCalldata: ${cavInstallCalldata}`); + //Add UserOp to batch + await modularSdk.addUserOpsToBatch({ to: address, data: cavInstallCalldata }); + + /*////////////////////////////////////////////////////////////// + INSTALL RESOURCE LOCK VALIDATOR + //////////////////////////////////////////////////////////////*/ + + // Get ResourceLockValidator init data + let rlvInitData = encodeAbiParameters([{ type: 'address' }], [modularSdk.getEOAAddress()]); + console.log('\x1b[33m%s\x1b[0m', `Credible-Setup -> RlvInitData: ${rlvInitData}`); + const rlvInstallCalldata = encodeFunctionData({ + abi: parseAbi(accountAbi), + functionName: 'installModule', + args: [BigInt(MODULE_TYPE.VALIDATOR), RESOURCE_LOCK_VALIDATOR_ADDRESS, rlvInitData], + }); + console.log('\x1b[33m%s\x1b[0m', `Credible-Setup -> RlvInstallCalldata: ${rlvInstallCalldata}`); + // Add UserOp to batch + await modularSdk.addUserOpsToBatch({ to: address, data: rlvInstallCalldata }); + + /*////////////////////////////////////////////////////////////// + ESTIMATE/SEND USER OP + //////////////////////////////////////////////////////////////*/ + + // Estimate UserOp + const op = await modularSdk.estimate(); + console.log(`Estimate UserOp: ${await printOp(op)}`); + // Send UserOp + const uoHash = await modularSdk.send(op); + console.log(`UserOpHash: ${uoHash}`); + // Await transaction hash + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 1200000; // 1 minute timeout + while (userOpsReceipt == null && Date.now() < timeout) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/hook-multiplexer-has-hook.ts b/examples/modules/credible-account-modules/hook-multiplexer-has-hook.ts new file mode 100644 index 00000000..daa47d05 --- /dev/null +++ b/examples/modules/credible-account-modules/hook-multiplexer-has-hook.ts @@ -0,0 +1,28 @@ +import { EtherspotBundler, ModularSdk } from '../../../src'; +import * as dotenv from 'dotenv'; +import { getViemAccount } from '../../../src/sdk/common/utils'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/hook-multiplexer-has-hook.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + Number(process.env.CHAIN_ID), bundlerApiKey); + + // get address of EtherspotWallet + const address: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); + + const moduleInfo = await modularSdk.getAllModules(); + console.log(`moduleInfo: ${JSON.stringify(moduleInfo)}`); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/install-credible-account-module.ts b/examples/modules/credible-account-modules/install-credible-account-module.ts new file mode 100644 index 00000000..5a6a6e56 --- /dev/null +++ b/examples/modules/credible-account-modules/install-credible-account-module.ts @@ -0,0 +1,44 @@ +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { encodeAbiParameters } from 'viem'; +import { NetworkConfig, Networks } from '../../../src'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/install-credible-account-module.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + const chainId = Number(process.env.CHAIN_ID); + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, bundlerApiKey); + + // get address of EtherspotWallet + const address: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); + + let cavInitData = encodeAbiParameters([{ type: 'uint256' }], [BigInt(MODULE_TYPE.VALIDATOR)]); + console.log('\x1b[33m%s\x1b[0m', `CavInitData: ${cavInitData}`); + + const networkConfig : NetworkConfig = Networks[chainId]; + const uoHash = await modularSdk.installModule(MODULE_TYPE.VALIDATOR, networkConfig.contracts.credibleAccountModule, cavInitData); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/install-hook-multiplexer.ts b/examples/modules/credible-account-modules/install-hook-multiplexer.ts new file mode 100644 index 00000000..d805c219 --- /dev/null +++ b/examples/modules/credible-account-modules/install-hook-multiplexer.ts @@ -0,0 +1,44 @@ +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { encodeAbiParameters, Hex } from 'viem'; +import { NetworkConfig, Networks } from '../../../src'; +import { getHookMultiPlexerInitData } from '../../pulse/utils'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/install-hook-multiplexer.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + const chainId = Number(process.env.CHAIN_ID); + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, bundlerApiKey); + + // get address of EtherspotWallet + const address: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); + + const networkConfig : NetworkConfig = Networks[chainId]; + + let hmpInitData = getHookMultiPlexerInitData([networkConfig.contracts.credibleAccountModule as Hex]); + const uoHash = await modularSdk.installModule(MODULE_TYPE.HOOK, networkConfig.contracts.credibleAccountModule, hmpInitData); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/install-resource-lock-validator.ts b/examples/modules/credible-account-modules/install-resource-lock-validator.ts new file mode 100644 index 00000000..3956af75 --- /dev/null +++ b/examples/modules/credible-account-modules/install-resource-lock-validator.ts @@ -0,0 +1,43 @@ +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { encodeAbiParameters } from 'viem'; +import { NetworkConfig, Networks } from '../../../src'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/install-resource-lock-validator.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + const chainId = Number(process.env.CHAIN_ID); + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, bundlerApiKey); + + // get address of EtherspotWallet + const address: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); + + let rlvInitData = encodeAbiParameters([{ type: 'address' }], [modularSdk.getEOAAddress()]); + + const networkConfig : NetworkConfig = Networks[chainId]; + const uoHash = await modularSdk.installModule(MODULE_TYPE.VALIDATOR, networkConfig.contracts.resourceLockValidator, rlvInitData); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/uninstall-credible-account-module.ts b/examples/modules/credible-account-modules/uninstall-credible-account-module.ts new file mode 100644 index 00000000..c1564586 --- /dev/null +++ b/examples/modules/credible-account-modules/uninstall-credible-account-module.ts @@ -0,0 +1,59 @@ +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { encodeAbiParameters, Hex } from 'viem'; +import { NetworkConfig, Networks } from '../../../src'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/uninstall-credible-account-module.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + + const chainId = Number(process.env.CHAIN_ID); + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, bundlerApiKey); + + // get address of EtherspotWallet + const etherspotWalletAddress: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${etherspotWalletAddress}`); + + const networkConfig : NetworkConfig = Networks[chainId]; + + //this should be previous node address of the module to be uninstalled and the deinit data + //deinit data is the data that is passed to the module to be uninstalled + // here we need to call the function which can find out the address of previous node of the module to be uninstalled + // and the deinit data can be 0x00 as default value + const deInitDataDefault = encodeAbiParameters([{ type: 'uint256' }, { type: 'address' }], + [BigInt(MODULE_TYPE.VALIDATOR), etherspotWalletAddress as Hex]); + + //generate deinit data... + const deInitData = await modularSdk.generateModuleDeInitData( + MODULE_TYPE.VALIDATOR, + networkConfig.contracts.credibleAccountModule, + deInitDataDefault); + + console.log(`deinitData: ${deInitData}`); + + const uoHash = await modularSdk.uninstallModule(MODULE_TYPE.VALIDATOR, + networkConfig.contracts.credibleAccountModule, deInitData); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/uninstall-hook-multiplexer.ts b/examples/modules/credible-account-modules/uninstall-hook-multiplexer.ts new file mode 100644 index 00000000..a5a8e24b --- /dev/null +++ b/examples/modules/credible-account-modules/uninstall-hook-multiplexer.ts @@ -0,0 +1,53 @@ +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { encodeAbiParameters, Hex } from 'viem'; +import { NetworkConfig, Networks } from '../../../src'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/uninstall-hook-multiplexer.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + + const chainId = Number(process.env.CHAIN_ID); + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, bundlerApiKey); + + // get address of EtherspotWallet + const etherspotWalletAddress: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${etherspotWalletAddress}`); + + const networkConfig : NetworkConfig = Networks[chainId]; + + //this should be previous node address of the module to be uninstalled and the deinit data + //deinit data is the data that is passed to the module to be uninstalled + // here we need to call the function which can find out the address of previous node of the module to be uninstalled + // and the deinit data can be 0x00 as default value + const deinitData = encodeAbiParameters([{ type: 'uint256' }], + [BigInt(MODULE_TYPE.HOOK)]); + + console.log(`deinitData: ${deinitData}`); + + const uoHash = await modularSdk.uninstallModule(MODULE_TYPE.HOOK, + networkConfig.contracts.hookMultiPlexer, deinitData); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/modules/credible-account-modules/uninstall-resourcelock-validator.ts b/examples/modules/credible-account-modules/uninstall-resourcelock-validator.ts new file mode 100644 index 00000000..569904c3 --- /dev/null +++ b/examples/modules/credible-account-modules/uninstall-resourcelock-validator.ts @@ -0,0 +1,55 @@ +import * as dotenv from 'dotenv'; +import { MODULE_TYPE, sleep } from '../../../src/sdk/common'; +import { generateModularSDKInstance } from '../../helpers/sdk-helper'; +import { encodeAbiParameters, Hex } from 'viem'; +import { NetworkConfig, Networks } from '../../../src'; + +dotenv.config(); + +// tsx examples/modules/credible-account-modules/uninstall-resourcelock-validator.ts +async function main() { + const bundlerApiKey = 'etherspot_public_key'; + + const chainId = Number(process.env.CHAIN_ID); + + // initializating sdk... + const modularSdk = generateModularSDKInstance( + process.env.WALLET_PRIVATE_KEY as string, + chainId, bundlerApiKey); + + // get address of EtherspotWallet + const etherspotWalletAddress: string = await modularSdk.getCounterFactualAddress(); + + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${etherspotWalletAddress}`); + + const networkConfig : NetworkConfig = Networks[chainId]; + + //this should be previous node address of the module to be uninstalled and the deinit data + //deinit data is the data that is passed to the module to be uninstalled + // here we need to call the function which can find out the address of previous node of the module to be uninstalled + // and the deinit data can be 0x00 as default value + const deInitDataDefault = '0x00'; + //generate deinit data... + const deInitData = await modularSdk.generateModuleDeInitData( + MODULE_TYPE.VALIDATOR, + networkConfig.contracts.resourceLockValidator, + deInitDataDefault); + + const uoHash = await modularSdk.uninstallModule(MODULE_TYPE.VALIDATOR, + networkConfig.contracts.resourceLockValidator, deInitData); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/pulse/utils.ts b/examples/pulse/utils.ts index f0e8bd25..9946b6df 100644 --- a/examples/pulse/utils.ts +++ b/examples/pulse/utils.ts @@ -45,17 +45,8 @@ export function getHookMultiPlexerInitData( delegatecallHooks, sigHooks, targetSigHooks - ] as any); + ]); - console.log('Encoded Data:', encodedData); - - const hookMultiplexerInitData = encodeFunctionData({ - abi: HookMultiplexer, - args: [encodedData], - functionName: 'onInstall', - }); - - console.log('Hook Multiplexer Init Data:', hookMultiplexerInitData); - - return hookMultiplexerInitData; + console.log('\x1b[33m%s\x1b[0m', `HookMultiPlexer Init Data: ${encodedData}`); + return encodedData; } \ No newline at end of file diff --git a/examples/resource-lock/claim-resourcelock-session-key.ts b/examples/resource-lock/claim-resourcelock-session-key.ts new file mode 100644 index 00000000..a23bbe09 --- /dev/null +++ b/examples/resource-lock/claim-resourcelock-session-key.ts @@ -0,0 +1,192 @@ +import { BigNumber, ethers } from 'ethers'; +import * as dotenv from 'dotenv'; +import { printOp } from '../../src/sdk/common/OperationUtils'; +import { getViemAccount, sleep } from '../../src/sdk/common'; +import { ModularSdk, EtherspotBundler, Networks } from '../../src'; +import { ERC20_ABI } from '../../src/sdk/helpers/abi/ERC20_ABI'; +import { encodeFunctionData, Hex, parseAbi, parseUnits } from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; +import { getSecretFromBidHashAndSessionKey } from './get-session-key-pair'; +import { bigintReplacer, getSessionDetailsByWalletAddressAndSessionKey, SessionDetails } from './get-session-key-details'; +import { BidSearchResult, searchBid } from './search-bid'; +import { getResourceLockInfo, ResourceLockInfoResponse } from './get-resourcelock-info'; +import { IntentSearchResult, searchIntent } from './search-intent'; + +dotenv.config(); + +const walletPrivateKey = process.env.WALLET_PRIVATE_KEY as string; +const bundlerApiKey = process.env.BUNDLER_API_KEY as string; +const bidHash = '0xf9385007459e20a6f37805309a73a1eaae824e06ec136ae3b95452229a1ce27f'; + +// tsx examples/resource-lock/claim-resourcelock-session-key.ts +async function main() { + + if (!walletPrivateKey || !bundlerApiKey || !bidHash) { + console.error('Please set the user_modular_wallet_address, walletPrivateKey, bundlerApiKey, and bidHash environment variables.'); + return; + } + + // search bid and get solver address + const bidSearchResult: BidSearchResult[] = await searchBid(bidHash); + + if (!bidSearchResult || bidSearchResult.length === 0) { + console.error(`No bid found for hash: ${bidHash}`); + return; + } + + const bidDetails = bidSearchResult[0]; + + if (!bidDetails) { + console.error(`No bid found for hash: ${bidHash}`); + return; + } + + const solverAddress = bidDetails.solverAddress; + + if (!solverAddress) { + console.error(`No solver address found for bid hash: ${bidHash}`); + return; + } + + const intentSearchResultResponse : IntentSearchResult[] = await searchIntent(bidDetails.intentHash); + + if (!intentSearchResultResponse || intentSearchResultResponse.length === 0) { + console.error(`No intent found for hash: ${bidDetails.intentHash}`); + return; + } + + const intentSearchResult: IntentSearchResult = intentSearchResultResponse[0]; + + if (!intentSearchResult || !intentSearchResult.userIntent) { + console.error(`No intent found for hash: ${bidDetails.intentHash}`); + return; + } + + const modularWalletAddress = intentSearchResult.userIntent.core.permittedAccounts[0].account; + const chainId = intentSearchResult.userIntent.core.permittedAccounts[0].chainId; + + if (!modularWalletAddress) { + console.error(`No modular wallet address found for intent hash: ${bidDetails.intentHash}`); + return; + } + + const resourceLockInfoResponse: ResourceLockInfoResponse = await getResourceLockInfo(bidHash); + + if (!resourceLockInfoResponse || !resourceLockInfoResponse.resourceLockInfo) { + console.error(`No resource lock info found for bid hash: ${bidHash}`); + return; + } + console.log(`Resource Lock Info: ${JSON.stringify(resourceLockInfoResponse, bigintReplacer, 2)}`); + + const sessionKeyAddress = resourceLockInfoResponse.resourceLockInfo.sessionKey; + + if(resourceLockInfoResponse.resourceLockInfo.validUntil < Date.now() / 1000) { + console.error(`SessionKey: ${sessionKeyAddress} expired in Resource lock info for bid hash: ${bidHash}`); + return; + } + + if (!sessionKeyAddress) { + console.error(`No session key address found for bid hash: ${bidHash}`); + return; + } + + // get session key private key from AWS Secrets Manager + const sessionPrivateKey = await getSecretFromBidHashAndSessionKey(bidHash, sessionKeyAddress); + + if (!sessionPrivateKey) { + console.error(`No session private key found for session key address: ${sessionKeyAddress}`); + return; + } + + // get the session-details from api-call + const sessionKeyDetails: SessionDetails = await getSessionDetailsByWalletAddressAndSessionKey( + chainId, + modularWalletAddress, + sessionKeyAddress + ); + + if (!sessionKeyDetails || !sessionKeyDetails.lockedTokens || sessionKeyDetails.lockedTokens.length === 0) { + console.error(`No locked tokens found for session key address: ${sessionKeyAddress}`); + return; + } + + const tokenAddress = sessionKeyDetails.lockedTokens[0].token; + const lockedAmount = sessionKeyDetails.lockedTokens[0].locked_amount; + const claimedAmount = sessionKeyDetails.lockedTokens[0].claimed_amount; + + if (!tokenAddress || !lockedAmount || !claimedAmount) { + console.error(`Invalid locked token details for session key address: ${sessionKeyAddress}`); + return; + } + + if (claimedAmount == lockedAmount || lockedAmount == '0') { + console.error(`You have already claimed: ${claimedAmount} locked tokens`); + return; + } + + // prepare UserOp to transfer the locked ERC20 tokens from the user's ModularWallet to the solver address + const modularSdk = new ModularSdk( + walletPrivateKey, + { + chainId: chainId, + bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey), + index: 0, + accountAddress: modularWalletAddress + }) + + // get transferFrom encoded data + const transactionData = encodeFunctionData({ + functionName: 'transfer', + abi: parseAbi(ERC20_ABI), + args: [solverAddress, lockedAmount] + }); + + // clear the transaction batch + await modularSdk.clearUserOpsFromBatch(); + + // add transactions to the batch + const userOpsBatch = await modularSdk.addUserOpsToBatch({ to: tokenAddress, data: transactionData }); + console.log('transactions: ', userOpsBatch); + + const credibleAccountModuleAddress = Networks[chainId].contracts.credibleAccountModule; + + console.log(`credibleAccountModuleAddress ${credibleAccountModuleAddress} as BigNumber is: ${BigNumber.from(credibleAccountModuleAddress)}`); + + // estimate transactions added to the batch and get the fee data for the UserOp + const op = await modularSdk.estimate({ + key: BigNumber.from(credibleAccountModuleAddress) + }); + + const nonceBig = BigNumber.from(op.nonce); + console.log(`Nonce: ${nonceBig}`); + + console.log(`Estimate UserOp: ${await printOp(op)}`); + + const userOpHash: string = await modularSdk.getUserOpHash(op); + console.log(`UserOpHash: ${userOpHash}`); + + // sign the UserOp using sessionPrivateKey (with viem) + const sessionAccount = privateKeyToAccount(sessionPrivateKey as Hex); + const signature = await sessionAccount.signMessage({ message: { raw: userOpHash as Hex } }); + + // set the signedUserOpHash in the UserOp + op.signature = signature; + + // sending to the bundler with isUserOpAlreadySigned true... + const uoHash = await modularSdk.send(op, true); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while ((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/examples/resource-lock/get-resourcelock-info.ts b/examples/resource-lock/get-resourcelock-info.ts new file mode 100644 index 00000000..2b6eff40 --- /dev/null +++ b/examples/resource-lock/get-resourcelock-info.ts @@ -0,0 +1,50 @@ +import axios from 'axios'; + +export interface TokenData { + token: string; + amount: number; +} + +export interface ResourceLock { + chainId: number; + tokenData: TokenData[]; + proof: string[]; + userOpHash: string; + transactionHash: string; +} + +export interface ResourceLockInfo { + intentHash: string; + bidHash: string; + rootHash: string; + smartWallet: string; + sessionKey: string; + validAfter: number; + validUntil: number; + resourceLocks: ResourceLock[]; + signature: string; + account: string; +} + +export interface ResourceLockInfoResponse { + intentHash: string; + bidHash: string; + resourceLockInfo: ResourceLockInfo; + resourceLockStatus: string; + createdAt: string; + createdAtEpoch: number; + updatedAt: string; + updatedAtEpoch: number; +} + +export async function getResourceLockInfo(bidHash: string): Promise { + const url = `https://pulse-dss-qa.etherspot.io/pulse/resource-lock-info/${bidHash}`; + const res = await axios.get(url); + return res.data; +} + +// tsx examples/resource-lock/get-resourcelock-info.ts +// (async () => { +// const info = await getResourceLockInfo('0xf9385007459e20a6f37805309a73a1eaae824e06ec136ae3b95452229a1ce27f'); +// console.log(JSON.stringify(info, null, 2)); +// })(); \ No newline at end of file diff --git a/examples/resource-lock/get-session-key-details.ts b/examples/resource-lock/get-session-key-details.ts new file mode 100644 index 00000000..bd94ac83 --- /dev/null +++ b/examples/resource-lock/get-session-key-details.ts @@ -0,0 +1,59 @@ +import axios from 'axios'; + +export type SessionData = { + sessionKey: string; + validAfter: number; + validUntil: number; + live: boolean; +}; + +export type LockedToken = { + token: string; + locked_amount: string; + claimed_amount: string; +}; + +export type SessionDetails = { + sessionData: SessionData; + lockedTokens: LockedToken[]; +}; + +export async function getAllSessionKeysOfWallet( + chainId: number | string, + walletAddress: string +): Promise { + const url = `https://pulse-dss-qa.etherspot.io/pulse/credible-account/session-details-wallet/chainId/${chainId}/walletAddress/${walletAddress}`; + const res = await axios.get(url); + return res.data; +} + +export async function getSessionDetailsByWalletAddressAndSessionKey( + chainId: number | string, + walletAddress: string, + sessionKey: string +): Promise { + console.log(`Fetching session details for wallet: ${walletAddress}, chainId: ${chainId}, sessionKey: ${sessionKey}`); + const allSessionKeyDetails = await getAllSessionKeysOfWallet(chainId, walletAddress); + + if (!allSessionKeyDetails || allSessionKeyDetails.length === 0) { + console.error(`No session keys found for wallet: ${walletAddress} on chain: ${chainId}`); + } + + const sessionDetails = allSessionKeyDetails.find( + (details) => details.sessionData.sessionKey === sessionKey + ); + + return sessionDetails as SessionDetails; +} + +export const bigintReplacer = (key: string, value: any) => { + return typeof value === 'bigint' ? value.toString() : value; +} + +// tsx examples/resource-lock/get-session-key-details.ts +// (async () => { +// const chainId = 10; +// const walletAddress = '0x23F04522a2ec5a8b188C48c18edAE54005b537a3'; +// const details = await getAllSessionKeysOfWallet(chainId, walletAddress); +// console.log(`details for wallet ${walletAddress} on chain ${chainId} are: ${JSON.stringify(details, bigintReplacer, 2)}`); +// })(); \ No newline at end of file diff --git a/examples/resource-lock/get-session-key-pair.ts b/examples/resource-lock/get-session-key-pair.ts new file mode 100644 index 00000000..5d1597a8 --- /dev/null +++ b/examples/resource-lock/get-session-key-pair.ts @@ -0,0 +1,35 @@ +import * as dotenv from 'dotenv'; +dotenv.config(); + +import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; + +const client = new SecretsManagerClient({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID!, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, + } +}); + +export async function getSecretFromBidHashAndSessionKey(bidHash: string, sessionKey: string) { + const secretName = `${sessionKey}-${bidHash}`; + console.log(`Fetching secret for: ${secretName}`); + if (!secretName) { + throw new Error("Secret name is required"); + } + const command = new GetSecretValueCommand({ SecretId: secretName }); + const response = await client.send(command); + if (response.SecretString) { + console.log(`Secret found for: ${secretName} as: ${response.SecretString}`); + return `0x${response.SecretString}`; + } + throw new Error("Secret not found"); +} + +// tsx examples/resource-lock/get-session-key-pair.ts +// (async () => { +// const bidHash = '0x7931e1aca917a4673d583cdc599bcfe6ff19496922aa75a11499e726884b9aec'; +// const sessionKey = '0xd0Df09FD15E6a4b2940A22a684EEC2F377E36a8C'; +// const secret = await getSecretFromBidHashAndSessionKey(bidHash, sessionKey); +// console.log(secret); +// })(); \ No newline at end of file diff --git a/examples/resource-lock/search-bid.ts b/examples/resource-lock/search-bid.ts new file mode 100644 index 00000000..8efdea20 --- /dev/null +++ b/examples/resource-lock/search-bid.ts @@ -0,0 +1,68 @@ +import axios from 'axios'; +import { bigintReplacer } from './get-session-key-details'; + +// Types for the bid search API response + +export interface BidPath { + tokenConsumed: string; + amountConsumed: string | number; + tokenReceived: string; + amountReceived: string | number; + action: string; +} + +export interface BidSolution { + to: string; + callData: string; + value: string | number; + path: BidPath; +} + +export interface BidStep { + sequenceNumber: number; + chainId: number; + validUntil: number; + solution: BidSolution; +} + +export interface BidDetails { + bidHash: string; + solverAddress: string; + intentHash: string; + steps: BidStep[]; +} + +export interface ExecutedTransaction { + chainId: number; + transactionHash: string; +} + +export interface BidSearchResult { + bidHash: string; + solverAddress: string; + intentHash: string; + bid: BidDetails; + bidStatus: string; + createdAt: string; + createdAtEpoch: number; + updatedAt: string; + updatedAtEpoch: number; + executedAtEpoch: number; + message: string; + executedTransactions: ExecutedTransaction[]; +} + +export async function searchBid(bidHash: string): Promise { + const url = `https://pulse-dss-qa.etherspot.io/pulse/bids/search?bidHash=${bidHash}`; + const res = await axios.get(url); + if (res.status !== 200) { + throw new Error(`Failed to fetch bid details for hash: ${bidHash}`); + } + return res.data; +} + +// tsx examples/resource-lock/search-bid.ts +// (async () => { +// const results = await searchBid('0xf9385007459e20a6f37805309a73a1eaae824e06ec136ae3b95452229a1ce27f'); +// console.log(JSON.stringify(results, bigintReplacer, 2)); +// })(); \ No newline at end of file diff --git a/examples/resource-lock/search-intent.ts b/examples/resource-lock/search-intent.ts new file mode 100644 index 00000000..386fa7a0 --- /dev/null +++ b/examples/resource-lock/search-intent.ts @@ -0,0 +1,62 @@ +import axios from 'axios'; + +// Types for the intent search API response + +export interface PermittedAccount { + account: string; + chainId: number; +} + +export interface DesiredAsset { + asset: string; + value: number | string; + chainId: number; +} + +export interface DispensableAsset { + asset: string; + maxValue: number | string; + chainId: number; +} + +export interface IntentCore { + permittedAccounts: PermittedAccount[]; +} + +export interface IntentConstraints { + permittedChains: number[]; + deadline: number; + maxGas: number; + slippagePercentage: number; + desiredAssets: DesiredAsset[]; + dispensableAssets: DispensableAsset[]; +} + +export interface UserIntent { + intentHash: string; + core: IntentCore; + constraints: IntentConstraints; +} + +export interface IntentSearchResult { + intentHash: string; + intentStatus: string; + userIntent: UserIntent; + account: string; + createdAt: string; + createdAtEpoch: number; + updatedAt: string; + updatedAtEpoch: number; +} + +export async function searchIntent(intentHash: string): Promise { + const url = `https://pulse-dss-qa.etherspot.io/pulse/intents/search?intent_hash=${intentHash}`; + const res = await axios.get(url); + return res.data; +} + +// tsx examples/resource-lock/search-intent.ts +// (async () => { +// const results = await searchIntent('0xddaea938a9293e91b2556ac656cf30b5cb5cf980748d783db24bf019a1ab4668'); +// console.log(JSON.stringify(results, null, 2)); +// })(); \ No newline at end of file diff --git a/package.json b/package.json index 099a50b7..e58a2aba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@etherspot/modular-sdk", - "version": "6.1.0", + "version": "7.0.0", "description": "Etherspot Modular SDK - build with ERC-7579 smart accounts modules", "keywords": [ "ether", @@ -63,9 +63,10 @@ "url": "https://github.com/etherspot/etherspot-modular-sdk/issues" }, "dependencies": { - "@lifi/sdk": "2.5.0", + "@aws-sdk/client-secrets-manager": "^3.835.0", "@thehubbleproject/bls": "0.5.1", "@walletconnect/universal-provider": "2.10.0", + "axios": "^1.10.0", "biome": "0.3.3", "class-transformer": "0.5.1", "class-validator": "0.14.1", @@ -90,7 +91,7 @@ "async-mutex": "0.5.0", "buffer": "6.0.3", "bun-types": "1.0.7", - "dotenv": "16.0.3", + "dotenv": "^16.0.3", "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-plugin-prettier": "4.2.1", diff --git a/src/sdk/base/BaseAccountAPI.ts b/src/sdk/base/BaseAccountAPI.ts index a09d534f..6db143e1 100644 --- a/src/sdk/base/BaseAccountAPI.ts +++ b/src/sdk/base/BaseAccountAPI.ts @@ -57,6 +57,9 @@ export abstract class BaseAccountAPI { factoryAddress?: string; validatorAddress?: string; hookMultiplexerAddress?: string; + credibleAccountModuleAddress?: string; + resourceLockValidatorAddress: string; + wallet: WalletProviderLike; publicClient: PublicClient; @@ -98,6 +101,8 @@ export abstract class BaseAccountAPI { this.publicClient = params.publicClient; this.validatorAddress = params.optionsLike?.multipleOwnerECDSAValidatorAddress ?? Networks[params.optionsLike.chainId]?.contracts?.multipleOwnerECDSAValidator ?? DEFAULT_MULTIPLE_OWNER_ECDSA_VALIDATOR_ADDRESS; this.hookMultiplexerAddress = Networks[params.optionsLike.chainId]?.contracts?.hookMultiPlexer || AddressZero; + this.credibleAccountModuleAddress = Networks[params.optionsLike.chainId]?.contracts?.credibleAccountModule || AddressZero; + this.resourceLockValidatorAddress = Networks[params.optionsLike.chainId]?.contracts?.resourceLockValidator || AddressZero; } get error$(): ErrorSubject { diff --git a/src/sdk/base/Bootstrap.ts b/src/sdk/base/Bootstrap.ts index 5e43c1da..7b9c67c6 100644 --- a/src/sdk/base/Bootstrap.ts +++ b/src/sdk/base/Bootstrap.ts @@ -37,3 +37,35 @@ export function makeBootstrapConfig(module: string, data: string): BootstrapConf config.push(newConfig); return config; } + +export function makeBootstrapConfigForModules(modules: string[], moduleInitDataItems: string[]): BootstrapConfig[] { + const config: BootstrapConfig[] = []; + + for (const [index, moduleEntry] of modules.entries()) { + + if (!moduleEntry) { + throw new Error(`Module name is not provided for index ${index}.`); + } + + const data = moduleInitDataItems[index]; + + if (!data) { + throw new Error(`Module init data for module ${moduleEntry} is not provided.`); + } + + const encodedFunctionData = encodeFunctionData({ + functionName: 'onInstall', + abi: parseAbi(modulesAbi), + args: [data], + }); + + const newConfig: BootstrapConfig = { + module: moduleEntry, + data: encodedFunctionData, + }; + + config.push(newConfig); + } + + return config; +} \ No newline at end of file diff --git a/src/sdk/base/EtherspotWalletAPI.ts b/src/sdk/base/EtherspotWalletAPI.ts index 2b59f2cd..762c8783 100644 --- a/src/sdk/base/EtherspotWalletAPI.ts +++ b/src/sdk/base/EtherspotWalletAPI.ts @@ -6,7 +6,8 @@ import { getViemAddress } from '../common/utils/viem-utils.js'; import { DEFAULT_BOOTSTRAP_ADDRESS, DEFAULT_QUERY_PAGE_SIZE, Networks } from '../network/constants.js'; import { BigNumber, BigNumberish } from '../types/bignumber.js'; import { BaseAccountAPI, BaseApiParams } from './BaseAccountAPI.js'; -import { BootstrapConfig, _makeBootstrapConfig, makeBootstrapConfig } from './Bootstrap.js'; +import { BootstrapConfig, _makeBootstrapConfig, makeBootstrapConfig, makeBootstrapConfigForModules } from './Bootstrap.js'; +import { getHookMultiPlexerInitData } from '../common/getInitData.js'; // Creating a constant for the sentinel address using viem const SENTINEL_ADDRESS = getAddress("0x0000000000000000000000000000000000000001"); @@ -36,6 +37,12 @@ export type FallbackInfo = { handlerAddress: string; }; + +export interface SigHookInit { + sig: string; + subHooks: Hex[]; +} + /** * An implementation of the BaseAccountAPI using the EtherspotWallet contract. * - contract deployer gets "entrypoint", "owner" addresses and "index" nonce @@ -232,9 +239,29 @@ export class EtherspotWalletAPI extends BaseAccountAPI { if (!this.validatorAddress) { throw new Error('Validator address not found'); } - const validators: BootstrapConfig[] = makeBootstrapConfig(this.validatorAddress, '0x'); + + const validators: BootstrapConfig[] = makeBootstrapConfigForModules( + [ + this.validatorAddress, + this.credibleAccountModuleAddress as Hex, + this.resourceLockValidatorAddress as Hex + ] , + [ + '0x', + encodeAbiParameters( + parseAbiParameters('uint256'), + [BigInt(MODULE_TYPE.VALIDATOR)] + ), + encodeAbiParameters([{ type: 'address' }], [this.services.walletService.EOAAddress as Hex]), + ] + ); + const executors: BootstrapConfig[] = makeBootstrapConfig(ADDRESS_ZERO, '0x'); - const hook: BootstrapConfig = _makeBootstrapConfig(ADDRESS_ZERO, '0x'); + + //Get HookMultiPlexer init data with CredibleAccountHook as global subhook + let hmpInitData = this.credibleAccountModuleAddress == ADDRESS_ZERO ? '0x' : getHookMultiPlexerInitData([this.credibleAccountModuleAddress as Hex]); + const hook: BootstrapConfig = _makeBootstrapConfig(this.hookMultiplexerAddress as Hex, hmpInitData); + const fallbacks: BootstrapConfig[] = makeBootstrapConfig(ADDRESS_ZERO, '0x'); const initMSAData = encodeFunctionData({ diff --git a/src/sdk/common/getInitData.ts b/src/sdk/common/getInitData.ts index 811a3d5f..b4770228 100644 --- a/src/sdk/common/getInitData.ts +++ b/src/sdk/common/getInitData.ts @@ -4,7 +4,8 @@ import { decodeFunctionData, parseAbi, slice, -} from 'viem' + encodeAbiParameters +} from 'viem'; import { InitialModules, Module } from './types.js' import { bootstrapAbi, factoryAbi } from './abis.js' @@ -46,3 +47,38 @@ export const getInitData = ({ fallbacks: initCallDataArgs[3] as Module[], } } + +export interface SigHookInit { + sig: string; + subHooks: Hex[]; +} + +export function getHookMultiPlexerInitData( + globalHooks: Hex[] = [], + valueHooks: Hex[] = [], + delegatecallHooks: Hex[] = [], + sigHooks: SigHookInit[] = [], + targetSigHooks: SigHookInit[] = [], +): Hex { + const abiType = [ + { type: 'address[]' }, + { type: 'address[]' }, + { type: 'address[]' }, + { + type: 'tuple[]', + components: [{ type: 'bytes4' }, { type: 'address[]' }], + }, + { + type: 'tuple[]', + components: [{ type: 'bytes4' }, { type: 'address[]' }], + }, + ]; + const encodedData = encodeAbiParameters(abiType, [ + globalHooks, + valueHooks, + delegatecallHooks, + sigHooks, + targetSigHooks + ] as any); + return encodedData; +} diff --git a/src/sdk/network/constants.ts b/src/sdk/network/constants.ts index 8577b01a..fb20d5b0 100644 --- a/src/sdk/network/constants.ts +++ b/src/sdk/network/constants.ts @@ -98,10 +98,12 @@ export const Networks: { contracts: { entryPoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', walletFactory: '0x38CC0EDdD3a944CA17981e0A19470d2298B8d43a', - bootstrap: '0xCF2808eA7d131d96E5C73Eb0eCD8Dc84D33905C7', + bootstrap: '0x2229B2C3D00a213D93151cd65C31e5b4ea4D0330', multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xfd090eAFdb3dccE1FA9517ce52BaeBAd1d8cE939', + resourceLockValidator: "0xa3789284adB928258DA2cC674090AC5c69D22183", }, }, [11155111]: { @@ -111,10 +113,12 @@ export const Networks: { contracts: { entryPoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', walletFactory: '0x38CC0EDdD3a944CA17981e0A19470d2298B8d43a', - bootstrap: '0xCF2808eA7d131d96E5C73Eb0eCD8Dc84D33905C7', + bootstrap: '0xFD109F06162B76d6A7752B853F4e825Df2cC9cBA', multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '0x22A55192a663591586241D42E603221eac49ed09', - hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + hookMultiPlexer: '0xe629A99Fe2fAD23B1dF6Aa680BA6995cfDA885a3', + credibleAccountModule: '0xc34D2E2D9Fa0aDbCd801F13563A1423858751A12', + resourceLockValidator: '0x08B42e03c1beC06caa3811F503EBF2D58CaccE94' }, }, [10]: { @@ -129,6 +133,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xddFAE48a3219b3037dDBd5ee108a283F2d4DBDcd', + resourceLockValidator: '0xa3789284adB928258DA2cC674090AC5c69D22183' }, }, [137]: { @@ -142,6 +148,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xddFAE48a3219b3037dDBd5ee108a283F2d4DBDcd', + resourceLockValidator: '0xa3789284adB928258DA2cC674090AC5c69D22183' }, }, [42161]: { @@ -155,6 +163,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xddFAE48a3219b3037dDBd5ee108a283F2d4DBDcd', + resourceLockValidator: '0xa3789284adB928258DA2cC674090AC5c69D22183' }, }, [1]: { @@ -168,6 +178,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [10200]: { @@ -181,6 +193,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [122]: { @@ -194,6 +208,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [123]: { @@ -208,6 +224,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [100]: { @@ -221,6 +239,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xb8970dB5cd5617411577a8eCE93b16CCf244e7A4', + resourceLockValidator: '0x7948Ad29e179716793B3F404F91a9d8aa0275712' }, }, [2357]: { @@ -234,6 +254,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [30]: { @@ -247,6 +269,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [31]: { @@ -260,6 +284,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [5000]: { @@ -273,6 +299,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [5003]: { @@ -286,6 +314,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [43114]: { @@ -299,6 +329,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [8453]: { @@ -312,6 +344,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xddFAE48a3219b3037dDBd5ee108a283F2d4DBDcd', + resourceLockValidator: '0xa3789284adB928258DA2cC674090AC5c69D22183' }, }, [56]: { @@ -325,6 +359,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '0xb8970dB5cd5617411577a8eCE93b16CCf244e7A4', + resourceLockValidator: '0x7948Ad29e179716793B3F404F91a9d8aa0275712' }, }, [97]: { @@ -338,6 +374,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [43113]: { @@ -351,6 +389,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [59144]: { @@ -364,6 +404,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [59140]: { @@ -377,6 +419,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [114]: { @@ -390,6 +434,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [14]: { @@ -403,6 +449,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [534351]: { @@ -416,6 +464,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [534352]: { @@ -429,6 +479,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [11155420]: { @@ -442,6 +494,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [28122024]: { @@ -455,6 +509,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [888888888]: { @@ -468,6 +524,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [80002]: { @@ -481,6 +539,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '0x22A55192a663591586241D42E603221eac49ed09', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [421614]: { @@ -494,6 +554,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [51]: { @@ -507,6 +569,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [50]: { @@ -520,6 +584,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [44787]: { @@ -533,6 +599,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [42220]: { @@ -546,6 +614,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, [79479957]: { @@ -572,6 +642,8 @@ export const Networks: { multipleOwnerECDSAValidator: '0x0eA25BF9F313344d422B513e1af679484338518E', erc20SessionKeyValidator: '', hookMultiPlexer: '0xDcA918dd23456d321282DF9507F6C09A50522136', + credibleAccountModule: '', + resourceLockValidator: '' }, }, }; diff --git a/src/sdk/network/interfaces.ts b/src/sdk/network/interfaces.ts index 68eabf42..3c116ef5 100644 --- a/src/sdk/network/interfaces.ts +++ b/src/sdk/network/interfaces.ts @@ -17,5 +17,7 @@ export interface NetworkConfig { multipleOwnerECDSAValidator: string; erc20SessionKeyValidator: string; hookMultiPlexer: string; + credibleAccountModule: string; + resourceLockValidator: string; }; }; diff --git a/src/sdk/sdk.ts b/src/sdk/sdk.ts index 159a01a2..acb1445a 100644 --- a/src/sdk/sdk.ts +++ b/src/sdk/sdk.ts @@ -21,6 +21,7 @@ import { ErrorHandler } from './errorHandler/errorHandler.service.js'; import { EtherspotBundler } from './bundler/index.js'; import { Account, formatEther, Hex, http, type PublicClient } from 'viem'; import { BigNumber, BigNumberish } from './types/bignumber.js'; +import { printOp } from './common/OperationUtils.js'; /** * Modular-Sdk @@ -209,6 +210,9 @@ export class ModularSdk { partialtx.factory = this.etherspotWallet.factoryAddress; } + console.log('Partial User Operation:', partialtx); + console.log(`User Operation: ${await printOp(partialtx)}`); + if (!paymasterDetails?.url) { const bundlerGasEstimate = await this.bundler.getVerificationGasInfo(partialtx);