INDID Core SDK
Software development kit that facilitates the interaction with INDID infrastructure.
init
A method for obtaining an initialized instance of the sdk. Throws an error if the apiKey is not valid.
const clientUser = await Client.init(
config: IClientConfig
);The rpcUrl is required if user operations are needed, if only delegated transactions are needed neither a provider or a chainId are required. If neither a provider or a chainId are provided the sdk will be initialized in read-only mode and the chainId will have to be provided in every delegated transaction function that requires it. It's possible to pass optional parameters to the init method, such as the entry point, the override bundler rpc url, the override backend url and the log level. The log level defaults to NONE, which means no logs will be printed.
Interfaces
interface IClientConfig {
apiKey: string;
rpcUrl?: string;
chainId?: BigNumberish;
overrideBundlerRpc?: string;
overrideBackendUrl?: string;
overrideEntryPoint?: string;
logLevel?: LogLevel;
}enum LogLevel {
NONE,
DEBUG,
INFO,
WARNING,
ERROR,
}Parameters
Takes the following parameters in the config object:
apiKey: The API key for authentication with INDID servicesrpcUrl(optional): The RPC URL for connecting to the blockchain. Required if user operations are neededchainId(optional): The blockchain network identifieroverrideBundlerRpc(optional): Custom URL to override the default bundler RPCoverrideBackendUrl(optional): Custom URL to override the default backendoverrideEntryPoint(optional): Custom address to override the default entry pointlogLevel(optional): Controls the verbosity of logs. Defaults toNONE
Returns
Returns an initialized Client instance that can be used to interact with the INDID infrastructure.
Example Usage
// Initialize with minimal configuration
const clientUser = await Client.init({
apiKey: "your-api-key-here",
});
// Initialize with custom RPC
const clientWithCustomRPC = await Client.init({
apiKey: "your-api-key-here",
rpcUrl: "https://polygon-rpc.com",
});
// Initialize with debugging enabled
const clientWithLogging = await Client.init({
apiKey: "your-api-key-here",
rpcUrl: "https://polygon-rpc.com",
logLevel: LogLevel.DEBUG,
});connectAccount
A method for connecting to a smart contract account with the signer of the owner, this is necessary for preparing and signing user operations. The field opts is only needed when the values set at project creation should be overridden or if the provider/chainId was not provided at initialization.
clientUser.connectAccount(
signer: IndidSigner,
accountAddress: string,
opts?: IConnectAccountOpts
): Promise<IConnectAccountResponse>;Interfaces
interface IConnectAccountOpts {
moduleType: string;
moduleAddress: string;
storageType: string;
factoryAddress: string;
accountVersion: string;
moduleVersion: string;
chainId?: BigNumberish;
}interface IConnectAccountResponse {
error?: string;
}Parameters
Takes the following parameters:
signer: AnIndidSignerobject representing the signer of the account owneraccountAddress: The address of the smart contract wallet to connect toopts(optional): Configuration options to override default project settings:moduleType: The type of module used by the accountmoduleAddress: The address of the module contractstorageType: The type of storage used by the accountfactoryAddress: The address of the account factory contractaccountVersion: The version of the account implementationmoduleVersion: The version of the module implementationchainId: The blockchain network identifier (required if not provided during initialization)
Returns
Returns a promise that resolves to an IConnectAccountResponse.
Example Usage
// Create a signer using a private key
const privateKey = "0x123...";
const wallet = new ethers.Wallet(privateKey);
const signer = IndidSigner.fromSecp256k1(wallet.privateKey);
// Connect to an existing account with default settings
const response = await clientUser.connectAccount(
signer,
"0x456..." // accountAddress
);
// Connect to an account with custom options
const responseWithOpts = await clientUser.connectAccount(
signer,
"0x456...", // accountAddress
{
moduleType: "enterprise",
moduleAddress: "0x789...",
storageType: "shared",
factoryAddress: "0xabc...",
accountVersion: "1",
moduleVersion: "1",
chainId: 137,
}
);
if (!response.error) {
console.log("Successfully connected to account");
} else {
console.error(`Connection failed: ${response.error}`);
}getCounterfactualAddress
A method for getting the address of a yet to be deployed smart contract wallet. The salt defaults to 0 and should be changed only if the same owner wants to deploy multiple smart contract wallets. The field opts is only needed when the values set at project creation should be overridden.
Returns the address inside an IGetCounterfactualAddressResponse
const response = await clientUser.getCounterfactualAddress(
owners: IndidAddress[],
salt?: string,
opts?: ICreateAccountOpts
);Interfaces
interface ICreateAccountOpts {
storageType: "standard" | "shared";
moduleType: "user" | "enterprise";
factoryAddress: string;
moduleAddress: string;
guardians: IndidAddress[];
beaconId?: BytesLike;
}interface IGetCounterfactualAddressResponse {
accountAddress: string;
error?: string;
}Parameters
Takes the following parameters:
owners: An array ofIndidAddressobjects representing the account ownerssalt(optional): A unique value to ensure address uniqueness. Defaults to "0"opts(optional): Configuration options to override default project settings:storageType: The type of storage to use for the accountmoduleType: The type of module to use for the accountfactoryAddress: The address of the account factory contractmoduleAddress: The address of the module contractguardians: An array of guardian addresses for the accountbeaconId: Optional beacon ID for upgradeable contracts
Returns
Returns an object containing:
accountAddress: The counterfactual address of the smart contract walleterror: Optional error message if the request fails
Example Usage
// Prepare the account parameters
const ownerPrivateKey = "0x123...";
const wallet = new ethers.Wallet(ownerPrivateKey);
const ownerAddress = IndidAddress.fromSecp256k1(wallet.address);
// Get counterfactual address with default settings
const response = await clientUser.getCounterfactualAddress([ownerAddress]);
console.log(`Counterfactual address: ${response.accountAddress}`);
// Get another counterfactual address for the same owner
const responseWithSalt = await clientUser.getCounterfactualAddress(
[ownerAddress],
"1" // Salt
);getInitCode
A method for getting the init code for a new smart contract wallet. The field opts is only needed when the values set at project creation should be overridden.
Returns the init code inside an IInitCodeResponse
const response = await clientUser.getInitCode(
owners: IndidAddress[],
salt?: string,
opts?: ICreateAccountOpts
);Interfaces
interface ICreateAccountOpts {
storageType: "standard" | "shared";
moduleType: "user" | "enterprise";
factoryAddress: string;
moduleAddress: string;
guardians: IndidAddress[];
beaconId?: BytesLike;
}interface IInitCodeResponse {
initCode: string;
error?: string;
}Parameters
Takes the following parameters:
owners: An array ofIndidAddressobjects representing the account ownerssalt(optional): A unique value to ensure deployment address uniqueness. Defaults to "0"opts(optional): Configuration options to override default project settings:storageType: The type of storage to use for the accountmoduleType: The type of module to use for the accountfactoryAddress: The address of the account factory contractmoduleAddress: The address of the module contractguardians: An array of guardian addresses for the accountbeaconId: Optional beacon ID for shared storage accounts
Returns
Returns an object containing:
initCode: The initialization code for the smart contract walleterror: Optional error message if the request fails
Example Usage
// Prepare the account parameters
const ownerPrivateKey = "0x123...";
const wallet = new ethers.Wallet(ownerPrivateKey);
const ownerAddress = IndidAddress.fromSecp256k1(wallet.address);
// Get init code with default settings
const response = await clientUser.getInitCode([ownerAddress]);
console.log(`Init code: ${response.initCode}`);
// Use init code in a user operation
const builder = await clientUser.prepareSendETH(
"0x789...", // recipient
ethers.utils.parseEther("0.1"),
{
initCode: response.initCode, // This will deploy the wallet as part of the user operation
}
);
// Get init code with custom options
const responseWithOpts = await clientUser.getInitCode(
[ownerAddress],
"1", // Salt
{
storageType: "standard",
moduleType: "user",
factoryAddress: "0x456...",
moduleAddress: "0x789...",
guardians: [],
}
);getAccountNonce
A method for getting the deployed smart contract wallet sequential nonce. If a smart contract wallect has been connected the accountAddress field is not needed. Otherwise the returned nonce is the sequential nonce of the specified accountAddress. This method is useful if the user wants to enforce a specific order of user operations, for example, if the user wants to send a user operation that depends on the result of another user operation. To do so the user can override the nonce inside the prepareSendTransaction method with the nonce returned by this method.
Returns a BigNumber with the account nonce inside an IGetNonceResponse.
const response = await clientUser.getAccountNonce(accountAddress?: string);Interfaces
interface IGetNonceResponse {
nonce: BigNumberish;
error?: string;
}Parameters
Takes the following parameters:
accountAddress(optional): The address of the smart contract wallet. If not provided, uses the address of the currently connected account
Returns
Returns an object containing:
nonce: The sequential nonce value as a BigNumberisherror: Optional error message if the request fails
Example Usage
// Get nonce of the connected account
const response = await clientUser.getAccountNonce();
console.log(`Current nonce: ${response.nonce}`);
// Get nonce of a specific account
const specificAccountResponse = await clientUser.getAccountNonce("0x123...");
console.log(`Account nonce: ${specificAccountResponse.nonce}`);
// Use nonce to enforce operation order
const builder = await clientUser.prepareSendETH(
"0x789...", // recipient
ethers.utils.parseEther("0.1"),
{
nonceOP: response.nonce.add(1), // Use next sequential nonce
}
);getNonSequentialAccountNonce
A method for getting a non sequantial nonce of the deployed smart contract wallet. If a smart contract wallect has been connected the accountAddress field is not needed. Otherwise the returned nonce is the nonce of the specified accountAddress. This is the method called internally by the prepareSendTransaction method.
Returns a BigNumber with the account nonce inside an IGetNonceResponse.
const response = await clientUser.getNonSequentialAccountNonce(accountAddress?: string);Interfaces
interface IGetNonceResponse {
nonce: BigNumberish;
error?: string;
}Parameters
Takes the following parameters:
accountAddress(optional): The address of the smart contract wallet. If not provided, uses the address of the currently connected account
Returns
Returns an object containing:
nonce: The non-sequential nonce value as a BigNumberisherror: Optional error message if the request fails
Example Usage
// Get non-sequential nonce of the connected account
const response = await clientUser.getNonSequentialAccountNonce();
console.log(`Non-sequential nonce: ${response.nonce}`);
// Get non-sequential nonce of a specific account
const specificAccountResponse = await clientUser.getNonSequentialAccountNonce(
"0x123..."
);
console.log(`Account non-sequential nonce: ${specificAccountResponse.nonce}`);
// Use in a transaction preparation
const builder = await clientUser.prepareSendETH(
"0x789...", // recipient
ethers.utils.parseEther("0.1"),
{
nonceOP: response.nonce, // Use the non-sequential nonce
}
);connectProvider
Connects to a blockchain provider using the provided RPC URL. Sets up the entryPoint contract and retrieves the chainId.
Throws an error if the connection to the provider fails.
await clientUser.connectProvider(rpcUrl: string);Example Usage
await clientUser.connectProvider("https://polygon-rpc.com");prepareSendTransaction
A method for preparing a partial user operation that executes the specified transactions. It's partial because it still needs to be sponsored (optionally) and signed. It can be used to send multiple transactions in a single operation, the array positions of the parameters must match. The nonceOP field is optional and can be used to override the nonce of the smart contract account. The initCode is optional and can be used to deploy a new smart contract wallet. The deadlineSeconds field is optional and can be used to specify the deadline for the operation, if the operation is not included in a block before the deadline the operation will fail. The default is 60 minutes. The doNotRevertOnTxFailure field is optional and can be used to specify if the operation should revert if one of the transactions fails. The default is true.
Internally the method will try to estimate the gas limit of every transaction, if the estimation fails the prepare will fail with an ethers gas estimation error. The gas limit can be overridden by passing the callGasLimit field in the opts parameter, this will also skip the gas estimation. Note: if the gas estimation fails the transaction will probably fail on chain too. All the other gas related fields are optional and can be used to override the default values.
Returns a builder containing the partial user operation.
const builder = await clientUser.prepareSendTransaction(
transactions: ICall[],
opts?: IUserOperationOptions
);Interfaces
interface ICall {
to: string;
value: BigNumberish;
data: BytesLike;
}interface IUserOperationOptions {
initCode?: string;
nonceOP?: BigNumberish;
doNotRevertOnTxFailure?: boolean;
deadlineSeconds?: number;
callGasLimit?: BigNumberish;
verificationGasLimit?: BigNumberish;
preVerificationGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
}Parameters
Takes the following parameters:
transactions: An array ofICallobjects, each containing:to: The recipient address of the transactionvalue: The amount of native currency to send (in wei)data: The calldata of the contract call
opts(optional): Configuration options for the user operation:initCode(optional): Initialization code for deploying a new walletnonceOP(optional): Custom nonce to override automatic nonce selectiondoNotRevertOnTxFailure(optional): If true, operation continues even if a transaction failsdeadlineSeconds(optional): Time in seconds after which the operation becomes invalid (defaults to 60 minutes)callGasLimit(optional): Gas limit for the execution transactionverificationGasLimit(optional): Gas limit for verificationpreVerificationGas(optional): Gas for pre-verification stepsmaxFeePerGas(optional): Maximum total fee per gas unitmaxPriorityFeePerGas(optional): Maximum priority fee per gas unit
Returns
Returns an IUserOperationBuilder containing the partial user operation that can be further processed (signed, sponsored, etc.).
Example Usage
// Define transactions to execute
const transactions = [
{
to: "0x123...", // ETH transfer
value: ethers.utils.parseEther("0.1"),
data: "0x", // empty calldata for simple transfers
},
{
to: "0x456...", // ERC20 transfer
value: 0,
data: "0xa9059cbb000000000000000000000000789...0000000000000000000000000000000000000000000000000de0b6b3a7640000", // transfer(address,uint256)
},
];
// Prepare transaction with default options
const builder = await clientUser.prepareSendTransaction(transactions);
// Prepare transaction with custom options
const builderWithOpts = await clientUser.prepareSendTransaction(transactions, {
deadlineSeconds: 3600, // 1 hour
doNotRevertOnTxFailure: true,
maxFeePerGas: ethers.utils.parseUnits("50", "gwei"),
});
// Prepare transaction for a new wallet deployment
const initCodeResponse = await clientUser.getInitCode([ownerAddress]);
const deployAndTransferBuilder = await clientUser.prepareSendTransaction(
transactions,
{
initCode: response.initCode, // Will deploy the wallet as part of this operation
}
);
// After preparation, the builder can be signed and sent
const signedOp = await clientUser.signUserOperation(builder);
const response = await clientUser.sendUserOperation(builder);prepareSendETH
A method for preparing a partial user operation that sends the desired amount of native currency to the recipient. The nonceOP field is optional and can be used to override the nonce of the smart contract account. The initCode is optional and can be used to deploy a new smart contract wallet. It's partial because it still needs to be sponsored (optionally) and signed.
Returns a builder containing the partial user operation.
const builder = await clientUser.prepareSendETH(
recipientAddress: string,
amount: BigNumberish,
opts?: IUserOperationOptions
);Interfaces
interface IUserOperationOptions {
initCode?: string;
nonceOP?: BigNumberish;
doNotRevertOnTxFailure?: boolean;
deadlineSeconds?: number;
callGasLimit?: BigNumberish;
verificationGasLimit?: BigNumberish;
preVerificationGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
}Parameters
Takes the following parameters:
recipientAddress: The address that will receive the native currencyamount: The amount of native currency to send (in wei)opts(optional): Configuration options for the user operation:initCode(optional): Initialization code for deploying a new walletnonceOP(optional): Custom nonce to override automatic nonce selectiondoNotRevertOnTxFailure(optional): If true, operation continues even if the transaction failsdeadlineSeconds(optional): Time in seconds after which the operation becomes invalid (defaults to 60 minutes)callGasLimit(optional): Gas limit for the execution transactionverificationGasLimit(optional): Gas limit for verificationpreVerificationGas(optional): Gas for pre-verification stepsmaxFeePerGas(optional): Maximum total fee per gas unitmaxPriorityFeePerGas(optional): Maximum priority fee per gas unit
Returns
Returns an IUserOperationBuilder containing the partial user operation that can be further processed (signed, sponsored, etc.).
Example Usage
// Simple ETH transfer
const builder = await clientUser.prepareSendETH(
"0x123...", // recipient address
ethers.utils.parseEther("0.1") // 0.1 ETH in wei
);
// ETH transfer with custom options
const builderWithOpts = await clientUser.prepareSendETH(
"0x123...", // recipient address
ethers.utils.parseEther("0.1"), // 0.1 ETH in wei
{
deadlineSeconds: 1800, // 30 minutes
maxFeePerGas: ethers.utils.parseUnits("30", "gwei"),
}
);
// Deploy a new wallet and send ETH in one operation
const initCodeResponse = await clientUser.getInitCode([ownerAddress]);
const deployAndTransferBuilder = await clientUser.prepareSendETH(
"0x123...", // recipient address
ethers.utils.parseEther("0.1"), // 0.1 ETH in wei
{
initCode: initCodeResponse.initCode, // Will deploy the wallet as part of this operation
}
);
// After preparation, the builder can be signed and sent
const signedOp = await clientUser.signUserOperation(builder);
const response = await clientUser.sendUserOperation(builder);prepareSendERC20
A method for preparing a partial user operation that sends the desired amount of the specified ERC20 to the recipient. The nonceOP field is optional and can be used to override the nonce of the smart contract account. The initCode is optional and can be used to deploy a new smart contract wallet. It's partial because it still needs to be sponsored (optionally) and signed.
Returns a builder containing the partial user operation.
const builder = await clientUser.prepareSendERC20(
contractAddress: string,
recipientAddress: string,
amount: BigNumberish,
opts?: IUserOperationOptions
);Interfaces
interface IUserOperationOptions {
initCode?: string;
nonceOP?: BigNumberish;
doNotRevertOnTxFailure?: boolean;
deadlineSeconds?: number;
callGasLimit?: BigNumberish;
verificationGasLimit?: BigNumberish;
preVerificationGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
}Parameters
Takes the following parameters:
contractAddress: The address of the ERC20 token contractrecipientAddress: The address that will receive the tokensamount: The amount of tokens to send (in the token's smallest unit)opts(optional): Configuration options for the user operation:initCode(optional): Initialization code for deploying a new walletnonceOP(optional): Custom nonce to override automatic nonce selectiondoNotRevertOnTxFailure(optional): If true, operation continues even if the transaction failsdeadlineSeconds(optional): Time in seconds after which the operation becomes invalid (defaults to 60 minutes)callGasLimit(optional): Gas limit for the execution transactionverificationGasLimit(optional): Gas limit for verificationpreVerificationGas(optional): Gas for pre-verification stepsmaxFeePerGas(optional): Maximum total fee per gas unitmaxPriorityFeePerGas(optional): Maximum priority fee per gas unit
Returns
Returns an IUserOperationBuilder containing the partial user operation that can be further processed (signed, sponsored, etc.).
Example Usage
// Simple ERC20 token transfer
const builder = await clientUser.prepareSendERC20(
"0xabc...", // ERC20 token contract address
"0x123...", // recipient address
ethers.utils.parseUnits("100", 18) // 100 tokens (assuming 18 decimals)
);
// ERC20 transfer with custom options
const builderWithOpts = await clientUser.prepareSendERC20(
"0xabc...", // ERC20 token contract address
"0x123...", // recipient address
ethers.utils.parseUnits("100", 18), // 100 tokens (assuming 18 decimals)
{
deadlineSeconds: 1800, // 30 minutes
maxFeePerGas: ethers.utils.parseUnits("30", "gwei"),
}
);
// Deploy a new wallet and send tokens in one operation
const initCodeResponse = await clientUser.getInitCode([ownerAddress]);
const deployAndTransferBuilder = await clientUser.prepareSendERC20(
"0xabc...", // ERC20 token contract address
"0x123...", // recipient address
ethers.utils.parseUnits("100", 18), // 100 tokens (assuming 18 decimals)
{
initCode: initCodeResponse.initCode, // Will deploy the wallet as part of this operation
}
);
// After preparation, the builder can be signed and sent
const signedOp = await clientUser.signUserOperation(builder);
const response = await clientUser.sendUserOperation(builder);prepareSendModuleOperation
A method for preparing a partial user operation that executes specific wallet functions implemented by the main module. The nonceOP field is optional and can be used to override the nonce of the smart contract account. The initCode is optional and can be used to deploy a new smart contract wallet. It's partial because it still needs to be sponsored (optionally) and signed. The initCode is optional and can be used to deploy a new smart contract wallet.
Returns a builder containing the partial user operation.
const builder = await clientUser.prepareSendModuleOperation(
calldata: string,
nonce: string,
deadline: number,
signatures: string,
opts?: IUserOperationOptions
);Interfaces
interface IUserOperationOptions {
initCode?: string;
nonceOP?: BigNumberish;
doNotRevertOnTxFailure?: boolean;
deadlineSeconds?: number;
callGasLimit?: BigNumberish;
verificationGasLimit?: BigNumberish;
preVerificationGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
}Parameters
Takes the following parameters:
calldata: The encoded function call data for the module operationnonce: The module-specific nonce (different from the account nonce)deadline: The expiration seconds for the module operationsignatures: The signatures of the wallet owners and/or guardians authenticating the module operationopts(optional): Configuration options for the user operation:initCode(optional): Initialization code for deploying a new walletnonceOP(optional): Custom nonce to override automatic nonce selectiondoNotRevertOnTxFailure(optional): If true, operation continues even if the transaction failsdeadlineSeconds(optional): Time in seconds after which the operation becomes invalid (defaults to 60 minutes)callGasLimit(optional): Gas limit for the execution transactionverificationGasLimit(optional): Gas limit for verificationpreVerificationGas(optional): Gas for pre-verification stepsmaxFeePerGas(optional): Maximum total fee per gas unitmaxPriorityFeePerGas(optional): Maximum priority fee per gas unit
Returns
Returns an IUserOperationBuilder containing the partial user operation that can be further processed (signed, sponsored, etc.).
Example Usage
// Encode the module function call (example: adding a new guardian)
const functionSelector = "0xabcdef12"; // Function selector for addGuardian(address)
const encodedAddress = ethers.utils.defaultAbiCoder
.encode(
["address"],
["0x123..."] // New guardian address
)
.slice(2); // Remove '0x' prefix
const calldata = functionSelector + encodedAddress;
// Module-specific parameters
const moduleNonce = "1"; // Nonce from the module
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
const message = ethers.utils.solidityKeccak256(
["bytes", "uint256", "uint256"],
[calldata, moduleNonce, deadline]
);
const messageHash = ethers.utils.arrayify(message);
// Sign the module operation
const signature = await signer.signMessage(messageHash);
// Prepare the module operation
const builder = await clientUser.prepareSendModuleOperation(
calldata,
moduleNonce,
deadline,
signature
);
// Prepare with custom options
const builderWithOpts = await clientUser.prepareSendModuleOperation(
calldata,
moduleNonce,
deadline,
signature,
{
callGasLimit: 500000,
maxFeePerGas: ethers.utils.parseUnits("30", "gwei"),
}
);
// After preparation, the builder can be signed and sent
const signedOp = await clientUser.signUserOperation(builder);
const response = await clientUser.sendUserOperation(builder);prepareEnterpriseRecoveryOperation
A method for preparing a partial user operation that executes a recovery operation. The nonceOP field is optional and can be used to override the nonce of the smart contract account. The initCode is optional and can be used to deploy a new smart contract wallet. It's partial because it still needs to be sponsored (optionally) and signed. The connected smart contract wallet should be owned by the enterprise guardian.
Returns a builder containing the partial user operation.
const builder = await clientUser.prepareEnterpriseRecoveryOperation(
accountAddress: string,
newOwner: string,
opts?: IUserOperationOptions
);Interfaces
interface IUserOperationOptions {
initCode?: string;
nonceOP?: BigNumberish;
doNotRevertOnTxFailure?: boolean;
deadlineSeconds?: number;
callGasLimit?: BigNumberish;
verificationGasLimit?: BigNumberish;
preVerificationGas?: BigNumberish;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
}Parameters
Takes the following parameters:
accountAddress: The address of the smart contract wallet to recovernewOwner: The address of the new owner for the walletopts(optional): Configuration options for the user operation:initCode(optional): Initialization code for deploying a new walletnonceOP(optional): Custom nonce to override automatic nonce selectiondoNotRevertOnTxFailure(optional): If true, operation continues even if the transaction failsdeadlineSeconds(optional): Time in seconds after which the operation becomes invalid (defaults to 60 minutes)callGasLimit(optional): Gas limit for the execution transactionverificationGasLimit(optional): Gas limit for verificationpreVerificationGas(optional): Gas for pre-verification stepsmaxFeePerGas(optional): Maximum total fee per gas unitmaxPriorityFeePerGas(optional): Maximum priority fee per gas unit
Returns
Returns an IUserOperationBuilder containing the partial user operation that can be further processed (signed, sponsored, etc.).
Example Usage
// The account must be connected using the enterprise guardian's signer
const privateKey = "0x123..."; // Enterprise guardian's private key
const wallet = new ethers.Wallet(privateKey);
const guardianSigner = IndidSigner.fromSecp256k1(
wallet.privateKey,
SignerKind.Guardian
);
// Connect to the wallet as the guardian
await clientUser.connectAccount(
guardianSigner,
"0x456..." // Account address
);
// Prepare the recovery operation
const builder = await clientUser.prepareEnterpriseRecoveryOperation(
"0x456...", // Account to recover
"0x789..." // New owner address
);
// Prepare with custom options
const builderWithOpts = await clientUser.prepareEnterpriseRecoveryOperation(
"0x456...", // Account to recover
"0x789...", // New owner address
{
callGasLimit: 500000,
maxFeePerGas: ethers.utils.parseUnits("30", "gwei"),
}
);
// After preparation, the builder can be signed and sent
const signedOp = await clientUser.signUserOperation(builder);
const response = await clientUser.sendUserOperation(builder);
// Wait for the operation to complete
const receipt = await clientUser.waitOP(response.userOpHash);
console.log(`User Op status: ${receipt.status}`);fillUserOperation
Fills a user operation with necessary data based on the provided calldata. Sets sender, calldata, gas parameters, and other fields required for a valid operation.
Throws an error if the provider is not connected.
async fillUserOperation(
callData: string,
opts?: IUserOperationOptions
): Promise<IUserOperationBuilder>Example Usage
const builder = await clientUser.fillUserOperation(calldata, {
callGasLimit: 500000,
});signUserOperation
A method for signing user operations, it applies the signature on the builder object itself. Return the userOpHash that has been signed, the signature and an optional error string inside an ISignUserOperationResponse.
const response = await clientUser.signUserOperation(
builder: IUserOperationBuilder
);Interfaces
interface ISignUserOperationResponse {
userOpHash: string;
signature: string;
error?: string;
}Parameters
Takes the following parameters:
builder: AnIUserOperationBuilderobject containing the user operation to be signed
Returns
Returns an object containing:
userOpHash: The hash of the user operation that was signedsignature: The signature applied to the user operationerror: Optional error message if the signing fails
Example Usage
// First prepare a user operation
const transactions = [
{
to: "0x123...",
value: ethers.utils.parseEther("0.1"),
data: "0x",
},
];
const builder = await clientUser.prepareSendTransaction(transactions);
// Sign the user operation
const response = await clientUser.signUserOperation(builder);
console.log(`UserOp hash: ${response.userOpHash}`);
console.log(`Signature: ${response.signature}`);
// The builder now has the signature applied to it
// It can be sent immediately after signing
const sendResponse = await clientUser.sendUserOperation(builder);
// Alternatively, you can get just the userOpHash without signing
const hashResponse = await clientUser.getUserOperationHash(builder);
console.log(`UserOp hash (for external signing): ${hashResponse.userOpHash}`);Note that the signature is automatically applied to the builder object. The signed operation can be sent immediately after signing without any additional steps.
buildUserOperation
Builds a complete user operation from a builder object. Finalizes all fields and prepares the operation for submission.
async buildUserOperation(builder: IUserOperationBuilder) {
return builder.buildOp(await this.entryPoint.getAddress(), this.chainId);
}Example Usage
const userOp = await clientUser.buildUserOperation(builder);sendUserOperation
A method for directing a builder instance to create a User Operation and send it to INDID bundler. The webhookData is optional and can be used to specify a webhook to be called upon the operation success or failure. Returns the User Operation Hash and a Task Id inside an ISendUserOpResponse.
const response = await clientUser.sendUserOperation(
builder: IUserOperationBuilder,
webhookData?: IWebHookRequest
);Interfaces
interface ISendUserOpResponse {
userOpHash: string;
taskId: string;
error?: string;
}interface IWebHookRequest {
tag: string;
metadata?: Record<string, unknown>;
}Parameters
Takes the following parameters:
builder: AnIUserOperationBuilderobject containing the user operation to be sentwebhookData(optional): Configuration for webhook notifications about the operation execution:tag: Mandatory field that identifies the specific webhook endpoint to callmetadata: Optional additional data to include in the webhook callback
Returns
Returns an object containing:
userOpHash: The hash of the user operation that was submittedtaskId: A unique identifier for tracking the operation executionerror: Optional error message if the submission fails
Example Usage
// First prepare and sign a user operation
const transactions = [
{
to: "0x123...",
value: ethers.utils.parseEther("0.1"),
data: "0x",
},
];
const builder = await clientUser.prepareSendTransaction(transactions);
await clientUser.signUserOperation(builder);
// Send the user operation without webhook
const response = await clientUser.sendUserOperation(builder);
console.log(`UserOp hash: ${response.userOpHash}`);
console.log(`Task ID: ${response.taskId}`);
// Wait for the operation to complete
const receipt = await clientUser.waitOP(response.userOpHash);
// Send with webhook notification
const webhookResponse = await clientUser.sendUserOperation(builder, {
tag: "transfer-complete",
metadata: {
transferId: "123456",
amount: "0.1",
currency: "ETH",
},
});
// When using webhooks, the backend will call your webhook endpoint
// when the operation completes, with the metadata you providedsendUserOperationBundler
Sends a user operation directly to the bundler and optionally waits for its inclusion. Provides both dry-run capability and actual submission with monitoring.
async sendUserOperationBundler(
builder: IUserOperationBuilder,
timeoutMs: number = 100000,
waitIntervalMs: number = 5000,
opts?: ISendUserOperationOpts
)Example Usage
const { userOpHash, wait } = await clientUser.sendUserOperationBundler(
builder,
120000,
5000
);
const receipt = await wait();getUserOperationHash
A method for getting the User Operation Hash from a builder instance. This can be useful for signing the operation in a different environment.
Returns the User Operation Hash inside an IGetUserOperationHashResponse, this needs to be arrayfied before being signed.
const response = await clientUser.getUserOperationHash(builder);Interfaces
interface IGetUserOperationHashResponse {
userOpHash: string;
error?: string;
}Parameters
Takes the following parameters:
builder: AnIUserOperationBuilderobject containing the user operation to get the hash for
Returns
Returns an object containing:
userOpHash: The hash of the user operationerror: Optional error message if the operation fails
Example Usage
// First prepare a user operation
const transactions = [
{
to: "0x123...",
value: ethers.utils.parseEther("0.1"),
data: "0x",
},
];
const builder = await clientUser.prepareSendTransaction(transactions);
// Get the hash for external signing
const response = await clientUser.getUserOperationHash(builder);
console.log(`UserOp hash: ${response.userOpHash}`);
// Convert to array format for signing in external wallets
const hashForSigning = ethers.utils.arrayify(response.userOpHash);
// Example of external signing (using ethers wallet)
const externalWallet = new ethers.Wallet("0x123..."); // Private key
const signature = await externalWallet.signMessage(hashForSigning);
// You can now use this signature in your application
console.log(`External signature: ${signature}`);waitOP
A method for waiting an User Operation Hash returned by sendUserOperation, returns the receipt inside an IUserOperationReceiptResponse upon success.
const response = await clientUser.waitOP(
userOpHash: string,
timeoutMs?: number);Interfaces
interface IUserOperationReceiptResponse {
receipt: IUserOperationReceipt;
error?: string;
}interface IUserOperationReceipt {
blockHash: string;
logsBloom: string;
contractAddress: string;
transactionIndex: number;
transactionHash: string;
gasUsed: BigNumberish;
blockNumber: BigNumberish;
cumulativeGasUsed: BigNumberish;
from: string;
blockTimestamp: string;
to: string;
logs: [{}];
status: number;
error?: string;
}Parameters
Takes the following parameters:
userOpHash: The hash of the user operation to wait fortimeoutMs(optional): Maximum time to wait in milliseconds before returning a timeout error
Returns
Returns an object containing:
receipt: An object with details about the executed user operation, including:blockHash: The hash of the block in which the operation was includedlogsBloom: Bloom filter for the logscontractAddress: The address of the contract that executed the operationtransactionIndex: The index of the transaction in the blocktransactionHash: The hash of the transactiongasUsed: The amount of gas used by the transactionblockNumber: The number of the block that includes the transactioncumulativeGasUsed: The total gas used in the block up to and including this transactionfrom: The sender addressblockTimestamp: The timestamp of the blockto: The recipient addresslogs: Array of log objects emitted during the transactionstatus: Status code (1 for success, 0 for failure)error: Optional error message if the transaction failed
error: Optional error message if the waiting operation fails
Example Usage
// First send a user operation
const transactions = [
{
to: "0x123...",
value: ethers.utils.parseEther("0.1"),
data: "0x",
},
];
const builder = await clientUser.prepareSendTransaction(transactions);
await clientUser.signUserOperation(builder);
const sendResponse = await clientUser.sendUserOperation(builder);
// Wait for the operation to complete with a 2-minute timeout
const response = await clientUser.waitOP(
sendResponse.userOpHash,
120000 // 2 minutes
);
// Check if the operation was successful
if (response.receipt.status === 1) {
console.log("Transaction successful!");
console.log(`Block number: ${response.receipt.blockNumber}`);
console.log(`Transaction hash: ${response.receipt.transactionHash}`);
console.log(`Gas used: ${response.receipt.gasUsed}`);
} else {
console.error(
`Transaction failed: ${response.receipt.error || "Unknown error"}`
);
}
// You can also inspect logs emitted during the transaction
const logs = response.receipt.logs;
for (const log of logs) {
console.log(`Log from address: ${log.address}`);
console.log(`Data: ${log.data}`);
}waitTask
A method for waiting a backend task ID returned by some sdk functions. sendUserOperation, returns the task outcome inside an IWaitTaskResponse and an optional reason with information about the task outcome.
const response = await clientUser.waitTask(
taskId: string,
timeoutMs?: number
);Interfaces
interface IWaitTaskResponse {
operationStatus: TaskUserOperationStatus;
receipt?: JSON;
reason?: string;
}enum TaskUserOperationStatus {
NOT_FOUND = "NOT_FOUND",
PENDING = "PENDING",
EXECUTED = "EXECUTED",
REVERTED = "REVERTED",
UNHANDLED = "UNHANDLED",
FAILED = "FAILED",
TIMEOUT = "TIMEOUT",
}Parameters
Takes the following parameters:
taskId: The ID of the task to wait fortimeoutMs(optional): Maximum time to wait in milliseconds before returning a timeout error
Returns
Returns an object containing:
operationStatus: The status of the task, which can be one of:NOT_FOUND: The task was not foundPENDING: The task is still in progressEXECUTED: The task completed successfullyREVERTED: The task was reverted before executionUNHANDLED: The task encountered an unhandled errorFAILED: The task failed to executeTIMEOUT: The task timed out while waiting
receipt: An optional JSON object containing the transaction receipt if availablereason: An optional string providing more information about the task outcome
Example Usage
// First send a user operation or other operation that returns a task ID
const transactions = [
{
to: "0x123...",
value: ethers.utils.parseEther("0.1"),
data: "0x",
},
];
const builder = await clientUser.prepareSendTransaction(transactions);
await clientUser.signUserOperation(builder);
const sendResponse = await clientUser.sendUserOperation(builder);
// Wait for the task to complete with a 2-minute timeout
const response = await clientUser.waitTask(
sendResponse.taskId,
120000 // 2 minutes
);
// Check the status of the task
switch (response.operationStatus) {
case TaskUserOperationStatus.EXECUTED:
console.log("Task completed successfully!");
console.log("Receipt:", response.receipt);
break;
case TaskUserOperationStatus.REVERTED:
console.error("Task reverted:", response.reason);
break;
case TaskUserOperationStatus.FAILED:
console.error("Task failed:", response.reason);
break;
case TaskUserOperationStatus.TIMEOUT:
console.error("Task timed out - check status later");
break;
default:
console.log(`Task status: ${response.operationStatus}`);
if (response.reason) {
console.log(`Additional info: ${response.reason}`);
}
}verifyWebhookSignature
A static method for verifying the signature of a webhook callback, it takes an IWebHookSignatureRequest and an optional verifyingKey. If the verifyingKey is not provided the default key is used. Returns a boolean, true if the signature is valid, false otherwise.
const response = Client.verifyWebhookSignature(
req: IWebHookSignatureRequest,
verifyingKey?: string
);Interfaces
interface IWebHookSignatureRequest {
headers: {
signature: string;
encodedMessage: string;
};
body: Record<string, unknown>;
}Parameters
Takes the following parameters:
req: An object containing the webhook request data:headers: Contains the signature headers:signature: The digital signature to verifyencodedMessage: The encoded message that was signed
body: The body of the webhook request as an object
verifyingKey(optional): The public key to use for verification. If not provided, the default key is used
Returns
Returns a boolean value:
true: If the signature is validfalse: If the signature is invalid
Example Usage
//TODO: fix this example with correct code
// Example webhook handler in an Express.js server
app.post("/webhook", (req, res) => {
// Extract the necessary headers
const webhookRequest = {
headers: {
signature: req.headers["x-indid-signature"],
encodedMessage: req.headers["x-indid-encoded-message"],
},
body: req.body,
};
// Verify the signature
const isValid = Client.verifyWebhookSignature(webhookRequest);
if (isValid) {
// Signature is valid, process the webhook
console.log("Webhook signature verified!");
// Extract information from the webhook
const { userOpHash, status, metadata } = req.body;
if (status === "executed") {
console.log(`Operation ${userOpHash} executed successfully`);
// Handle successful operation
} else if (status === "reverted") {
console.log(`Operation ${userOpHash} reverted`);
// Handle reverted operation
}
// Return success response
res.status(200).json({ success: true });
} else {
// Invalid signature, reject the webhook
console.error("Invalid webhook signature");
res.status(401).json({ error: "Invalid signature" });
}
});
// Using a custom verification key
const customKey = "0x123..."; // Your custom verification key
const isValidCustom = Client.verifyWebhookSignature(webhookRequest, customKey);prepareDelegatedTransactions
A method for preparing a delegated transaction without sending it. This function creates the necessary data for a delegated transaction that can be sent later with an admin sdk. The account signer is used to sign the transaction data using EIP-712.
If chainId wasn't provided during initialization, it must be provided in the options. The deadlineSeconds field is optional and can be used to specify the expiration time for the operation (defaults to 60 minutes). The doNotRevertOnTxFailure flag can be used to specify if the operation should continue even if one of the transactions fails.
Returns an object implementing ISendDelegatedTransactionsRequest to be sent with sendDelegatedTransactions function of admin sdk.
const response = await clientUser.prepareDelegatedTransactions(
transactions: ICall[],
opts?: IDelegatedTransactionOptions
): Promise<ISendDelegatedTransactionsRequest>;Interfaces
interface ICall {
to: string;
value: BigNumberish;
data: BytesLike;
}interface IDelegatedTransactionOptions {
chainId?: BigNumberish;
doNotRevertOnTxFailure?: boolean;
deadlineSeconds?: number;
webhookData?: IWebHookRequest;
}interface ISendDelegatedTransactionsRequest {
accountAddress: string;
chainId: BigNumberish;
moduleAddress: string;
data: BytesLike;
nonce: BigNumberish;
deadline: number;
sigs: BytesLike;
webhookData?: IWebHookRequest;
}Parameters
Takes the following parameters:
transactions: An array ofICallobjects, each containing:to: The recipient address of the transactionvalue: The amount of native currency to send (in wei)data: The calldata to include in the transaction
opts(optional): Configuration options for the delegated transaction:chainId(optional): The blockchain network identifier (required if not provided during initialization)doNotRevertOnTxFailure(optional): If true, operation continues even if a transaction failsdeadlineSeconds(optional): Time in seconds after which the operation becomes invalid (defaults to 60 minutes)webhookData(optional): Configuration for webhook notifications
Returns
Returns a promise that resolves to an ISendDelegatedTransactionsRequest object containing all the necessary data for submitting the delegated transaction through an admin SDK, including:
accountAddress: The address of the smart contract walletchainId: The blockchain network identifiermoduleAddress: The address of the module handling the transactiondata: The encoded transaction datanonce: The transaction noncedeadline: The expiration timestampsigs: The signatures authorizing the transactionwebhookData(optional): Webhook configuration
Example Usage
// Define transactions to execute
const transactions = [
{
to: "0x123...", // ETH transfer
value: ethers.utils.parseEther("0.1"),
data: "0x", // empty calldata for simple transfers
},
{
to: "0x456...", // ERC20 transfer
value: 0,
data: "0xa9059cbb000000000000000000000000789...0000000000000000000000000000000000000000000000000de0b6b3a7640000", // transfer(address,uint256)
},
];
// Prepare the delegated transaction
const preparedTx = await clientUser.prepareDelegatedTransactions(transactions, {
chainId: 137, // Polygon network
deadlineSeconds: 3600, // 1 hour
webhookData: {
tag: "delegated-transfer",
metadata: {
purpose: "example-transaction",
},
},
});
// The prepared transaction can now be sent using an admin SDK
console.log("Prepared transaction data:");
console.log(`Account: ${preparedTx.accountAddress}`);
console.log(`Chain ID: ${preparedTx.chainId}`);
console.log(`Module: ${preparedTx.moduleAddress}`);
console.log(`Deadline: ${new Date(preparedTx.deadline * 1000).toISOString()}`);
// Send the prepared transaction using an admin SDK (on the server side)
// const response = await adminClient.sendPreparedDelegatedTransactions(preparedTx);prepareContractDeploymentTransactions
A method for preparing deterministic contract deployment transactions and calculating their expected on-chain addresses.
const result = await clientUser.prepareContractDeploymentTransactions(
params: Array<{
bytecode: string;
salt?: string;
}>
);Interfaces
interface IContractDeploymentParams {
bytecode: string;
salt?: string;
}interface IContractDeploymentResult {
expectedAddresses: string[];
deployTxs: ICall[];
}interface ICall {
to: string;
value: BigNumberish;
data: BytesLike;
}Parameters
Takes the following parameters:
params: An array of deployment parameters, where each item contains:bytecode: The compiled contract bytecode to deploysalt(optional): A unique value to ensure deployment address uniqueness. Defaults to "0" if not provided
Returns
Returns an object containing:
expectedAddresses: Array of calculated contract addresses that will result from the deploymentsdeployTxs: Array of transaction objects (ICall) ready to be used with eitherprepareSendTransactionorprepareDelegatedTransactions
Example Usage
// Prepare contract deployment transactions
const deploymentResult = await clientUser.prepareContractDeploymentTransactions(
[
{
bytecode: "0x608060405234801561001057600080fd5b50...",
salt: "0x123",
},
{
bytecode: "0x608060405234801561001057600080fd5b50...",
// uses default salt
},
]
);
// Get the calculated addresses for reference
const { expectedAddresses, deployTxs } = deploymentResult;
console.log("Expected contract addresses:");
for (let i = 0; i < expectedAddresses.length; i++) {
console.log(`Contract ${i + 1}: ${expectedAddresses[i]}`);
}
// Deploy the contracts using user operations
const builder = await clientUser.prepareSendTransaction(deployTxs);
await clientUser.signUserOperation(builder);
const response = await clientUser.sendUserOperation(builder);
// Wait for the operation to complete
const receipt = await clientUser.waitOP(response.userOpHash);
console.log(`Deployment transaction status: ${receipt.receipt.status}`);
// Or deploy using delegated transactions
const preparedTx = await clientUser.prepareDelegatedTransactions(deployTxs);
// Then send with admin SDK
// const response = await adminClient.sendPreparedDelegatedTransactions(preparedTx);Note: If the provider has not been connected, this method will throw an error.