Skip to content

Modules

Modules are designed to extend the functionality of smart contract accounts in a consistent and predictable manner. This guide explains how modules work and how to interact with them in the SDK.

Modules

General Module Interaction Pattern

Interacting with a module typically follows these steps:

Create or Find a Module

Either create a module with custom functionality yourself, or use one of the available modules from our documentation

Install the Module

Install the module on top of the users Smart Account.

Extend the Smart Account client with module actions

You will need to extend the account client with the functions to interact with the module. The extension pattern is used to save on the size of the SDK bundle

Use the functionality provided by the module

Once you've extended the SDK client with the required module functions, you will be able to access those functions on top of the client object and interact with your newly installed module.

Step-by-Step Guide

1. Create the module

Use the appropriate to[ModuleName] function to create a module instance:

const myModule = to[ModuleName]({
  account: nexusClient.account,
  signer: eoaAccount,
  moduleInitArgs: {
    // Module-specific initialization arguments
  }
})

2. Install the module

Install the module on your Nexus client's smart contract account:

const hash = await nexusClient.installModule({
  module: myModule.moduleInitData
});
 
// Wait for the installation to be confirmed
const { success } = await nexusClient.waitForUserOperationReceipt({ hash });

3. Extend the Nexus client

Extend your Nexus client with module-specific actions:

const extendedNexusClient = nexusClient.extend([ModuleName]Actions(myModule));

4. Use the module's functionality

Use the extended Nexus client to interact with the module's features:

const result = await extendedNexusClient.[moduleSpecificAction]({
  // Action-specific parameters
});

Example: Multisig Module

Here's an example of how to use the Smart Sessions module:

// 1. Create the module
const ownableModule = toOwnableValidator({
  account: nexusClient.account,
  signer: account,
  moduleInitArgs: {
    threshold: 1n,
    owners: [publicKey]
  }
})
 
// 2. Install the module
const hash = await nexusClient.installModule({
  module: ownableModule.moduleInitData
})
 
// 3. Extend the Nexus client
const ownableDanClient = nexusClient
  .extend(ownableActions(ownableModule))
 
// 4. Use the module's functionality
const newSignature = await ownableNexusClient.prepareSignatures({
  signatures: ["0x...", "0x..."]
})

Module Extension Pattern

The SDK uses a module extension pattern to maintain efficient bundle sizes and enable tree-shaking. Here's why:

  • Tree-shaking Optimization: If all module actions were included in the base NexusClient, your bundle would include code for every possible module, even ones you don't use.
  • Flexible Integration: Developers can choose which modules to include, reducing bundle size by only importing needed functionality.
  • Type Safety: Each module extension adds its own type definitions, providing proper TypeScript support for module-specific actions.

For example, instead of:

// ❌ This would include ALL module code in your bundle
const nexusClient = createNexusClient({
  // config
  modules: [sessionsModule, securityModule, otherModules...]
});

We use:

// ✅ Only the sessions module code is included
const nexusClient = await createNexusClient({ /* config */ });
const sessionNexusClient = nexusClient.extend(smartSessionCreateActions(sessionsModule));