starknet-modular-account

This project is an implementation of a Modular Account for Starknet. It includes a reference account; a set of extensible/reusable components to build your own tool; modules, including a sessionkey validator; a set of SDKs based on starknet.js to use the account in a Dapp or a signer; tests to demonstrate how to use the account and a documentation.

⚠️ ⚠️ ⚠️ this project is very much a work-in-progress and has not been audited yet ⚠️ ⚠️ ⚠️

Why Develop Another Smart Account?

Starknet has been a place of innovation for Smart Accounts since the very start. Thank to actors like Starkware, Openzeppelin, Argent, Ledger, Cartridge, Braavos or Equilibrium, the user experience has no equivalent. However, implementations are not always compatible and it is hard for teams that need specific features to get it from even one provider or to develop it.

Meanwhile, the Smart Account Ecosystem on Ethereum has grown and a number of ideas have shown up that could be implemented on Starknet. starknet-modular-account is an extensible account that allow developers to write modules for specific use cases. You should see it as an experiment or a wish to get:

  • extensibility: we could add features to Starknet that we can already find with Ethereum Smart Account and Multisig like Safe, Kernel or the Alchemy accounts
  • interoperability: we could develop modules with clean interfaces that would be re-used by widespread accounts
  • security: we could audit components not large account code base
  • ease of use: we could reference a module registry that accounts providers would reference and user understand

Doing it as a community project would help everybody to get those feature now if they want...

License

This project is licensed under the MIT License

Concepts

Table of Contents

  • Accounts and the Modular Account
  • Modules
  • Accounts and the Modular Account

    The starknet modular account is a configurable account that provides standard features and delegates others to some modules of choice. For instance, the account enables users to add and remove modules; it allows to execute a method of a module. On the other hand, the account delegates the validation of transactions to modules. This account/module split simplifies the development of specific features; it provides an account that can easily evolve to match the user requirements and be extended...

    This document presents the different aspects and interfaces of the modular account as well as modules. It introduces how the account works in general and how the modular account is different. This document is useful for developing new modules. It also provides hints about accessing and managing the modular account from an application.

    Account Interface

    Abstract accounts or smart accounts are key components of the Starknet protocol. On most blockchains, the source of a transaction is a public key, i.e. the counterpart of your keys that remain secret. In the case of Starknet, the source of a transaction is always an account address. This leads to an immediate question that is "How do you create an account?". As you can guess, you need to run a transaction create an account. In some cases, you will not even have an account to proceed. If this question is out of the scope of this document, this answer to that question is pretty "smart" and we encourage you to find it.

    So, except for the account creation that relies on a specific syscall, Starknet interacts with the account both before and during the execution of a transaction. How the account behaves is not part of the protocol which leaves a lot of freedom to developers. To work correctly, however, Starknet requires the account to implement the __validate__ and __execute__ entrypoints in an account contract. To say it otherwise, that is the interface that does the account.

    The account interface as described in both SNIP-6 and the comments on this thread. The code below provides the standard definition of a Starknet account! A Starknet account is a Starknet contract that implements the following interface:

    #![allow(unused)]
    fn main() {
    struct Call {
        to: ContractAddress,
        selector: felt252,
        calldata: Array<felt252>
    }
    
    /// @title SRC-6 Standard Account
    trait ISRC6 {
        /// @notice Execute a transaction through the account
        /// @param calls The list of calls to execute
        /// @return The list of each call's serialized return value
        fn __execute__(calls: Array<Call>) -> Array<Span<felt252>>;
    
        /// @notice Assert whether the transaction is valid to be executed
        /// @param calls The list of calls to execute
        /// @return The string 'VALID' represented as felt when is valid
        fn __validate__(calls: Array<Call>) -> felt252;
    
        /// @notice Assert whether a given signature for a given hash is valid
        /// @param hash The hash of the data
        /// @param signature The signature to validate
        /// @return The string 'VALID' represented as felt when the signature is valid
        fn is_valid_signature(hash: Array<felt252>, signature: Array<felt252>) -> felt252;
    }
    }

    It is very simple both to understand and to conform to the account development specification. As a result, an account can be develop that does things that you would not expect from a regular chain like:

    • blindly validate all the transactions that are submitted. This scenario that we call the Yasager or Yeasayer is a very interesting to code; at least to learn more about accounts
    • execute something very different from what the transaction requests. For instance we could store a signature that would allow to execute the requested transaction later on a different call and execute nothing

    The Modular Account

    The starknet modular account is an implementation of an account so that you can develop and register a module that are triggered from inside the __validate__ and/or __execute__ entrypoints. The benefit of this approach is that you can develop some code that change the behavior of the account without rewriting a whole account.

    There are 2 types of modules for the starknet modular account:

    • Validator Modules that are triggered when validating a transaction
    • Executor Modules that are triggered when executing a transaction

    Note: Only Validator Modules are implemented for now. Executor Modules are part of the Modular Account roadmap.

    Validator and Core Validator Modules

    A validator module is a class that implements the following interface and can be used by the account to delegate the transaction validation:

    #![allow(unused)]
    fn main() {
    /// @title Validator Module Interface
    trait IValidator {
        fn validate(calls: Array<Call>) -> felt252;
    }
    }

    As you can guess Validator modules "replace" the __validate__ entrypoint of the account:

    Note: __validate__ is a reserved entrypoint name for the account and the cairo compiler treat them differently. That is why the module entrypoint are named validate and not __validate__ but that should have been the case.

    A Validator is a Core Validator if it also implements the ICoreValidator interface:

    #![allow(unused)]
    fn main() {
    /// @title Core Validator Module Interface
    trait ICoreValidator<TState> {
        fn is_valid_signature(self: @TState, hash: Array<felt252>, signature: Array<felt252>) -> felt252;
        fn initialize(ref self: TState, public_key: Array<felt252>);
    }
    }

    The core validator has some enhanced features, including the ability to check a signature from another validator. This can be leverage to support and verify offchain. This is possible with the is_valid_signature function that can be used, not only to check transactions but also signed messages. The initialize function is used to setup the core module configuration at the installation time.

    When installing the modular account, not only the public key of the signer is required like on most account but the Core validator module class hash(*) is also mandatory. So "the" Core validator module is a module, i.e. a class, that contains all the validation logic for the account, i.e. the __validate__ and the is_valid_signature functions.

    For now, the only core validator module available is the stark module; it:

    • computes the transaction into a a pedersen hash
    • validate the transaction signature with the stark curve

    That is why the modular account constructor is the following:

    #![allow(unused)]
    fn main() {
    #[constructor]
    fn constructor(ref self: ContractState, core_validator: felt252, public_key: Array<felt252>) {
        self.account.initializer(core_validator, public_key);
    }
    }

    (*) the core_validator is a felt252 and not a ClassHash type due to some technical constraints on the validation of the account deployment. However the external representation of a ClassHash is the same as the one from a felt252.

    Note: the public_key is an Array so that it can be used to support private keys that are larger than the felt232. In the case of the Stark Curve, we simply put the public key in an array of one

    Validator Modules and Prefix Call

    So if there is a core validator module that is used by default by the modular account, other validator modules can also be used in addition to it. In order to be triggered, a number of conditions must be met:

    • the validator module class must be declared to the network
    • the validator module class has must be added to the account with the module management API as described in Module Management
    • some metadata in the form of a prefix call(**) must be added to the transaction

    Assuming the module is declared in the network and installed in the modular account, adding a prefix will trigger it. But what is a prefix? Let's say a transaction is a set of calls like this:

    [call1, call2, ..., callN]
    

    A prefixCall with the following structure has to be created:

    const prefixCall: Call = {
      contractAddress: accountAddress,
      // selector!("__module_validate__")
      entrypoint: moduleEntrypointSelector,
      calldata: [
        moduleClassHash,
        ...otherValidatorArgs,
      ]
    }
    

    And the transaction that will be requested will actually be the following:

    [prefixCall, call1, call2, ..., callN]
    

    As a matter of fact the prefixCall does not modify the execution of the transaction that will be made of the other calls from 1 to N. Instead, it modifies the behavior of the account __validate__ entrypoint that will check the validation is compliant with the module requirements. In this call, the following values are used:

    • accountAddress is the account address. If you use another address the call will fail
    • moduleEntrypointSelector is the sn_keccak of __module_validate__ so it is a fixed value
    • moduleClassHash that is passed as the first parameter of the calldata is the module class hash taht is being used
    • otherValidatorArgs is an array of felt252 that can be used to pass some parameters to the module so that it can actually validate the transactions. For instance in the case of a Yasager module, it could be that no other parameters are used.

    (**) As discussed in the roadmap, we are exploring some changes so that the validator module does not need to use a prefix call. However the SDKs are masking the complexity associated with generating that call and there is not guarranty for now that this change that will land in the signature will actually work.

    Executor Modules

    The executor module are implemented for now. They will rely on the same usage of a prefix call with a moduleEntrypointSelector that is the sn_keccak of __module_execute__.

    Additional Account Interfaces

    The modular account provides another set of interfaces to interact with modules. The code below shows the interface definition:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IModule<TState> {
        fn __module_validate__(self: @TState, calldata: Array<felt252>);
        fn __module_execute__(self: @TState, calldata: Array<felt252>);
        fn add_module(ref self: TState, class_hash: ClassHash);
        fn remove_module(ref self: TState, class_hash: ClassHash);
        fn update_core_module(ref self: TState, class_hash: ClassHash);
        fn get_core_module(self: @TState) -> ClassHash;
        fn is_module(self: @TState, class_hash: ClassHash) -> bool;
        fn call_on_module(self: @TState, class_hash: ClassHash, call: Call) -> Array<felt252>;
        fn execute_on_module(ref self: TState, class_hash: ClassHash, call: Call) -> Array<felt252>;
    }
    }

    Module Management

    The account provides 2 sets of interfaces to manage the core module and other modules:

    • get_core_module and update_core_module enables to check the current core module and to change it for another module.
    • add_module, remove_module and is_module enables to add, remove and check the account modules that are not the core validator module

    Note: update_core_module is very risky and probably contains flows right now

    Module Configuration

    Module configuration depends on the module and requires you call the module entrypoint from the account. The account provides 2 helper functions that are used by the SDKs to grant those accesses. The functions are:

    • call_on_module a view function that allows interactions with the view functions of a module
    • execute_on_module that can be used to run transactions on the module from
    • the account (and only from it).

    Prefix functions

    __module_validate__ and __module_execute__ are functions that do nothing. They are part of the account so that when you prefix the calls the transaction does not fail because Starknet nodes checks for the existence of an entrypoint as part of the transaction validation process.

    Upgrade and Backward Compatibility

    The modular account implements the openzeppelin Upgradeable interface that is the following:

    #![allow(unused)]
    fn main() {
    fn upgrade(ref self: ContractState, new_class_hash: ClassHash)
    }

    It enables both to upgrade accounts and to move to another version of the account like the one from openzeppelin, argent or braavo. Be Careful moving to another account requires you update the internal state of the account so that it works correctly and upgrading without updating the state will for sure break it in an IRREVERSIBLE way. The experiments/starknet-bootstrap-account provides some the basic principle that can be used to perform those migrations but it needs to be developed and battle-tested. We do not know if the reverse, i.e. moving from another account to the

    Modules

    The project comes with 2 validator modules:

    • The stark validator that can be used as a core validator for the modular account. This relies on a pedersen-based hash of the transaction and the stark curve signature verification primitives to validate transactions.
    • The sessionkey validator that requires an access to the core validator. The sessionkey validator requires an offchain authorisation to be granted by the account signer and allows a 3rd party to run a limited number of transactions with the account.

    Validator Interfaces

    As mentioned earlier, validators must implement the following interface to work properly. How the implementation is done depends on the requirements...

    #![allow(unused)]
    fn main() {
    /// @title Validator Module Interface
    trait IValidator {
        fn validate(calls: Array<Call>) -> felt252;
    }
    }

    In addition, if the validator is a Core validator, it must also implement the following interface:

    #![allow(unused)]
    fn main() {
    /// @title Core Validator Module Interface
    trait ICoreValidator<TState> {
        fn is_valid_signature(self: @TState, hash: Array<felt252>, signature: Array<felt252>) -> felt252;
        fn initialize(ref self: TState, public_key: Array<felt252>);
    }
    }

    Validator Configuration

    In order to allow calls and execution on the account with the entrypoints from the module, the module must also implement the following interface. The call and the execute functions map the input and output respectively between the call_on_module and execute_on_module on the account and the view functions and the external functions of the module on the other side.

    #![allow(unused)]
    fn main() {
    pub trait IConfigure {
        fn call(self: @TState, call: Call) -> Array<felt252>;
        fn execute(ref self: TState, call: Call) -> Array<felt252>;
    }
    }

    As a result, to provide an access to the configuration of a module, not only use must add the required entrypoint in the module but you must also provide the mapping in one of these 2 functions.

    Validator Entrypoints

    The entrypoints associated with each module are specific to the module. They can be whatever is required by the module. Below are the entrypoints:

    • For the stark validator
    #![allow(unused)]
    fn main() {
    pub trait IPublicKeys{
        fn add_public_key(ref self: ContractState, new_public_key: felt252);
        fn get_public_keys(self: @ContractState) -> Array<felt252>;
        fn get_threshold(self: @ContractState) -> u8;
        fn remove_public_key(ref self: ContractState, old_public_key: felt252);
        fn set_threshold(ref self: ContractState, new_threshold: u8);
    }
    }
    • For the Eth validator
    #![allow(unused)]
    fn main() {
    pub trait IPublicKey<TState> {
        fn set_public_key(ref self: TState, new_public_key: EthPublicKey);
        fn get_public_key(self: @TState) -> EthPublicKey;
    }
    }
    • For the sessionkey validator
    #![allow(unused)]
    fn main() {
    pub trait IDisableSessionKey {
        fn disable_session_key(ref self: ContractState, sessionkey: felt252);
        fn is_disabled_session_key(self: @ContractState, sessionkey: felt252);
    }
    }

    Using SDKs

    starknet-modular-account comes with 2 SDKs that leverage starknet.js.

    • @0xknwn/starknet-modular-account provides the SmartrAccount class that extends starknet.js account to support multiple signers and helps to manage modules. It also provides the AccountModuleInterface that should be used by module SDKs.
    • @0xknwn/starknet-module-sessionkey provides the SessionKeyModule that implements the AccountModuleInterface as well as tools to configure the sessionkey module, including the PolicyManager and the PolicyGrantor classes.
    • @0xknwn/starknet-module provides the EthModule that implements the AccountModuleInterface.

    In addition, the project provides another SDK called @0xknwn/starknet-test-helpers that can be used to create helper classes outside of this repository. It is used to demonstrate the 2 main SDKs.

    This section provides a set of tutorials about how to use the account and modules. If you want to understand how modules are working internally, you should check Modules Internals.

    Installing SDKs

    SDKs are provided as NPM packages for an easy use. If you plan to use them, you should have a Javascript or a Typescript project configured,

    Create a typescript project

    If not already done the script below shows how to create a minimalistic typescript project:

    mkdir documentation-examples && cd documentation-examples
    npm init -y
    git init
    npm install typescript --save-dev
    npx tsc --init
    

    Obviously you might want to adapt the script to your requirements and that is out of the scope of this documentation...

    The rest of the documentation assume typescript is used and the javascript file are generated in the dist directory. To change the default settings edit the tsconfig.json and change the following:

    • set the outDir property to be dist
    • change the target to be es2020 or later. That is because we intensively use bigint in the project and it was not supported with es2016

    This is an example of a working tsconfig.json:

    {
      "compilerOptions": {
        "target": "esnext",
        "module": "commonjs",
        "outDir": "./dist/",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
      }
    }
    

    Then create a simple src directory with a index.ts file in it:

    mkdir -p src
    echo 'console.log("success");' > src/index.ts
    

    To transpile typescript in javascript, run:

    npx tsc --build
    

    To run the output, run:

    node dist/index.js
    

    Install the Modular Account SDK

    The modular account SDK is named @0xknwn/starknet-modular-account. To add it to your project, run the command below:

    npm install --save @0xknwn/starknet-modular-account
    
    npm install --save starknet@next
    

    Install the SessionKey Module SDK

    If you plan to use the sessionkey module, you will need the @0xknwn/starknet-module-sessionkey SDK but very likely also the @0xknwn/starknet-modular-account SDK. To install the 2 packages, run the command below:

    npm install --save \
      @0xknwn/starknet-modular-account \
      @0xknwn/starknet-module-sessionkey
    
    npm install --save starknet@next
    

    Deploying the Modular Account

    Declare a the SmartrAccount and StarkValidator classes

    If you are working on a network that does not have the classes already declared, you will need to declare them. The modular account main SDK, aka @0xknwn/starknet-modular-account contains class and a helper function named declareClass to declare the class to the network. To use it, you need to pass:

    • A starknet.js Account as a first parameter
    • The name of the class to declare as the 2nd parameter. They are SmartrAccount for the modular account and StarkValidator for the Stark Core Validator

    Below is an example of a script that declares the 2 classes.

    // file src/01-declare-class.ts
    import { RpcProvider, Account } from "starknet";
    import { declareClass, classNames } from "@0xknwn/starknet-modular-account";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    const ozAccountAddress =
      "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691";
    const ozPrivateKey = "0x71d7bb07b9a64f6f78ac4c816aff4da9";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const account = new Account(
        provider,
        ozAccountAddress,
        ozPrivateKey,
        "1",
        "0x3"
      );
    
      const { classHash: smartrAccountClassHash } = await declareClass(
        account,
        classNames.SmartrAccount
      );
      console.log("smartrAccount class hash:", smartrAccountClassHash);
    
      const { classHash: starkValidatorClassHash } = await declareClass(
        account,
        classNames.StarkValidator
      );
      console.log("starkValidator class hash:", starkValidatorClassHash);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Note: To declare the class, the account you use must be loaded with ETH.

    Assuming you have named the script src/01-declare-class.ts, to transpile it and run it, use the script below:

    npx tsc --build
    
    node dist/01-declare-class.js
    

    The output should return the hashes for the 2 classes.

    Verify the SmartrAccount and StarkValidator class hash

    The 2 class hashes do NOT depend on the deployment or the network. So you can find them at any time with the classHash helper that comes with the SDK. The script below shows how to use that function:

    // file src/01-check-class.ts
    import { classHash, classNames } from "@0xknwn/starknet-modular-account";
    
    console.log("smartrAccount class hash:", classHash(classNames.SmartrAccount));
    console.log("starkValidator class hash:", classHash(classNames.StarkValidator));
    

    Assuming you have named the script src/01-check-class.ts, you can transpile and run it:

    npx tsc --build
    
    node dist/01-check-class.js
    

    Charge ETH to the SmartrAccount Address to deploy it

    Here again, the SDK provides a helper function called deployAccount to help with the deployment of the modular account. Before you move forward with the account, you must compute the account address with accountAddress and send ETH to it. To proceed, create a file named src/01-load-eth.ts with this content:

    {{#include ../experiments/documentation-examples/src/01-load-eth.ts}}
    

    Note: You must create a file abi/ERC20.ts that contains the ABI of an ERC20 in order to call it from a contract.

    Transpile and run the script:

    npx tsc --build
    
    node dist/01-load-eth.js
    

    Deploy the Modular Account

    Now that the address has some ETH on it, you can deploy the account with the deployAccount helper. Create a file named src/01-deploy-account.ts like below:

    // file src/01-deploy-account.ts
    import { RpcProvider, Account, Signer, CallData } from "starknet";
    import {
      accountAddress,
      classHash,
      deployAccount,
      SmartrAccount,
      SmartrAccountABI,
      classNames,
    } from "@0xknwn/starknet-modular-account";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    const smartrAccountPrivateKey = "0x1";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const smartrSigner = new Signer(smartrAccountPrivateKey);
      const smartrAccountPublicKey = await smartrSigner.getPubKey();
      const starkValidatorClassHash = classHash(classNames.StarkValidator);
      const calldata = new CallData(SmartrAccountABI).compile("constructor", {
        core_validator: starkValidatorClassHash,
        args: [smartrAccountPublicKey],
      });
      const smartrAccountAddress = accountAddress(
        classNames.SmartrAccount,
        smartrAccountPublicKey,
        calldata
      );
      const smartrAccount = new SmartrAccount(
        provider,
        smartrAccountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const address = await deployAccount(
        smartrAccount,
        classNames.SmartrAccount,
        smartrAccountPublicKey,
        calldata
      );
      if (address !== smartrAccountAddress) {
        throw new Error(
          `The account should have been deployed to ${smartrAccountAddress}, instead ${address}`
        );
      }
      console.log("accountAddress", smartrAccountAddress);
      console.log("public key", smartrAccountPublicKey);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/01-deploy-account.js
    

    Using the modular account from the SDK

    You can use rely on the SmartrAccount class to use the account. The script below shows all the requirements to compute the class hash, the address and instantiate the account:

    // file src/01-using-account.ts
    import { RpcProvider, Signer, CallData } from "starknet";
    import {
      accountAddress,
      classHash,
      SmartrAccount,
      SmartrAccountABI,
      classNames,
    } from "@0xknwn/starknet-modular-account";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    const smartrAccountPrivateKey = "0x1";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const smartrSigner = new Signer(smartrAccountPrivateKey);
      const smartrAccountPublicKey = await smartrSigner.getPubKey();
      const starkValidatorClassHash = classHash(classNames.StarkValidator);
      const calldata = new CallData(SmartrAccountABI).compile("constructor", {
        core_validator: starkValidatorClassHash,
        args: [smartrAccountPublicKey],
      });
      const smartrAccountAddress = accountAddress(
        classNames.SmartrAccount,
        smartrAccountPublicKey,
        calldata
      );
      const smartrAccount = new SmartrAccount(
        provider,
        smartrAccountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      console.log("address", smartrAccount.address);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/01-using-account.js
    

    Using the Stark Validator

    The Stark Validator Module can be used as a Core Validator for the account. It is used by default by the modular account in many configuration. In this section of the documentation, you will see how you can use the Moduler Account with the Stark Validator Module as a Core Module.

    Note: This section assumes the SmartrAccount class has been instantiated in the smartrAccount variable as shown in Using the modular account from the SDK. It also assumes the Counter contract that comes with the project has been deploys to the counterAddress and the CounterABI class is available. The 02-setup.ts script that comes with this project ensure those steps are executed.

    Interacting with a Contract

    The starknet modular account SDK provides the SmartrAccount class that extends the starknet.js Account class. As you can see from the script below, using the SmartrAccount is exactly like using the Account class, you can:

    • instantiate the account with an RpcProvider, an address and a Signer or private key
    • use the account in a Contract to call view functions
    • use the execute function of the account to call an external function of a contract. SmartrAccount provides the same methods as Account
    // file src/02-execute-tx.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./02-init";
    import { RpcProvider, Contract } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress, smartrAccountPrivateKey } =
        await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/02-execute-tx.js
    

    Interacting with the Stark Validator

    The SmartrAccount class, however, provides more than just the regular Account class. It can interact with functions that are part of the module and not part of the account. In the case of the Stark Validator, those functions are:

    #![allow(unused)]
    fn main() {
    fn get_public_key(self: @TState) -> felt252;
    fn set_public_key(ref self: TState, new_public_key: felt252);
    }

    To execute a function that is part of the module you need:

    • to figure out the stark validator module class hash
    • to check the module is installed on the account. That is something that is setup at the account deployment time
    • to use one of callOnModule for view functions or executeOnModule for running transactions on the SmartrAccount.

    The sections below dig into the details of these operations.

    Getting the stark validator module class hash

    This is something we have already done previously. You can use classHash("StaekValidator") after your imported the classHash function from @0xknwn/starknet-modular-account like below:

    // file src/02-check-class.ts
    import { classHash, classNames } from "@0xknwn/starknet-modular-account";
    
    console.log("starkValidator class hash:", classHash(classNames.StarkValidator));
    

    To execute the script, make sure you have deployed the account in the network and run the following commands:

    npx tsc --build
    
    node dist/02-check-class.js
    

    Check the module is installed on the account

    The SmartrAccount provides a method isModule that can be used to know if a module is installed with the account.

    // file src/02-module-installed.ts
    import {
      SmartrAccount,
      classHash,
      classNames,
    } from "@0xknwn/starknet-modular-account";
    import { init } from "./02-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const isInstalled = await account.isModule(
        classHash(classNames.StarkValidator)
      );
      console.log(
        "module",
        classHash(classNames.StarkValidator),
        "is installed",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/02-module-installed.js
    

    Calling views functions in the module

    To execute a view function on the module, we must build the argumemt list with the CallData class. Thwn we can call the callOnModule function from SmartrAccount with the module class hash, the function name and the calldata like below:

    // file src/02-registered-publickeys.ts
    import {
      StarkValidatorABI,
      SmartrAccount,
      classHash,
      classNames,
    } from "@0xknwn/starknet-modular-account";
    import { init } from "./02-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(StarkValidatorABI);
      const calldata = await moduleCallData.compile("get_public_key", {});
      const publickey = await account.callOnModule(
        classHash(classNames.StarkValidator),
        "get_public_key",
        calldata
      );
      console.log("publickey is", `0x${BigInt(publickey[0]).toString(16)}`);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/02-registered-publickey.js
    

    Executing external functions in the module

    To execute an external function on the module, we must build the argumemt list with the CallData class. Then we can call the executeOnModule function from SmartrAccount with the module class hash, the function name and the calldata like below. Here we will register a second public key for the same account:

    // file src/02-add-publickey.ts
    import {
      StarkValidatorABI,
      SmartrAccount,
      classHash,
      classNames,
    } from "@0xknwn/starknet-modular-account";
    import { init } from "./02-init";
    import { CallData, RpcProvider, Signer } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const newAccountPrivateKey = "0x2";
    
    const main = async () => {
      const signer = new Signer(newAccountPrivateKey);
      const newAccountPublicKey = await signer.getPubKey();
      console.log("second account public key", newAccountPublicKey);
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(StarkValidatorABI);
      const calldata = await moduleCallData.compile("set_public_key", {
        new_public_key: newAccountPublicKey,
      });
      const { transaction_hash } = await account.executeOnModule(
        classHash(classNames.StarkValidator),
        "set_public_key",
        calldata
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/02-update-publickey.js
    

    You can re-run the script from the previous example to check the account has two registered public key:

    node dist/02-registered-publickey.js
    

    Interacting with a Contract with the new registered key

    You now can interact with the SmartrAccount with your new private key like below:

    // file src/02-execute-tx-pk2.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./02-init";
    import { RpcProvider, Contract } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const newSmartrAccountPrivateKey = "0x2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        newSmartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/02-execute-tx-pk2.js
    

    Interacting with the Contract with the SDK Stark Module

    You can use the StarkValidator as a secondary module, even when installed as a Core module. To run a transaction with such a configuration, all you have to do is to call the StarkModule with the account address when creating the SmartrAccount by adding a 4th parameter to the constructor. The script below shows the change:

    // file src/02-execute-tx-pk2-with-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { StarkModule } from "@0xknwn/starknet-module";
    import { init, CounterABI } from "./02-init";
    import { RpcProvider, Contract } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const newSmartrAccountPrivateKey = "0x2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress } = await init();
      const module = new StarkModule(accountAddress);
      const account = new SmartrAccount(
        provider,
        accountAddress,
        newSmartrAccountPrivateKey,
        module,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    If you check the transaction, you can see the call is prefixed by a call to __validate_module__ with the account address and StarkValidator class hash as a first parameter. Transpile and run the script:

    npx tsc --build
    
    node dist/02-execute-tx-pk2-with-module.js
    

    Note: The Starkmodule is part of @0xknwn/starknet-module and this SDK as to be installed if you want to interact with the StarkModule as a secondary module.

    Using the Multisig Validator

    When installed with the Multisig Validator Module, the starknet modular account not only can have multiple signers registered but it can also require N of those signers to agree to sign a transaction. This Multisig feature takes place offchain and the SmartrAccount class provides the framework manage several signers on the same account to sign the a transaction. This is what is shown in this section.

    Note 1: This section assumes the SmartrAccount class has been instantiated in the smartrAccount variable as shown in Using the modular account from the SDK. It also assumes the Counter contract that comes with the project has been deploys to the counterAddress and the CounterABI class are available. The 03-setup.ts script that comes with this project ensure those steps are executed.

    Note 2: This multi-signer model has some limits: the exchange of the transaction between signers is not managed by the SDK and requires some synchronization between the actors. In addition, the fact the transaction is a regular transaction that involves the account Nonce generated by the network prevents from using it at a large scale. The starknet modular account can be used to implement more advanced models to workaround those 2 issues.

    Interacting with the Multisig Validator

    The SmartrAccount class, however, provides more than just the regular Account class. It can interact with functions that are part of the module and not part of the account. In the case of the Multisig Validator, those functions are:

    #![allow(unused)]
    fn main() {
    fn get_public_keys(self: @TState) -> Array<felt252>;
    fn add_public_key(ref self: TState, new_public_key: felt252);
    fn remove_public_key(ref self: TState, old_public_key: felt252);
    fn get_threshold(self: @TState) -> u8;
    fn set_threshold(ref self: TState, new_threshold: u8);
    }

    To execute a function that is part of the module you need:

    • to figure out the Multisig Validator module class hash
    • to check the module is installed on the account. That is something that is setup at the account deployment time
    • to use one of callOnModule for view functions or executeOnModule for running transactions on the SmartrAccount.

    The sections below dig into the details of these operations.

    Getting the stark validator module class hash

    This is something we have already done previously. You can use classHash("MultisigValidator") after your imported the classHash function from @0xknwn/starknet-module like below:

    // file src/03-check-class.ts
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    
    console.log(
      "MultisigValidator class hash:",
      classHash(moduleClassNames.MultisigValidator)
    );
    

    To execute the script, make sure you have deployed the account in the network and run the following commands:

    npx tsc --build
    
    node dist/03-check-class.js
    

    Check the module is installed on the account

    The SmartrAccount provides a method isModule that can be used to know if a module is installed with the account.

    // file src/03-module-installed.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const isInstalled = await account.isModule(
        classHash(moduleClassNames.MultisigValidator)
      );
      console.log(
        "module",
        classHash(moduleClassNames.MultisigValidator),
        "is installed",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-module-installed.js
    

    Calling views functions in the module

    To execute a view function on the module, we must build the argumemt list with the CallData class. Thwn we can call the callOnModule function from SmartrAccount with the module class hash, the function name and the calldata like below:

    // file src/03-registered-publickeys.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      MultisigValidatorABI,
      classHash as moduleClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(MultisigValidatorABI);
      const calldata = moduleCallData.compile("get_public_keys", {});
      const publickeysList = await account.callOnModule(
        moduleClassHash(moduleClassNames.MultisigValidator),
        "get_public_keys",
        calldata
      );
      console.log("number of public keys for module", publickeysList.length);
      publickeysList.forEach((publickey, idx) => {
        console.log("publickey #", idx + 1, `0x${publickey.toString(16)}`);
      });
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-registered-publickeys.js
    

    Executing external functions in the module

    To execute an external function on the module, we must build the argumemt list with the CallData class. Then we can call the executeOnModule function from SmartrAccount with the module class hash, the function name and the calldata like below. Here we will register a second public key for the same account:

    // file src/03-add-publickeys.ts
    import {
      SmartrAccountABI,
      SmartrAccount,
    } from "@0xknwn/starknet-modular-account";
    import {
      MultisigValidatorABI,
      classHash as moduleClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import { CallData, RpcProvider, Signer, hash, type Call } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const secondAccountPrivateKey = "0x2";
    const thirdAccountPrivateKey = "0x3";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const module_class_hash = moduleClassHash(moduleClassNames.MultisigValidator);
      const calls: Call[] = [];
      for (const privateKey of [secondAccountPrivateKey, thirdAccountPrivateKey]) {
        const signer = new Signer(privateKey);
        const publicKey = await signer.getPubKey();
        console.log("new account public key", publicKey);
        const moduleCallData = new CallData(MultisigValidatorABI);
        const moduleCalldata = moduleCallData.compile("add_public_key", {
          new_public_key: publicKey,
        });
        const accountCallData = new CallData(SmartrAccountABI);
        const calldata = accountCallData.compile("execute_on_module", {
          class_hash: module_class_hash,
          call: {
            selector: hash.getSelectorFromName("add_public_key"),
            to: accountAddress,
            calldata: moduleCalldata,
          },
        });
        const call: Call = {
          entrypoint: "execute_on_module",
          contractAddress: accountAddress,
          calldata,
        };
        calls.push(call);
      }
      const { transaction_hash } = await account.execute(calls);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-add-publickeys.js
    

    You can re-run the script from the previous example to check the account has two registered public key:

    node dist/03-registered-publickeys.js
    

    The output should look like that:

    number of public keys for module 3
    publickey # 1 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca
    publickey # 2 0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5
    publickey # 3 0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20
    

    Interacting with a Contract with the new registered key

    You now can interact with the SmartrAccount with your second private key like below:

    // file src/03-execute-tx.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./03-init";
    import { RpcProvider, Contract } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const secondAccountPrivateKey = "0x2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        secondAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-execute-tx-pk2.js
    

    Changing the Account Threshold to 2

    By changing the Multisig Validator Threshold to 2, you force transactions to be signed by 2 of the 3 signers of the account. Run a script like below to change the threshold:

    // file src/03-increase-threshold.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      MultisigValidatorABI,
      classHash as moduleClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(MultisigValidatorABI);
      const calldata = moduleCallData.compile("set_threshold", {
        new_threshold: 2,
      });
      const { transaction_hash } = await account.executeOnModule(
        moduleClassHash(moduleClassNames.MultisigValidator),
        "set_threshold",
        calldata
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-increase-threshold.js
    

    You can check the current threshold on the account with the script below:

    // file src/04-get-threshold.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      MultisigValidatorABI,
      classHash as moduleClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(MultisigValidatorABI);
      const calldata = await moduleCallData.compile("get_threshold", {});
      const threshold = await account.callOnModule(
        moduleClassHash(moduleClassNames.MultisigValidator),
        "get_threshold",
        calldata
      );
      threshold.forEach((threshold) => console.log("threshold", threshold));
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Run the script with the command below:

    npx tsc --build
    
    node dist/03-get-threshold.js
    

    Checking you can NOT run a transaction with a single signer

    The script below executes a transaction with a single signer as it was the case in the previous section:

    // file src/03-execute-tx.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./03-init";
    import { RpcProvider, Contract } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress, smartrAccountPrivateKey } =
        await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Make sure you have deployed the account and the counter contract in the network and run the following commands:

    npx tsc --build
    
    node dist/03-execute-tx.js
    

    You are now getting an error saying the signature is invalid like below:

    Execution failed.
    Failure reason: 0x4163636f756e743a20696e76616c6964207369676e6174757265 
    ('Account: invalid signature')
    

    Running a Multiple Signer Transaction

    To run a transaction with multiple signers, you need to instantiate several SmartrAccount, each one with a different signer. Because you have set the threshold, you need to instantiate 2 accounts.

    Once done, proceed in 3 steps:

    • Step 1: generate the transaction details. This requires you create the calls but also you set some details about it, including: the Fees, the Nonce, the Version and the Chain. The SmartrAccount class uses the provider to get the chain id. To get the other details, you should run the prepareMultisig that returns the details associated with the transaction.
    • Step 2: have all the signers generate their part of the signature. The signMultisig takes the list of calls and the details you have generated and provides the signature as an array of string
    • Step 3: Execute the transaction with all the signatures from Step 2. This could be done by anyone, including one of the account you have already created. The executeMultisig function takes the list of calls, the details and an array that contains all the signatures.

    The script below signs the transaction with 2 signers and to run the increment external function of the Counter contract. It shows the value of the counter before and after the call:

    // file src/03-execute-tx-multiple-signers.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./03-init";
    import { RpcProvider, Contract, ArraySignatureType } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const secondSmartrAccountPrivateKey = "0x2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress, smartrAccountPrivateKey } =
        await init();
      const firstAccount = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const secondAccount = new SmartrAccount(
        provider,
        accountAddress,
        secondSmartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
    
      // Before you start check the value of the counter
      const counter = new Contract(CounterABI, counterAddress, provider);
      let currentCounter = await counter.call("get");
      console.log("currentCounter value", currentCounter);
    
      // Step 1: Prepare the transaction and get the details
      const call = counter.populate("increment");
      const calls = [call];
    
      const detail = await firstAccount.prepareMultisig(calls);
      console.log("below are the details assciated with the transaction");
      console.log(detail);
    
      // Step 2: Sign the transaction with 2 signers
      // (because the threshold on the account is currently 2)
      const firstSignature: ArraySignatureType = await firstAccount.signMultisig(
        calls,
        detail
      );
      console.log("first signature is", firstSignature);
      const secondSignature: ArraySignatureType = await secondAccount.signMultisig(
        calls,
        detail
      );
      console.log("second signature is", secondSignature);
    
      // Step 3: Execute the transaction
      const { transaction_hash } = await firstAccount.executeMultisig(
        calls,
        detail,
        [...firstSignature, ...secondSignature]
      );
      const receipt = await firstAccount.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      // Once finished, check the value of the counter again
      currentCounter = await counter.call("get");
      console.log("currentCounter value", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-execute-tx-multiple-signers.js
    

    Reset the threshold to one

    As for any transaction, you need to run a multi-signed transaction to reset the account threshold back to one. The script below build the call to execute_on_module and run it with multiple signer:

    // file src/03-decrease-threshold.ts
    import {
      SmartrAccount,
      SmartrAccountABI,
    } from "@0xknwn/starknet-modular-account";
    import {
      MultisigValidatorABI,
      classHash as moduleClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import {
      CallData,
      RpcProvider,
      hash,
      type Call,
      type ArraySignatureType,
    } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const secondSmartrAccountPrivateKey = "0x2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const firstAccount = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const secondAccount = new SmartrAccount(
        provider,
        accountAddress,
        secondSmartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
    
      // Before you start build the set_threshold call
      const moduleCallData = new CallData(MultisigValidatorABI);
      const moduleCalldata = moduleCallData.compile("set_threshold", {
        new_threshold: 1,
      });
      const accountCallData = new CallData(SmartrAccountABI);
      const calldata = accountCallData.compile("execute_on_module", {
        class_hash: moduleClassHash(moduleClassNames.MultisigValidator),
        call: {
          selector: hash.getSelectorFromName("set_threshold"),
          to: accountAddress,
          calldata: moduleCalldata,
        },
      });
      const call: Call = {
        entrypoint: "execute_on_module",
        contractAddress: accountAddress,
        calldata,
      };
      const calls = [call];
    
      // Step 1: Prepare the transaction and get the details
      const detail = await firstAccount.prepareMultisig(calls);
    
      // Step 2: Sign the transaction with 2 signers
      // (because the threshold on the account is currently 2)
      const firstSignature: ArraySignatureType = await firstAccount.signMultisig(
        calls,
        detail
      );
      const secondSignature: ArraySignatureType = await secondAccount.signMultisig(
        calls,
        detail
      );
    
      // Step 3: Execute the transaction
      const { transaction_hash } = await firstAccount.executeMultisig(
        calls,
        detail,
        [...firstSignature, ...secondSignature]
      );
      const receipt = await firstAccount.waitForTransaction(transaction_hash);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Execute the script with the following commands:

    npx tsc --build
    
    node dist/03-decrease-threshold.js
    

    You can check the threshold is back to one:

    node dist/03-get-threshold.js
    

    Remove Registered Keys

    You can now run transaction with a single signer on the account. The script below shows how to remove 2 public keys from a single call:

    // file src/03-remove-publickeys.ts
    import {
      SmartrAccountABI,
      SmartrAccount,
    } from "@0xknwn/starknet-modular-account";
    import {
      MultisigValidatorABI,
      classHash as moduleClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./03-init";
    import { CallData, RpcProvider, Signer, hash, type Call } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const secondAccountPrivateKey = "0x2";
    const thirdAccountPrivateKey = "0x3";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const module_class_hash = moduleClassHash(moduleClassNames.MultisigValidator);
      const calls: Call[] = [];
      for (const privateKey of [secondAccountPrivateKey, thirdAccountPrivateKey]) {
        const signer = new Signer(privateKey);
        const publicKey = await signer.getPubKey();
        console.log("account public key to remove", publicKey);
        const moduleCallData = new CallData(MultisigValidatorABI);
        const moduleCalldata = moduleCallData.compile("remove_public_key", {
          old_public_key: publicKey,
        });
        const accountCallData = new CallData(SmartrAccountABI);
        const calldata = accountCallData.compile("execute_on_module", {
          class_hash: module_class_hash,
          call: {
            selector: hash.getSelectorFromName("remove_public_key"),
            to: accountAddress,
            calldata: moduleCalldata,
          },
        });
        const call: Call = {
          entrypoint: "execute_on_module",
          contractAddress: accountAddress,
          calldata,
        };
        calls.push(call);
      }
      const { transaction_hash } = await account.execute(calls);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/03-remove-publickeys.js
    

    You can check the 2 of the 3 public keys have been removed:

    node dist/03-registered-publickeys.js
    

    Using the Eth Validator

    The Eth Validator Module can both work as a Secondary or as the Core Validator for the account. It requires a separate SDK. In this section of the documentation, you will see how you can use the Moduler Account to interact with the Eth Validator Module.

    Note: This section assumes the SmartrAccount class has been instantiated in the smartrAccount variable as shown in Using the modular account from the SDK. It also assumes the Counter contract that comes with the project has been deploys to the counterAddress and the CounterABI class is available. The 05-setup.ts script that comes with this project ensure those steps are executed.

    Installing the Eth Validator SDK

    If you plan to use the Eth Validatoi module, you might need the @0xknwn/starknet-module SDK in addition to the @0xknwn/starknet-modular-account SDK. To install it, run:

    npm install --save \
      @0xknwn/starknet-module
    

    Declaring the Eth Validator

    If you are working on a network that does not have the eth validator class already declared, you will need to declare it. The Eth validator module SDK, aka @0xknwn/starknet-module contains a helper function named declareClass to declare the class to the network. To use it, you need to pass:

    • A starknet.js Account as a first parameter
    • The name of the class to declare as the 2nd parameter. For the Eth Validator, the name isEthValidator

    Below is an example of a script that declares the new classes.

    // file src/04-declare-eth-validator.ts
    import { RpcProvider, Account } from "starknet";
    import {
      declareClass,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    const ozAccountAddress =
      "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691";
    const ozPrivateKey = "0x71d7bb07b9a64f6f78ac4c816aff4da9";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const account = new Account(
        provider,
        ozAccountAddress,
        ozPrivateKey,
        "1",
        "0x3"
      );
    
      const { classHash: ethValidatorClassHash } = await declareClass(
        account,
        moduleClassNames.EthValidator
      );
      console.log("EthValidator class hash:", ethValidatorClassHash);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Note: To declare the class, the account you use must be loaded with ETH.

    Assuming you have named the script src/04-declare-eth-validator.ts, transpile and run it:

    npx tsc --build
    
    node dist/04-declare-eth-validator.js
    

    The output should return the hash for the class.

    Verify the Eth Validator class hash

    The class hash does NOT depend on the deployment or the network. So you can find them at any time with the classHash helper that comes with the SDK. The script below shows how to use that function:

    // file src/04-check-eth-validator.ts
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    
    console.log(
      "Computed EthValidator class hash:",
      classHash(moduleClassNames.EthValidator)
    );
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/04-check-eth-validator.js
    

    Using the Eth Validator as a Secondary Validator

    The simplest way to use the Eth Validator is to add it as a module to an existing account and execute a transaction with the EthModule class from the @0xknwn/starknet-module.

    Register the Eth Validator as a Module

    The modular account SDK comes with the addModule, removeModule and isModule. You can use those 3 functions to manage the module in the account once it has been declared to the network. To register the module in the account, use addModule:

    // file src/04-add-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./04-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const { transaction_hash } = await account.addModule(
        classHash(moduleClassNames.EthValidator)
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      const isInstalled = await account.isModule(
        classHash(moduleClassNames.EthValidator)
      );
      console.log(
        "module",
        classHash(moduleClassNames.EthValidator),
        "is installed",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/04-add-module.js
    

    Register the public key associated with your Eth Private Key

    Every module comes with a set of Management API. In the case of the Eth Validator, the associated interfaces are the following:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IPublicKey<TState> {
        fn set_public_key(ref self: TState, new_public_key: EthPublicKey);
        fn get_public_key(self: @TState) -> EthPublicKey;
    }
    }

    Now that you have installed the module, you can create an ETH private key and register the associated public key in the module. For the purpose of the demonstration, we will use an arbitrary (and now unsafe) private/public key pair:

    • private key: 0xb28ebb20fb1015da6e6367d1b5dba9b52862a06dbb3a4022e4749b6987ac1bd2
    • public key:
      • x: 0xd31cf702f5c89d49c567dcfd568bc4869e343506749f69d849eb408802cfa646
      • y: 0x348c7bbf341964c306669365292c0066c23a2fedd131907534677aa3e22db2fc

    Because Starknet types can only manage felt252 that are smaller than uint256 the format used by EthPublicKey is actually an array<felt252> that is made of [x.low, x.high, y.low, y.high]. To register the public key, use the script below:

    // file src/04-register-publickey.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash as ethClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { EthSigner, cairo } from "starknet";
    import { init } from "./04-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const ethPrivateKey =
      "0xb28ebb20fb1015da6e6367d1b5dba9b52862a06dbb3a4022e4749b6987ac1bd2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const module_class_hash = ethClassHash(moduleClassNames.EthValidator);
      const signer = new EthSigner(ethPrivateKey);
      const publicKey = await signer.getPubKey();
      const coords = publicKey.slice(2, publicKey.length);
      const x = coords.slice(0, 64);
      const x_felts = cairo.uint256(`0x${x}`);
      const y = coords.slice(64, 128);
      const y_felts = cairo.uint256(`0x${y}`);
      console.log("x:", `0x${x}`);
      console.log("(x.low:", x_felts.low, ", x.high:", x_felts.high, ")");
      console.log("y:", `0x${y}`);
      console.log("(y.low:", y_felts.low, ", y.high:", y_felts.high, ")");
      const { transaction_hash } = await account.executeOnModule(
        module_class_hash,
        "set_public_key",
        [
          x_felts.low.toString(),
          x_felts.high.toString(),
          y_felts.low.toString(),
          y_felts.high.toString(),
        ]
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/04-register-publickey.js
    

    You can check the public key is correctly registered with the script below:

    // file src/04-get-publickey.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      EthValidatorABI,
      classHash as ethClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./04-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(EthValidatorABI);
      const calldata = moduleCallData.compile("get_public_key", {});
      const public_keys = await account.callOnModule(
        ethClassHash(moduleClassNames.EthValidator),
        "get_public_key",
        calldata
      );
      public_keys.forEach((public_key, idx) =>
        console.log(`public key (${idx}):`, public_key)
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/04-get-publickey.js
    

    Run a transaction with the EthModule

    The EthModule is an implementation of a module that can be passed to the SmartrAccount and manages the decoration of the transaction under the hood. To fully instantiate that module, you will need:

    • to instantiate the EthModule module from the SDK
    • to use the EthSigner provided by Starknet.js with the Private Key
    • to instantiate the SmartrAccount with the 2 classes above

    Then you can run a transaction, exactly as you would do with any Starknet.js account. The example below execute the increment entrypoint on the Counter contract:

    // file src/04-execute-tx.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./04-init";
    import { RpcProvider, Contract, EthSigner } from "starknet";
    import { EthModule } from "@0xknwn/starknet-module";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const ethPrivateKey =
      "0xb28ebb20fb1015da6e6367d1b5dba9b52862a06dbb3a4022e4749b6987ac1bd2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress } = await init();
      console.log("accountAddress", accountAddress);
      const signer = new EthSigner(ethPrivateKey);
      const ethModule = new EthModule(accountAddress);
      const account = new SmartrAccount(
        provider,
        accountAddress,
        signer,
        ethModule,
        "1",
        "0x3"
      );
      console.log("counterAddress", counterAddress);
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call, {
        version: "0x3",
        resourceBounds: {
          l2_gas: {
            max_amount: "0x0",
            max_price_per_unit: "0x0",
          },
          l1_gas: {
            max_amount: "0x2f10",
            max_price_per_unit: "0x22ecb25c00",
          },
        },
      });
      console.log("transaction_hash", transaction_hash);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/04-execute-tx.js
    

    Remove the Eth Validator Module

    You can use removeModule and isModule to remove the module from the account with the script below:

    // file src/04-remove-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./04-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const { transaction_hash } = await account.removeModule(
        classHash(moduleClassNames.EthValidator)
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      const isInstalled = await account.isModule(
        classHash(moduleClassNames.EthValidator)
      );
      console.log(
        "module",
        classHash(moduleClassNames.EthValidator),
        "has been removed",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/04-remove-module.js
    

    Using the Eth Validator as the Core Validator

    You can also use the Eth Validator as a Core Validator for the account. For that purpose you will deploy a new account and use the EthSigner to validate the account deployment without registering the EthModule in the SmartrAccount. In order to proceed you need to:

    • Generate the public key as an Array of felt252
    • Compute the account address
    • Send ETH to the modular account address
    • Deploy the Account

    Compute the Public Key as an Array of felt252

    The EthSigner helps to generate the public key from the private key. Once you have the public key you should slice to get an array of 4 pieces like below:

    const signer = new EthSigner(ethPrivateKey);
    const publicKey = await signer.getPubKey();
    const coords = publicKey.slice(2, publicKey.length);
    const x = coords.slice(0, 64);
    const x_felts = cairo.uint256(`0x${x}`);
    const y = coords.slice(64, 128);
    const y_felts = cairo.uint256(`0x${y}`);
    const publicKeyArray = [
      x_felts.low.toString(),
      x_felts.high.toString(),
      y_felts.low.toString(),
      y_felts.high.toString(),
    ];
    

    Compute the Account Address

    Once you have the public key, you should use the accountAddress function from @0xknwn/starknet-modular-account to compute the address of the account you will install. As a Salt, we will use the hash.computeHashOnElements from the public key like below:

    const publicKeyHash = hash.computeHashOnElements(publicKeyArray);
    const computedAccountAddress = accountAddress(
      "SmartrAccount",
      publicKeyHash,
      [ethClassHash("EthValidator"), "0x4", ...publicKeyArray]
    );
    

    Note: The "0x4" that is inserted in the calldata is here to indicate there are 4 pieces to the publci key:

    Send ETH to the SmartrAccount Address to deploy it

    To deploy the account, you need to have ETH associated with the target account address. Assuming you have access to an account with ETH, this is how you send eth to the computedAccountAddress:

    const account = new SmartrAccount(
      provider,
      ozAccountAddress,
      smartrAccountPrivateKey
    );
    const ETH = new Contract(ERC20ABI, ethAddress, account);
    const initial_EthTransfer = cairo.uint256(5n * 10n ** 15n);
    const call = ETH.populate("transfer", {
      recipient: computedAccountAddress,
      amount: initial_EthTransfer,
    });
    const { transaction_hash } = await account.execute(call);
    const output = await account.waitForTransaction(transaction_hash);
    

    Deploy the Account with the Eth Validator as Core

    To deploy the account, you will need to use the deployAccount helper function from @0xknwn/starknet-modular-account with a SmartrAccount that has been instantiated with a EthSigner like below:

    const ethSmartrSigner = new EthSigner(smartrAccountPrivateKey);
    const ethAccount = new SmartrAccount(
      provider,
      computedAccountAddress,
      ethSmartrSigner
    );
    const address = await deployAccount(
      ethAccount,
      "SmartrAccount",
      publicKeyHash,
      [ethClassHash("EthValidator"), "0x4", ...publicKeyArray]
    );
    

    The Script Code

    You will find below the whole script that does the account deployment:

    // file src/04-deploy-account.ts
    import {
      RpcProvider,
      EthSigner,
      Contract,
      cairo,
      hash,
      Account,
    } from "starknet";
    import {
      accountAddress,
      deployAccount,
      SmartrAccount,
      classNames as accountClassName,
    } from "@0xknwn/starknet-modular-account";
    import {
      classHash as ethClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./04-init";
    import { ABI as ERC20ABI } from "./abi/ERC20";
    const strkAddress =
      "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    // const providerURL = "http://127.0.0.1:5050/rpc";
    const ethPrivateKey =
      "0xb28ebb20fb1015da6e6367d1b5dba9b52862a06dbb3a4022e4749b6987ac1bd2";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { ozAccountAddress, ozAccountPrivateKey } = await init();
    
      // Step 1 - Get the public key from the Eth Signer
      const ethSmartrSigner = new EthSigner(ethPrivateKey);
      const publicKey = await ethSmartrSigner.getPubKey();
      const coords = publicKey.slice(2, publicKey.length);
      const x = coords.slice(0, 64);
      const x_felts = cairo.uint256(`0x${x}`);
      const y = coords.slice(64, 128);
      const y_felts = cairo.uint256(`0x${y}`);
      const publicKeyArray = [
        x_felts.low.toString(),
        x_felts.high.toString(),
        y_felts.low.toString(),
        y_felts.high.toString(),
      ];
    
      // Step 2 - Compute the account address
      const publicKeyHash = hash.computeHashOnElements(publicKeyArray);
      const computedAccountAddress = accountAddress(
        accountClassName.SmartrAccount,
        publicKeyHash,
        [ethClassHash(moduleClassNames.EthValidator), "0x4", ...publicKeyArray]
      );
    
      // Step 3 - Send STRK to the computed account address
      const account = new Account(
        provider,
        ozAccountAddress,
        ozAccountPrivateKey,
        "1",
        "0x3"
      );
      const STRK = new Contract(ERC20ABI, strkAddress, account);
      const initial_strkTransfer = cairo.uint256(50000n * 10n ** 15n);
      const { transaction_hash } = await STRK.transfer(
        computedAccountAddress,
        initial_strkTransfer
      );
      const output = await account.waitForTransaction(transaction_hash);
      if (!output.isSuccess()) {
        throw new Error("Could not send STRK to the expected address");
      }
    
      // Step 4 - Deploy the account with the EthValidator as Core Validator
      const ethAccount = new SmartrAccount(
        provider,
        computedAccountAddress,
        ethSmartrSigner,
        undefined,
        "1",
        "0x3"
      );
      const address = await deployAccount(
        ethAccount,
        accountClassName.SmartrAccount,
        publicKeyHash,
        [ethClassHash(moduleClassNames.EthValidator), "0x4", ...publicKeyArray],
        {
          version: "0x3",
          resourceBounds: {
            l2_gas: {
              max_amount: "0x0",
              max_price_per_unit: "0x0",
            },
            l1_gas: {
              max_amount: "0x2f10",
              max_price_per_unit: "0x22ecb25c00",
            },
          },
        }
      );
      if (address !== computedAccountAddress) {
        throw new Error(
          `The account should have been deployed to ${computedAccountAddress}, instead ${address}`
        );
      }
      console.log("accountAddress", computedAccountAddress);
      console.log("public key", publicKeyArray);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run it:

    npx tsc --build
    
    node dist/04-deploy-account.js
    

    Running a transaction with the Eth Validator as Core

    Running a transaction with the EthValidator as a Core is no more complex than running a transaction on a regular account. All you need to do is

    • get the account address that could have been saved from earlier
    • instantiate the SmartrAccount with the Starknet.js EthSigner
    • execute the transaction

    Below is an example that assumes you have deployed the account with the 04-deploy-account.ts script earlier:

    // file src/04-execute-tx-core.ts
    import {
      SmartrAccount,
      accountAddress,
      classNames as accountClassNames,
    } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./04-init";
    import { RpcProvider, Contract, EthSigner, cairo, hash } from "starknet";
    import {
      classHash as ethClassHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    const providerURL = "http://127.0.0.1:5050/rpc";
    const ethPrivateKey =
      "0xb28ebb20fb1015da6e6367d1b5dba9b52862a06dbb3a4022e4749b6987ac1bd2";
    
    const main = async () => {
      // recompute the account address
      const signer = new EthSigner(ethPrivateKey);
      const publicKey = await signer.getPubKey();
      const coords = publicKey.slice(2, publicKey.length);
      const x = coords.slice(0, 64);
      const x_felts = cairo.uint256(`0x${x}`);
      const y = coords.slice(64, 128);
      const y_felts = cairo.uint256(`0x${y}`);
      const publicKeyArray = [
        x_felts.low.toString(),
        x_felts.high.toString(),
        y_felts.low.toString(),
        y_felts.high.toString(),
      ];
    
      const publicKeyHash = hash.computeHashOnElements(publicKeyArray);
      const computedAccountAddress = accountAddress(
        accountClassNames.SmartrAccount,
        publicKeyHash,
        [ethClassHash(moduleClassNames.EthValidator), "0x4", ...publicKeyArray]
      );
    
      // execute the transaction
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { counterAddress } = await init();
      const account = new SmartrAccount(
        provider,
        computedAccountAddress,
        signer,
        undefined,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call, {
        version: "0x3",
        resourceBounds: {
          l2_gas: {
            max_amount: "0x0",
            max_price_per_unit: "0x0",
          },
          l1_gas: {
            max_amount: "0x2f10",
            max_price_per_unit: "0x22ecb25c00",
          },
        },
      });
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run it:

    npx tsc --build
    
    node dist/04-execute-tx-core.js
    

    As you can see from the script:

    • You do not need the EthModule to interact with the account. That is because the validator is used as a Core Validator and, as such, the transaction does not required to be prefixed
    • Running transactions is the same as running a transaction with the Stark validator. Only the signature changes.

    Using the P256 Validator

    The P256 Validator Module can both work as a Secondary or as the Core Validator for the account. It requires the module SDK. In this section of the documentation, you will see how you can use the Modular Account to interact with the P256 Validator Module.

    Note: This section assumes the SmartrAccount class has been instantiated in the smartrAccount variable as shown in Using the modular account from the SDK. It also assumes the Counter contract that comes with the project has been deploys to the counterAddress and the CounterABI class is available. The 05-setup.ts script that comes with this project ensure those steps are executed.

    Installing the P256 Validator SDK

    If you plan to use the P256 Validatoi module, you might need the @0xknwn/starknet-module SDK in addition to the @0xknwn/starknet-modular-account SDK. To install it, run:

    npm install --save \
      @0xknwn/starknet-module
    

    Declaring the P256 Validator

    If you are working on a network that does not have the P256 validator class already declared, you will need to declare it. The P256 validator module SDK, aka @0xknwn/starknet-module contains a helper function named declareClass to declare the class to the network. To use it, you need to pass:

    • A starknet.js Account as a first parameter
    • The name of the class to declare as the 2nd parameter. For the P256 Validator, the name isP256Validator

    Below is an example of a script that declares the new classes.

    // file src/05-declare-p256-validator.ts
    import { RpcProvider, Account } from "starknet";
    import {
      declareClass,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const ozAccountAddress =
      "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691";
    const ozPrivateKey = "0x71d7bb07b9a64f6f78ac4c816aff4da9";
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const account = new Account(
        provider,
        ozAccountAddress,
        ozPrivateKey,
        "1",
        "0x3"
      );
    
      const { classHash: p256ValidatorClassHash } = await declareClass(
        account,
        moduleClassNames.P256Validator
      );
      console.log("P256Validator class hash:", p256ValidatorClassHash);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Note: To declare the class, the account you use must be loaded with P256.

    Assuming you have named the script src/05-declare-p256-validator.ts, transpile and run it:

    npx tsc --build
    
    node dist/05-declare-p256-validator.js
    

    The output should return the hash for the class.

    Verify the P256 Validator class hash

    The class hash does NOT depend on the deployment or the network. So you can find them at any time with the classHash helper that comes with the SDK. The script below shows how to use that function:

    // file src/05-check-p256-validator.ts
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    
    console.log(
      "Computed P256Validator class hash:",
      classHash(moduleClassNames.P256Validator)
    );
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/05-check-p256-validator.js
    

    Using the P256 Validator as a Secondary Validator

    The simplest way to use the P256 Validator is to add it as a module to an existing account and execute a transaction with the P256Module class from the @0xknwn/starknet-module.

    Register the P256 Validator as a Module

    The modular account SDK comes with the addModule, removeModule and isModule. You can use those 3 functions to manage the module in the account once it has been declared to the network. To register the module in the account, use addModule:

    // file src/05-add-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./05-init";
    import { RpcProvider } from "starknet";
    
    const ozAccountAddress =
      "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691";
    const ozPrivateKey = "0x71d7bb07b9a64f6f78ac4c816aff4da9";
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const { transaction_hash } = await account.addModule(
        classHash(moduleClassNames.P256Validator)
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      const isInstalled = await account.isModule(
        classHash(moduleClassNames.P256Validator)
      );
      console.log(
        "module",
        classHash(moduleClassNames.P256Validator),
        "is installed:",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/05-add-module.js
    

    Register the public key associated with your P256 Private Key

    Every module comes with a set of Management API. In the case of the P256 Validator, the associated interfaces are the following:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IPublicKey<TState> {
        fn set_public_key(ref self: TState, new_public_key: P256PublicKey);
        fn get_public_key(self: @TState) -> P256PublicKey;
    }
    }

    Now that you have installed the module, you can create an ETH private key and register the associated public key in the module. For the purpose of the demonstration, we will use an arbitrary (and now unsafe) private/public key pair:

    • private key: 0x1efecf7ee1e25bb87098baf2aaab0406167aae0d5ea9ba0d31404bf01886bd0e
    • public key:
      • x: 0x097420e05fbc83afe4d73b31890187d0cacf2c3653e27f434701a91625f916c2
        • x.low: 269579757328574126121444003492591638210
        • x.high: 12566025211498978771503502663570524112
      • y: 0x98a304ff544db99c864308a9b3432324adc6c792181bae33fe7a4cbd48cf263a
        • y.low: 230988565823064299531546210785320445498
        • y.high: 202889101106158949967186230758848275236

    Because Starknet types can only manage felt252 that are smaller than uint256 the format used by P256PublicKey is actually an array<felt252> that is made of [x.low, x.high, y.low, y.high]. To register the public key, use the script below:

    // file src/05-register-publickey.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash as moduleClassHash,
      P256Signer,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { cairo } from "starknet";
    import { init } from "./05-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const p256PrivateKey =
      "0x1efecf7ee1e25bb87098baf2aaab0406167aae0d5ea9ba0d31404bf01886bd0e";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const module_class_hash = moduleClassHash(moduleClassNames.P256Validator);
      const signer = new P256Signer(p256PrivateKey);
      const publicKey = await signer.getPubKey();
      const coords = publicKey.slice(2, publicKey.length);
      const x = coords.slice(0, 64);
      const x_felts = cairo.uint256(`0x${x}`);
      const y = coords.slice(64, 128);
      const y_felts = cairo.uint256(`0x${y}`);
      console.log("x:", `0x${x}`);
      console.log("(x.low:", x_felts.low, ", x.high:", x_felts.high, ")");
      console.log("y:", `0x${y}`);
      console.log("(y.low:", y_felts.low, ", y.high:", y_felts.high, ")");
      const { transaction_hash } = await account.executeOnModule(
        module_class_hash,
        "set_public_key",
        [
          x_felts.low.toString(),
          x_felts.high.toString(),
          y_felts.low.toString(),
          y_felts.high.toString(),
        ]
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/05-register-publickey.js
    

    You can check the public key is correctly registered with the script below:

    // file src/05-get-publickey.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash as moduleClassHash,
      P256ValidatorABI,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./05-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const moduleCallData = new CallData(P256ValidatorABI);
      const calldata = moduleCallData.compile("get_public_key", {});
      const public_keys = await account.callOnModule(
        moduleClassHash(moduleClassNames.P256Validator),
        "get_public_key",
        calldata
      );
      public_keys.forEach((public_key, idx) =>
        console.log(`public key (${idx}):`, public_key)
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/05-get-publickey.js
    

    Run a transaction with the P256Module

    The P256Module is an implementation of a module that can be passed to the SmartrAccount and manages the decoration of the transaction under the hood. To fully instantiate that module, you will need:

    • to instantiate the P256Module module from the SDK
    • to use the P256Signer provided by `@0xknwn/starknet-module with the Private Key
    • to instantiate the SmartrAccount with the 2 classes above

    Then you can run a transaction, exactly as you would do with any Starknet.js account. The example below execute the increment entrypoint on the Counter contract:

    // file src/05-execute-tx.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./05-init";
    import { RpcProvider, Contract } from "starknet";
    import { P256Module, P256Signer } from "@0xknwn/starknet-module";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const p256PrivateKey =
      "0x1efecf7ee1e25bb87098baf2aaab0406167aae0d5ea9ba0d31404bf01886bd0e";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, counterAddress, smartrAccountPrivateKey } =
        await init();
      console.log("accountAddress", accountAddress);
      const signer = new P256Signer(p256PrivateKey);
      const p256Module = new P256Module(accountAddress);
      const account = new SmartrAccount(
        provider,
        accountAddress,
        signer,
        p256Module,
        "1",
        "0x3"
      );
      console.log("counterAddress", counterAddress);
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call, {
        version: "0x3",
        resourceBounds: {
          l2_gas: {
            max_amount: "0x0",
            max_price_per_unit: "0x0",
          },
          l1_gas: {
            max_amount: "0x2f10",
            max_price_per_unit: "0x22ecb25c00",
          },
        },
      });
      console.log("transaction_hash", transaction_hash);
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/05-execute-tx.js
    

    Remove the P256 Validator Module

    You can use removeModule and isModule to remove the module from the account with the script below:

    // file src/05-remove-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./05-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const { transaction_hash } = await account.removeModule(
        classHash(moduleClassNames.P256Validator)
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      const isInstalled = await account.isModule(
        classHash(moduleClassNames.P256Validator)
      );
      console.log(
        "module",
        classHash(moduleClassNames.P256Validator),
        "is installed:",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/05-remove-module.js
    

    Using the P256 Validator as the Core Validator

    You can also use the P256 Validator as a Core Validator for the account. For that purpose you will deploy a new account and use the P256Signer to validate the account deployment without registering the P256Module in the SmartrAccount. In order to proceed you need to:

    • Generate the public key as an Array of felt252
    • Compute the account address
    • Send ETH to the modular account address
    • Deploy the Account

    Compute the Public Key as an Array of felt252

    The P256Signer helps to generate the public key from the private key. Once you have the public key you should slice to get an array of 4 pieces like below:

    const p236SmartrSigner = new P256Signer(p256PrivateKey);
    const publicKey = await p236SmartrSigner.getPubKey();
    const coords = publicKey.slice(2, publicKey.length);
    const x = coords.slice(0, 64);
    const x_felts = cairo.uint256(`0x${x}`);
    const y = coords.slice(64, 128);
    const y_felts = cairo.uint256(`0x${y}`);
    const publicKeyArray = [
      x_felts.low.toString(),
      x_felts.high.toString(),
      y_felts.low.toString(),
      y_felts.high.toString(),
    ];
    

    Compute the Account Address

    Once you have the public key, you should use the accountAddress function from @0xknwn/starknet-modular-account to compute the address of the account you will install. As a Salt, we will use the hash.computeHashOnElements from the public key like below:

    const publicKeyHash = hash.computeHashOnElements(publicKeyArray);
    const computedAccountAddress = accountAddress(
      "SmartrAccount",
      publicKeyHash,
      [moduleClassHash("P256Validator"), "0x4", ...publicKeyArray]
    );
    

    Note: The "0x4" that is inserted in the calldata is here to indicate there are 4 pieces to the public key:

    Send ETH to the SmartrAccount Address to deploy it

    To deploy the account, you need to have ETH associated with the target account address. Assuming you have access to an account with ETH, this is how you send p256 to the computedAccountAddress:

    const account = new SmartrAccount(
      provider,
      ozAccountAddress,
      smartrAccountPrivateKey
    );
    const ETH = new Contract(ERC20ABI, ethAddress, account);
    const initial_EthTransfer = cairo.uint256(5n * 10n ** 15n);
    const call = ETH.populate("transfer", {
      recipient: computedAccountAddress,
      amount: initial_EthTransfer,
    });
    const { transaction_hash } = await account.execute(call);
    const output = await account.waitForTransaction(transaction_hash);
    

    Deploy the Account with the P256 Validator as Core

    To deploy the account, you will need to use the deployAccount helper function from @0xknwn/starknet-modular-account with a SmartrAccount that has been instantiated with a P256Signer like below:

    const p256Account = new SmartrAccount(
      provider,
      computedAccountAddress,
      p236SmartrSigner
    );
    const address = await deployAccount(
      p256Account,
      "SmartrAccount",
      publicKeyHash,
      [moduleClassHash("P256Validator"), "0x4", ...publicKeyArray]
    );
    

    The Script Code

    You will find below the whole script that does the account deployment:

    // file src/05-deploy-account.ts
    import { RpcProvider, Contract, cairo, hash, Account } from "starknet";
    import {
      accountAddress,
      deployAccount,
      SmartrAccount,
      classNames as accountClassNames,
    } from "@0xknwn/starknet-modular-account";
    import {
      classHash as P256ClassHash,
      P256Signer,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    import { init } from "./05-init";
    import { ABI as ERC20ABI } from "./abi/ERC20";
    const strkAddress =
      "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    const p256PrivateKey =
      "0x1efecf7ee1e25bb87098baf2aaab0406167aae0d5ea9ba0d31404bf01886bd0e";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { ozAccountAddress, ozAccountPrivateKey } = await init();
    
      // Step 1 - Get the public key from the Eth Signer
      const p236SmartrSigner = new P256Signer(p256PrivateKey);
      const publicKey = await p236SmartrSigner.getPubKey();
      const coords = publicKey.slice(2, publicKey.length);
      const x = coords.slice(0, 64);
      const x_felts = cairo.uint256(`0x${x}`);
      const y = coords.slice(64, 128);
      const y_felts = cairo.uint256(`0x${y}`);
      const publicKeyArray = [
        x_felts.low.toString(),
        x_felts.high.toString(),
        y_felts.low.toString(),
        y_felts.high.toString(),
      ];
    
      // Step 2 - Compute the account address
      const publicKeyHash = hash.computeHashOnElements(publicKeyArray);
      const computedAccountAddress = accountAddress(
        accountClassNames.SmartrAccount,
        publicKeyHash,
        [P256ClassHash(moduleClassNames.P256Validator), "0x4", ...publicKeyArray]
      );
    
      // Step 3 - Send STRK to the computed account address
      const account = new Account(
        provider,
        ozAccountAddress,
        ozAccountPrivateKey,
        "1",
        "0x3"
      );
      const STRK = new Contract(ERC20ABI, strkAddress, account);
      const initial_strkTransfer = cairo.uint256(50000n * 10n ** 15n);
      const { transaction_hash } = await STRK.transfer(
        computedAccountAddress,
        initial_strkTransfer
      );
      const output = await account.waitForTransaction(transaction_hash);
      if (!output.isSuccess()) {
        throw new Error("Could not send STRK to the expected address");
      }
    
      // Step 4 - Deploy the account with the P256Validator as Core Validator
      const p256Account = new SmartrAccount(
        provider,
        computedAccountAddress,
        p236SmartrSigner,
        undefined,
        "1",
        "0x3"
      );
      const address = await deployAccount(
        p256Account,
        accountClassNames.SmartrAccount,
        publicKeyHash,
        [P256ClassHash(moduleClassNames.P256Validator), "0x4", ...publicKeyArray],
        {
          version: "0x3",
          resourceBounds: {
            l2_gas: {
              max_amount: "0x0",
              max_price_per_unit: "0x0",
            },
            l1_gas: {
              max_amount: "0x2f10",
              max_price_per_unit: "0x22ecb25c00",
            },
          },
        }
      );
      if (address !== computedAccountAddress) {
        throw new Error(
          `The account should have been deployed to ${computedAccountAddress}, instead ${address}`
        );
      }
      console.log("accountAddress", computedAccountAddress);
      console.log("public key", publicKeyArray);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run it:

    npx tsc --build
    
    node dist/05-deploy-account.js
    

    Running a transaction with the P256 Validator as Core

    Running a transaction with the P256Validator as a Core is no more complex than running a transaction on a regular account. All you need to do is

    • get the account address that could have been saved from earlier
    • instantiate the SmartrAccount with the Starknet.js P256Signer
    • execute the transaction

    Below is an example that assumes you have deployed the account with the 05-deploy-account.ts script earlier:

    // file src/05-execute-tx-core.ts
    import {
      SmartrAccount,
      accountAddress,
      classNames as accountlassNames,
    } from "@0xknwn/starknet-modular-account";
    import { init, CounterABI } from "./05-init";
    import { RpcProvider, Contract, cairo, hash } from "starknet";
    import {
      classHash as moduleClassHash,
      P256Signer,
      classNames as moduleClassNames,
    } from "@0xknwn/starknet-module";
    const providerURL = "http://127.0.0.1:5050/rpc";
    const p256PrivateKey =
      "0x1efecf7ee1e25bb87098baf2aaab0406167aae0d5ea9ba0d31404bf01886bd0e";
    
    const main = async () => {
      // recompute the account address
      const signer = new P256Signer(p256PrivateKey);
      const publicKey = await signer.getPubKey();
      const coords = publicKey.slice(2, publicKey.length);
      const x = coords.slice(0, 64);
      const x_felts = cairo.uint256(`0x${x}`);
      const y = coords.slice(64, 128);
      const y_felts = cairo.uint256(`0x${y}`);
      const publicKeyArray = [
        x_felts.low.toString(),
        x_felts.high.toString(),
        y_felts.low.toString(),
        y_felts.high.toString(),
      ];
    
      const publicKeyHash = hash.computeHashOnElements(publicKeyArray);
      const computedAccountAddress = accountAddress(
        accountlassNames.SmartrAccount,
        publicKeyHash,
        [moduleClassHash(moduleClassNames.P256Validator), "0x4", ...publicKeyArray]
      );
    
      // execute the transaction
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { counterAddress } = await init();
      const account = new SmartrAccount(
        provider,
        computedAccountAddress,
        signer,
        undefined,
        "1",
        "0x3"
      );
      const counter = new Contract(CounterABI, counterAddress, account);
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await account.execute(call, {
        version: "0x3",
        resourceBounds: {
          l2_gas: {
            max_amount: "0x0",
            max_price_per_unit: "0x0",
          },
          l1_gas: {
            max_amount: "0x2f10",
            max_price_per_unit: "0x22ecb25c00",
          },
        },
      });
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run it:

    npx tsc --build
    
    node dist/05-execute-tx-core.js
    

    As you can see from the script:

    • You do not need the P256Module to interact with the account. That is because the validator is used as a Core Validator and, as such, the transaction does not required to be prefixed
    • Running transactions is the same as running a transaction with the Stark validator. Only the signature changes!

    Using the SessionKey Validator

    The starknet modular account comes with the @0xknwn/starknet-module-sessionkey SDK that you can use to work with the sessionkey validator. In this section, you will see, how to install and use the validator, how to request a sessionkey and how to use it.

    Note: This section assumes the SmartrAccount class has been instantiated in the smartrAccount variable as shown in Using the modular account from the SDK. It also assumes the Counter contract that comes with the project has been deploys to the counterAddress and the CounterABI class is available. The 06-setup.ts script that comes with this project ensure those steps are executed.

    Install the SessionKey Validator

    @0xknwn/starknet-module-sessionkey is a SDK that complements @0xknwn/starknet-modular-account and provide the tools to manage the module and interact with it from the SmartrAccount. Start by adding the module to the project with a command like the one below:

    npm install --save @0xknwn/starknet-module-sessionkey
    

    If you are working on a network that does not have the sessionkey module class declared already, like the devnet, you should declare the class. The declareClass function that comes with the SDK allow such an installation. Below is an example of a script that install the module class:

    // file src/06-declare-class.ts
    import { RpcProvider, Account } from "starknet";
    import {
      declareClass,
      classNames as sessionkeyClassNames,
    } from "@0xknwn/starknet-module-sessionkey";
    
    // these are the settings for the devnet with --seed=0
    // change them to mee your requirements
    const providerURL = "http://127.0.0.1:5050/rpc";
    const ozAccountAddress =
      "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691";
    const ozPrivateKey = "0x71d7bb07b9a64f6f78ac4c816aff4da9";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const account = new Account(
        provider,
        ozAccountAddress,
        ozPrivateKey,
        "1",
        "0x3"
      );
    
      const { classHash: sessionkeyValidatorClassHash } = await declareClass(
        account,
        sessionkeyClassNames.SessionKeyValidator
      );
      console.log("SessionKeyValidator class hash:", sessionkeyValidatorClassHash);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Note: To declare the class, the account you use must be loaded with ETH. In order to do it, you can use any account, including the modular account.

    Transpile it and run the script with the commands below:

    npx tsc --build
    
    node dist/06-declare-class.js
    

    Note: only the declareClass function from @0xknwn/starknet-module-sessionkey can declare the Sessionkey Validator class. If you are using the declareClass function from @0xknwn/starknet-modular-account, you will be able to declare the Stark Validator and the Modular Account class but not the SessionKey Validator.

    Register the SessionKey Validator as a Module

    The modular account SDK comes with the addModule, removeModule and isModule. You can use those 3 functions to manage the module in the account once it has been declared to the network. To register the module in the account, use addModule:

    // file src/06-add-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import { classHash, classNames as sessionkeyClassNames } from "@0xknwn/starknet-module-sessionkey";
    import { init } from "./06-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const { transaction_hash } = await account.addModule(
        classHash(sessionkeyClassNames.SessionKeyValidator)
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      const isInstalled = await account.isModule(classHash(sessionkeyClassNames.SessionKeyValidator));
      console.log(
        "module",
        classHash(sessionkeyClassNames.SessionKeyValidator),
        "is installed",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/06-add-module.js
    

    Requesting a Session Key

    Once the module is installed in the account, a dapp can request a grant associated with a private key. In order to get that authorization, it must first build a session key request. A request is made of the following elements:

    • the account address. A session key is granted to an account and cannot be used with another one.
    • the sessionkey validator module class hash. Because an account could have several sessionkey module installed, we want to make sure the session key is granted to a very specific module.
    • the core validator class hash. Here we want to make sure the sessionkey grantor is the core validator for the account to prevent checking a signature with another module from the account
    • the dapp public key that is associated with the session key request. Once the session key is authorized, the dapp will only be able to sign transactions with its private key counterpart.
    • an expiration time. Here again, we want the session key to be limited in time and we strongly suggest the signer keep that period short.
    • an array of policies, i.e a list of (1) contract addresses and (2) entrypoints the dapp will be able to call with that session key. Keep in mind that many contracts are upgradeable and that a good practice would be to grant an access only to a contract that is opensource, audited and not upgradeable.
    • the chain id. This is something that might be remove in a later version of the session key because the account address already depends of the chain id. However, this element is mandatory to request a sessionkey grant

    Create and Manage Policies

    Policies are very specific to session keys and as such, the sessionkey SDK, i.e @0xknwn/starknet-module-sessionkey provides a class named PolicyManager that helps to manage them. There are 2 main reasons to use that class:

    • When requesting a sessionkey. That is because you do not use the array of policies but a hash of each one of them and a merkle tree of those hashes. The PolicyManager that comes with the SDK provides you with that Merkle tree root that is what is used to request the sessionkey grant.
    • When signing a transaction with the session key. That is because when you sign a transaction, not only you need to provide a valid signature of the transaction with the private key that has been granted the access but you also need to provide the merkle proofs that the call that are used in the transaction are part of the sessionkey policies.

    So assuming you want to grant an access to the increment and increment_by entrypoints of the Counter class and assuming counterAddress contains counter address, to use the PolicyManager class, you will call its constructor like below:

    const policyManager = new PolicyManager([
      { contractAddress: counterContract.address, selector: "increment" },
      { contractAddress: counterContract.address, selector: "increment_by" },
    ]);
    

    The policyManager.getRoot() function will return the root of the merkle tree associated with the policies.

    Note: The second useful function of PolicyManager is the getProof(policy) function. However, once the session key module registered in SmartrAccount that function is used by the account and you should not use it by yourself.

    Create a SessionKeyModule

    The SessionKeyModule is an implementation of a module that can be passed to the SmartrAccount and manages the decoration of the transaction under the hood. To fully instantiate that module, you will need:

    • all the data above
    • to request/get an authorization from the core validator signer
    • to add the signature provided by the grantor back into the module

    The first step consists in instantiating the SessionKeyModule with all the data associated with the session key:

    const sessionKeyModule = new SessionKeyModule(
      sessionkeyPublicKey,
      accountAddress,
      sessionkeyClassHash,
      chain,
      expires,
      policyManager
    );
    

    Request a Grant by the Account Core Validator Signer

    To request the authorization you should call the request method on the module with the starkValidatorClassHash like below:

    const request = await sessionKeyModule.request(starkValidatorClassHash);
    

    Note: this step is very important because it stores the starkValidatorClassHash in the module.

    Get the Session Key Grant

    Now, you need to use the core validator signer to provide the signature that will be necessary for the module to be activated. The sessionkey SDK provides the SessionKeyGrantor to help you with signing the request. To generate the signature, run the sign method on it:

    const grantor = new SessionKeyGrantor(
      starkValidatorClassHash,
      smartrAccountPrivateKey
    );
    const signature = await grantor.sign(sessionKeyModule);
    

    Register the Core Validator Signature with the SessionKeyModule

    To finish with the module configuration, you just have to add the signature to it like below:

    sessionKeyModule.add_signature(signature);
    

    Create an Account with the SessionKeyModule

    Now you can create an Account with the SmartrAccount class. The class constructor can use a module as a 4th argument of the constructor like below:

    const smartrAccountWithSessionKey = new SmartrAccount(
      provider,
      accountAddress,
      sessionkeyPrivateKey,
      sessionKeyModule
    );
    

    Note: here you are not using the private key from the core validator signer but the private key generated by your dapp.

    Source Code

    The overall sessionkey request/grant process with the execution of a transaction on the counter contract is available as a single script below:

    // file src/06-sessionkey-transaction.ts
    import {
      SmartrAccount,
      classHash,
      classNames as accountClassNames,
    } from "@0xknwn/starknet-modular-account";
    import {
      classHash as sessionkeyClassHash,
      PolicyManager,
      SessionKeyModule,
      SessionKeyGrantor,
      classNames as sessionkeyClassNames,
    } from "@0xknwn/starknet-module-sessionkey";
    import { init, CounterABI } from "./06-init";
    import { RpcProvider, Signer, Contract } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const sessionkeyPrivateKey = "0x4";
    
    const main = async () => {
      const { counterAddress, smartrAccountPrivateKey, accountAddress } =
        await init();
      const provider = new RpcProvider({ nodeUrl: providerURL });
    
      // Step 1: Collect all the necessary information to request a sessionkey
      // Authorization to the modular account signers
      const signer = new Signer(sessionkeyPrivateKey);
      const sessionkeyPublicKey = await signer.getPubKey();
    
      const chain = await provider.getChainId();
    
      const expires = BigInt(Math.floor(Date.now() / 1000) + 24 * 60 * 60); // 24 hours from now
    
      const policyManager = new PolicyManager([
        { contractAddress: counterAddress, selector: "increment" },
        { contractAddress: counterAddress, selector: "increment_by" },
      ]);
    
      // Step 2: Create a session key module that can be used to request a session
      // key and to create a SmartrAccount with the signed session key that can
      // execute transactions as any regular Account
      const sessionKeyModule = new SessionKeyModule(
        sessionkeyPublicKey,
        accountAddress,
        sessionkeyClassHash(sessionkeyClassNames.SessionKeyValidator),
        chain,
        `0x${expires.toString(16)}`,
        policyManager
      );
    
      // Step 3: Generate the sessionkey grant request
      // that is an important step to request a session key because that is when
      // the core validator class is registered with the session key module
      const request = await sessionKeyModule.request(
        classHash(accountClassNames.StarkValidator)
      );
      console.log("request", request);
    
      // Step 4: Use the SessionKeyGrantor helper class to sign the request
      const grantor = new SessionKeyGrantor(
        classHash(accountClassNames.StarkValidator),
        smartrAccountPrivateKey
      );
      const signature = await grantor.sign(sessionKeyModule);
    
      // Step 5: Register the signatures to the session key module
      sessionKeyModule.add_signature(signature);
    
      // Step 6: Create the SmartrAccount with the session key module
      const smartrAccountWithSessionKey = new SmartrAccount(
        provider,
        accountAddress,
        sessionkeyPrivateKey,
        sessionKeyModule,
        "1",
        "0x3"
      );
    
      // Step 7: Execute transactions with the session key module
      const counter = new Contract(
        CounterABI,
        counterAddress,
        smartrAccountWithSessionKey
      );
      let currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
      const call = counter.populate("increment");
      const { transaction_hash } = await smartrAccountWithSessionKey.execute(call);
      const receipt =
        await smartrAccountWithSessionKey.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
      currentCounter = await counter.call("get");
      console.log("currentCounter", currentCounter);
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run it:

    npx tsc --build
    
    node dist/06-sessionkey-transaction.js
    

    Execute a Transaction with the Session Key

    As you can see from above, executing a transaction with a session key is exactly the same as executing any transaction with a starknet.js account. All you need to do is to use the SmartrAccount that comes with the @0xknwn/starknet-modular-account SDK with the SessionKeyModule from the @0xknwn/starknet-module-sessionkey SDK.

    Invalidate the Session Key

    If needed, you can block a session key. To proceed, you would need the hash of the session key as shown in the request. That is the hash that is actually signed by the core validator signer and stored in sessionkeyHash in the script. Then, you can use disable_session_key entrypoint on the module with the executeOnModule entrypoint of the account:

    // file src/06-block-sessionkey.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash as sessionkeyClassHash,
      SessionKeyValidatorABI,
      classNames as sessionkeyClassNames,
    } from "@0xknwn/starknet-module-sessionkey";
    import { init } from "./06-init";
    import { CallData, RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    const sessionkeyHash = "0x7";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
    
      const moduleCallData = new CallData(SessionKeyValidatorABI);
      const calldata = moduleCallData.compile("disable_session_key", {
        sessionkey: sessionkeyHash,
      });
      const { transaction_hash } = await account.executeOnModule(
        sessionkeyClassHash(sessionkeyClassNames.SessionKeyValidator),
        "disable_session_key",
        calldata
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script:

    npx tsc --build
    
    node dist/06-block-sessionkey.js
    

    Remove the SessionKey Validator Module from the Account

    To remove the module from the account, use removeModule:

    // file src/06-remove-module.ts
    import { SmartrAccount } from "@0xknwn/starknet-modular-account";
    import {
      classHash,
      classNames as sessionkeyClassNames,
    } from "@0xknwn/starknet-module-sessionkey";
    import { init } from "./06-init";
    import { RpcProvider } from "starknet";
    
    const providerURL = "http://127.0.0.1:5050/rpc";
    
    const main = async () => {
      const provider = new RpcProvider({ nodeUrl: providerURL });
      const { accountAddress, smartrAccountPrivateKey } = await init();
      const account = new SmartrAccount(
        provider,
        accountAddress,
        smartrAccountPrivateKey,
        undefined,
        "1",
        "0x3"
      );
      const { transaction_hash } = await account.removeModule(
        classHash(sessionkeyClassNames.SessionKeyValidator)
      );
      const receipt = await account.waitForTransaction(transaction_hash);
      console.log("transaction succeeded", receipt.isSuccess());
    
      const isInstalled = await account.isModule(
        classHash(sessionkeyClassNames.SessionKeyValidator)
      );
      console.log(
        "module",
        classHash(sessionkeyClassNames.SessionKeyValidator),
        "has been removed",
        isInstalled
      );
    };
    
    main()
      .then(() => {})
      .catch((e) => {
        console.warn(e);
      });
    

    Transpile and run the script with the commands below:

    npx tsc --build
    
    node dist/06-remove-module.js
    

    Modules

    Starknet Modular Account requires modules to work properly. There are 2 types of modules:

    • Validator Modules, including the Core Validator Module are used to validate transactions
    • Executor Modules are not yet available. They enable to change the account behavior: you can create metadata to track the signer complies to some specific rules; you can add checks that are not related to the signature like the fact that an amount allowed to a signer is not already spent.

    This section details module development and specific implementations. In particular, the folllowing are explained.

    If you just plan to use the Starknet Modular Account with those modules, we suggest you check the SDKs sections of the documentation.

    Developing your Own Validator Module

    A Validator Module can be used to change the way the Modular Account validates transactions and messages. Validators can be used as the Core Validator, i.e. they validate the transaction on the account by default. If not, they require the transaction to be decorated with a prefix call to the __module_validate__ entrypoint.

    This section provides directions about how to develop and test your own module. If you are interested to extend the account with Validators, we suggest you contact the project to get some help.

    Validator Module

    A Validator is a class that implement the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IValidator<TState> {
        fn validate(self: @TState, grantor_class: ClassHash, calls: Array<Call>) -> felt252;
    }
    }

    validate generated the hash for a transaction and validates the transaction for the account. Opposite to is_valid_signature, validate has access to the whole transaction and, as such should always be fully implemented by any validator module.

    Note: the grantor class that is passed by the account is the Core Validator class hash registered with the account. It can be used for some specific use. For instance, in the case of the sessionkey validator, it enables the sessionkey validator module to validate the sessionkey authorization with the is_valid_signature from the Core Validator of the account.

    Core Validator Interface

    In addition to the IValidator interface, Core Validator Modules must implement the ICoreValidator interface. That is because the module has to configure the account public key when the accounted is created the first time

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait ICoreValidator<TState> {
        fn is_valid_signature(self: @TState, hash: Array<felt252>, signature: Array<felt252>) -> felt252;
        fn initialize(ref self: TState, public_key: Array<felt252>);
    }
    }
    • is_valid_signature, given the hash that can be a transaction hash or a message hash should return starknet::VALIDATED is the signature is valid and 0 if not. Depending on the case, it can be that the function cannot be implemented. If that is the case, we suggest you return that the transaction is not valid
    • initialize is used at the installation time of the account to store the first account public key. The reason the public_key is an Array<felt252> is to be able to initialize scheme where the public key requires more than one felt252.

    Management Interface and Mappers

    Each Validator Module can provide some management functions to configure the module. The name and implementation of those functions depend on the module.

    The management interface cannot be call on a class, they must be triggered on a contract. To workaround that issue, the account provides 2 entrypoints execute_on_module and call_on_module that can call the management interface from the account. The execute_on_module provides some additional security making sure that only the account signers can initiate those calls.

    To allow the remove access between the account calls and the management interface the validator requires the call and execute methods to be implemented. Those methods map the input and output arguments of the management function. The associated interface looks like:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IConfigure<TState> {
        fn call(self: @TState, call: Call) -> Array<felt252>;
        fn execute(ref self: TState, call: Call) -> Array<felt252>;
    }
    }

    Note: To work the Call should include the following:

    • selector must be the selector for the management interface, i.e. the sn_keccak of the entrypoint name
    • to should be the account address
    • calldata should be the call data as defined by the ABI of the class

    Version Interface

    It is recommended to implement the IVersion interface in the module and add them to the mappers as describe above. These interface helps the users to identify the validators, even if eventually, the class hash remains the only real identifier:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IVersion<TState> {
        fn get_version(self: @TState) -> felt252;
        fn get_name(self: @TState) -> felt252;
    }
    }

    Other considerations

    In addition to the feature above, we suggest a number of good practices to develop modules

    • Be very careful with Storage variable names as they can be accessed by another module. A good practice is to prefix those names with a prefix that is uniquely derived from the module.
    • Use the account notify_owner_addition and notify_owner_removal functions that are part of the internal implementation of the account to notify users of the addition/removal of public keys
    • Add a Typescript/Javascript module SDK to the Modular Account SDK. That would allow applications to easily use your module and help with tests.
    • Provide a set of tests to make sure the module is behaving correctly and so that people can understand how it is supposed to work
    • Write a documentation both about the module and the SDK.

    The Stark Validator Module

    The Stark Validator Module is an implementation of a Validator Module for the Starknet Modular Account. It must be used as the Core Validator for the Account. This document explains the features, the configuration and some of the Internals of this module.

    Validator Module

    A validator is a contract that implement the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    trait IValidator<TState> {
        fn validate(self: @TState, grantor_class: ClassHash, calls: Array<Call>) -> felt252;
    }
    }

    validate is used to validate a transaction on the account. It

    • gets the hash for the current transaction from the network
    • use is_valid_signature to check the signature is valid

    Note: the grantor class that is passed by the account is the Core Validator class hash registered with the account. In the case of the Stark Validator it is the module class hash. The validator does not use that parameter.

    Core Validator Interface

    In addition to the IValidator interface, The Stark Validator Module implements the ICoreValidator interface. That is because the Stark Validator can be installed as a Core Validator Module, i.e. the default Validator for the account. The interface looks like this:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait ICoreValidator<TState> {
        fn is_valid_signature(self: @TState, hash: Hash<felt252>, signature: Array<felt252>) -> felt252;
        fn initialize(ref self: TState, public_key: Array<felt252>);
    }
    }

    In the case of the Stark Validator the 2 functionsare:

    • is_valid_signature. It checks a hash of a transaction or a hash of a message matches the account public keys of the current configurationm i.e stored in the account storage:
      • It checks the elements of the signature are valid considering the public keys registered in the account
      • It checks the number of valid signature matches the threshold defines in the account.
    • initialize is used at the installation time of the account to store the first account public key. In the case of the Stark Validator, the public key can be managed in a single felt so you can just use an array of one, i.e. array![publicKey] in cairo or [publicKey] in Typescript/Javascript.

    Note: In the case of the Stark Validator the key is simply stored in the Account_public_keys storage. It is also stored in the Account_public_key so that we can downgrade the account back to an OpenZeppelin Account.

    Management Interface

    Each Validator Module can provide some management entrypoint to configure the module. In the case of the Stark Validator, the management methods are:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IPublicKeys<TState> {
        fn add_public_key(ref self: TState, new_public_key: felt252);
        fn get_public_keys(self: @TState) -> Array<felt252>;
        fn get_threshold(self: @TState) -> u8;
        fn remove_public_key(ref self: TState, old_public_key: felt252);
        fn set_threshold(ref self: TState, new_threshold: u8);
    }
    }

    As you can assess by their name:

    • add_public_key adds a public key into the account. Be careful that it does not remove the existing key that must be managed separately.
    • remove_public_key removes a public key from the account.
    • get_public_keys returns the list of current public keys registered with the account
    • set_threshold defines the number of signer that must sign a transaction or message for the signature to be valid
    • get_threshold list the current threshold of the account.

    Version Interface

    The Stark Validator implements the IVersion interface below:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IVersion<TState> {
        fn get_version(self: @TState) -> felt252;
        fn get_name(self: @TState) -> felt252;
    }
    }
    • get_name() returns stark-validator in a shortString
    • get_version() returns the version starting with a v, like v0.1.8 as a short string.

    Module Management Interface Mappers

    The management interface cannot be call on a class, they must be triggered on a contract. To workaround that issue, the account provides 2 entrypoints execute_on_module and call_on_module that can call the management interface from the account. The execute_on_module provides some additional security making sure that only the account signers can initiate those calls.

    To allow the remove access between the account calls and the management interface the validator requires the call and execute method are implemented and does map the input and output arguments correctly. That is the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IConfigure<TState> {
        fn call(self: @TState, call: Call) -> Array<felt252>;
        fn execute(ref self: TState, call: Call) -> Array<felt252>;
    }
    }

    In the case of the Stark Validator:

    • call can takes calls to get_public_keys, get_name, get_version and get_threshold.
    • execute execute calls to add_public_key, remove_public_keys and set_threshold.

    Note: To work the Call should include the following:

    • selector must be the selector for the management interface, i.e. the sn_keccak of the entrypoint name
    • to should be the account address
    • calldata should be the call data as defined by the ABI of the class

    The ETH and P256 Validator Module

    The Eth and P256 Validator Modules are implementations of a Validator Module for the Starknet Modular Account. They can both be used as the Core Validator or as a secondary Validator for the Account. This document explains the features, the configuration and some of the Internals of these modules.

    Validator Module

    A validator is a contract that implement the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    trait IValidator<TState> {
        fn validate(self: @TState, grantor_class: ClassHash, calls: Array<Call>) -> felt252;
    }
    }

    validate is used to validate a transaction on the account. It

    • gets the hash for the current transaction from the network
    • use is_valid_signature to check the signature is valid

    Note: the grantor class that is passed by the account is the Core Validator class hash registered with the account. In the case of the Eth and P256 Validator it is the module class hash. The validator does not use that parameter for now.

    Core Validator Interface

    In addition to the IValidator interface, The Eth and P256 Validator Modules implement the ICoreValidator interface. That is because they can be installed as a Core Validator Modules, i.e. the default Validator for the account. The interface looks like this:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait ICoreValidator<TState> {
        fn is_valid_signature(self: @TState, hash: Hash<felt252>, signature: Array<felt252>) -> felt252;
        fn initialize(ref self: TState, public_key: Array<felt252>);
    }
    }

    In the case of these Validators, the 2 functions are:

    • is_valid_signature. It checks a hash of a transaction or a hash of a message matches the account public keys of the current configurationm i.e stored in the account storage. It checks the elements of the signature are valid considering the public keys registered in the account
    • initialize is used at the installation time of the account to store the first account public key. In the case of the Eth and P256 Validator, the public key is managed by an array of 4 felt.

    Note: The downgrade from the account back to an OpenZeppelin Account as not been tested.

    Management Interface

    Each Validator Module can provide some management entrypoint to configure the module. The management methods for the 2 validators are:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IPublicKeys<TState> {
        fn set_public_key(ref self: TState, new_public_key: Array<felt252>);
        fn get_public_key(self: @TState) -> Array<felt252>;
    }
    }

    As you can assess by their name:

    • set_public_key changes the public key associated with the account. Be careful that it removes the existing key
    • get_public_key returns the elements of current public keys registered with the account

    Version Interface

    The Validator implements the IVersion interface below:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IVersion<TState> {
        fn get_version(self: @TState) -> felt252;
        fn get_name(self: @TState) -> felt252;
    }
    }
    • get_name() returns eth-validator for the ETH validator in a shortString. It returns p256-validator for the ETH validator in a shortString.
    • get_version() returns the version starting with a v, like v0.1.8 as a short string.

    Module Management Interface Mappers

    The management interface cannot be call on a class, they must be triggered on a contract. To workaround that issue, the account provides 2 entrypoints execute_on_module and call_on_module that can call the management interface from the account. The execute_on_module provides some additional security making sure that only the account signers can initiate those calls.

    To allow the remove access between the account calls and the management interface the validator requires the call and execute method are implemented and does map the input and output arguments correctly. That is the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IConfigure<TState> {
        fn call(self: @TState, call: Call) -> Array<felt252>;
        fn execute(ref self: TState, call: Call) -> Array<felt252>;
    }
    }

    In the case of the Eth Validator:

    • call can takes calls to get_public_key, get_name and get_version.
    • execute can execute calls to set_public_key.

    Note: To work the Call should include the following:

    • selector must be the selector for the management interface, i.e. the sn_keccak of the entrypoint name
    • to should be the account address
    • calldata should be the call data as defined by the ABI of the class and in the case of an EthPublicKey, it should be an array of 4 felts.

    The SessionKey Validator Module

    The SessionKey Validator Module is an implementation of a Validator Module for the Starknet Modular Account. It must be used as a secondary Validator for the Account and requires a Core Validator module to authorize the sessionkey. This document explains the features, the configuration and some of the Internals of this module.

    Note: To understand the process of requesting a session key authorization and signing the authorization, you should check the documentation for the Session Key Validator SDK

    Validator Module

    A validator is a contract that implement the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IValidator<TState> {
        fn validate(self: @TState, grantor_class: ClassHash, calls: Array<Call>) -> felt252;
    }
    }

    In the case of the sessionkey Validator the validate function checks the transaction signature is valid by:

    • making sure the session key has not been disabled with the module management interface.
    • extracting the session key data and making sure they are valid, signed by the core validator and still properly configured in the account.
    • check the transaction calls matches the policies associated with the session key by examining the merkle proofs of the signature and recomputing the merkle root with those proofs and the calls
    • check the transaction is signed by the private key that has been granted the access by the session key.

    Note: the grantor class that is passed by the account is the Core Validator class hash registered with the account. In the case of the SessionKey Validator it is used to check the session key authorization is actually valid with the Core Validator Signature.

    Core Validator Interface

    This module cannot be used as a Core Validator. That is because the session key needs to be authorized by the Core Validator Signer to be used. As a result, it does not implement the ICoreValidator interface. In addition, a message cannot be signed by a session key simply because the policy cannot apply to it and there is no way to check a transaction hash is valid without the calls. That is why the is_valid_signature cannot be implemented for that case.

    Management Interface

    Each Validator Module can provide some management entrypoint to configure the module. In the case of the Stark Validator, the management methods are:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IDisableSessionKey<TState> {
        fn disable_session_key(ref self: TState, sessionkey: felt252);
        fn is_disabled_session_key(self: @TState, sessionkey: felt252) -> bool;
    }
    }

    As you can assess by their name:

    • disable_session_key register the hash of the session key in the account to block a future attempt to use it again. Note that disabling is permanent and a session key must be re-emitted if necessary
    • is_disabled_session_key list if a session key has been registered with the disabled session keys

    Module Management Interface Mappers

    The management interface cannot be call on a class, they must be triggered on a contract. To workaround that issue, the account provides 2 entrypoints execute_on_module and call_on_module that can call the management interface from the account. The execute_on_module provides some additional security making sure that only the account signers can initiate those calls.

    To allow the remove access between the account calls and the management interface the validator requires the call and execute method are implemented and does map the input and output arguments correctly. That is the following interface:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IConfigure<TState> {
        fn call(self: @TState, call: Call) -> Array<felt252>;
        fn execute(ref self: TState, call: Call) -> Array<felt252>;
    }
    }

    In the case of the Stark Validator:

    • call takes calls to is_disabled_session_key and match the input/output on the account.
    • execute takes calls to disable_session_key and match the input/output on the account.

    Note: To work the Call should include the following:

    • selector must be the selector for the management interface, i.e. the sn_keccak of the entrypoint name
    • to should be the account address
    • calldata should be the call data as defined by the ABI of the class

    Version Interface

    The Session Key Validator implements the IVersion interface below:

    #![allow(unused)]
    fn main() {
    #[starknet::interface]
    pub trait IVersion<TState> {
        fn get_version(self: @TState) -> felt252;
        fn get_name(self: @TState) -> felt252;
    }
    }
    • get_name() returns sessionkey-validator in a shortString
    • get_version() returns the version starting with a v, like v0.1.8 as a short string.

    Roadmap and Known Issues

    The starknet modular account is a work-in-progress effort to provide features to dapps and provide better experience to Starknet. As such, there is a lot to work on and this section gives some ideas of the direction the project will take the coming weeks and months.

    Roadmap

    Ideas to enhance the account are legions. However, we plan to stay focus on a small set of features:

    1. Add support for the version 3 of the transaction as described in SNIP-8.
    2. Add an entrypoint to allow execute_from_outside with the signature from the core validator and some limitation as described in SNIP-9. This entrypoint would require to manage a user defined Nonce to prevent replay-scenarios. It would also battle test the model. The benefit from that entrypoint besides is that it allows a 3rd party to pay for your transaction. A consolation prize for the paymaster to not be implemented yet. For some implementation details:
    3. Develop a Guarded Validator. This would require it to be a core validators. It would provide more value for the modular account because the implementation requires some different signature depending on the calls so it would validate/invalidate the current set of interfaces.
    4. Move the Validator Module data from a prefix call to the account signature. This might not be feasible but it makes sense because the validation is supposed to rely on the signature.
    5. Add support for Executor Modules. Some scenario like the ability to delay the execution of a transaction or the ability to track some data like an amount of ERC20 dedicated to a 3rd party requires those modules to work.
    6. Ensure the compatibility between session key and offchain signature à la EIP-712 or SNIP-12.
    7. Check what can be done to support upgrades from OpenZeppelin, Argent and Braavo Accounts

    If you need support for another scenario, feel free to open an issue on Github.

    Known Issues

    In addition to the planned changes above, there are a number of smaller issues that have been identified and need to be addressed. Most of them are implementation details bit some of them might impact your usage. Those issues are the following:

    • Version the contracts for both accounts and modules and maintain a compatibility matrix. Actually we should check if we can rely on the boostrap experiment to have a more stable account address stability and introduce a contract that manages versions and allow to initiate some sort of registry.
    • Improve the upgrade management with the help of the bootstrap experiment. Right now the upgrade works because it is actually a downgrade and move back. In reality it will not pass a test of an upgrade with an Argent, Braavos or Openzeppelin Account.
    • Right now, type conversions are done manually with the core::traits::Into package. Improve it so that is it more readable and consistent with how other libraries like openzeppelin does.
    • Reduce, as much as possible the SDKs API surface to make it simpler to use for developers.
    • Associate class versions with class hash in the documentation and version the documentation so that we can keep track of the version history
    • It is possible to get a sessionkey that has all accesses on an account. We need to disable that feature to force dapps to requests what they want and for users to review policies.
    • For a better understanding of the sessionkey flow in the SDK we should sign directly the request and not the SessionKeyModule
    • Check when OpenZeppelin uses Poseidon Hash and see if that impacts the Eth module.
    • Fees are not computed correctly when a large part comes from the signature and as a result, the deploy_account has a fixed maxFee. Improve the computation and remove that fix value.
    • Check existing audits and see if the recommendations apply. If yes, apply them.

    SDK Reference Documentation

    This section provides the generated reference documentation for the Typescript/Javascript SDKs that comes with the project. For a better understanding of those SDKs, check the Using SDKs section.

    The 3 SDKs can be downloaded from npmjs.com.

    • @0xknwn/starknet-modular-account extends the starknet.js Account to support multiple signers and manage modules. It also provides the AccountModuleInterface that should be used by module SDKs. You can check the reference documentation for this SDK here
    • @0xknwn/starknet-module-sessionkey provides the SessionKeyModule that implements the AccountModuleInterface as well as tools to configure the sessionkey module, including the PolicyManager and the PolicyGrantor classes. You can check the reference documentation for this SDK here
    • @0xknwn/starknet-module provides the EthModule that implements the AccountModuleInterface. To sign transaction, you can simply use the Starknet.js EthSigner. You can check the reference documentation for this SDK here

    In addition, the project provides another SDK called @0xknwn/starknet-test-helpers that can be used to create helper classes outside of this repository. That project is used to help building demonstration and/or tests. In particular in includes the tools to 2 contracts named Counter and SwapRouter. You can check the reference documentation for this SDK here.

    @0xknwn/starknet-modular-account


    @0xknwn/starknet-modular-account

    Classes

    Interfaces

    @0xknwn/starknet-modular-account


    @0xknwn/starknet-modular-account / SmartrAccount

    Class: SmartrAccount

    Defined in: sdks/account/src/smartr_account.ts:71

    Represents a SmartrAccount.

    Extends

    • Account

    Constructors

    new SmartrAccount()

    new SmartrAccount(providerOrOptions, address, pkOrSigner, module, cairoVersion?, transactionVersion?): SmartrAccount

    Defined in: sdks/account/src/smartr_account.ts:76

    Parameters

    providerOrOptions

    ProviderInterface | ProviderOptions

    address

    string

    pkOrSigner

    string | Uint8Array | SignerInterface

    module

    undefined | AccountModuleInterface

    cairoVersion?

    CairoVersion

    transactionVersion?

    any = RPC.ETransactionVersion.V2

    Returns

    SmartrAccount

    Overrides

    Account.constructor

    Properties

    address

    address: string

    Defined in: node_modules/starknet/dist/index.d.ts:4416

    Inherited from

    Account.address


    cairoVersion

    cairoVersion: CairoVersion

    Defined in: node_modules/starknet/dist/index.d.ts:4417

    Inherited from

    Account.cairoVersion


    channel

    channel: RpcChannel | RpcChannel$1

    Defined in: node_modules/starknet/dist/index.d.ts:3386

    Inherited from

    Account.channel


    deploySelf()

    deploySelf: (__namedParameters, details?) => Promise<DeployContractResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4448

    Parameters

    __namedParameters

    DeployAccountContractPayload

    details?

    UniversalDetails

    Returns

    Promise<DeployContractResponse>

    Inherited from

    Account.deploySelf


    getStateUpdate()

    getStateUpdate: () => Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>(blockIdentifier) => Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>(blockIdentifier) => Promise<{ block_hash: string; new_root: string; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>(blockIdentifier?) => Promise<StateUpdateResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3423

    Gets the state changes in a specific block (result of executing the requested block)

    Returns

    Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Parameters

    blockIdentifier

    "pending"

    Returns

    Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Parameters

    blockIdentifier

    "latest"

    Returns

    Promise<{ block_hash: string; new_root: string; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Parameters

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<StateUpdateResponse>

    Param

    block identifier

    Returns

    StateUpdateResponse

    Inherited from

    Account.getStateUpdate


    module

    module: undefined | AccountModuleInterface

    Defined in: sdks/account/src/smartr_account.ts:74


    responseParser

    responseParser: RPCResponseParser

    Defined in: node_modules/starknet/dist/index.d.ts:3385

    Inherited from

    Account.responseParser


    signer

    signer: SignerInterface

    Defined in: node_modules/starknet/dist/index.d.ts:4415

    Inherited from

    Account.signer


    transactionVersion

    readonly transactionVersion: "0x3" | "0x2"

    Defined in: node_modules/starknet/dist/index.d.ts:4418

    Inherited from

    Account.transactionVersion

    Methods

    accountInvocationsFactory()

    accountInvocationsFactory(invocations, details): Promise<AccountInvocations>

    Defined in: node_modules/starknet/dist/index.d.ts:4553

    Parameters

    invocations

    Invocations

    details

    AccountInvocationsFactoryDetails

    Returns

    Promise<AccountInvocations>

    Inherited from

    Account.accountInvocationsFactory


    addModule()

    Call Signature

    addModule(class_hash, execute?): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:428

    Add a module to an account.

    Parameters
    class_hash

    string

    the module to add

    execute?

    true

    If true, the transaction is executed, otherwise it is built and returned so that it can be used in a multicall.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.

    Call Signature

    addModule(class_hash, execute): Promise<Call[]>

    Defined in: sdks/account/src/smartr_account.ts:432

    Add a module to an account.

    Parameters
    class_hash

    string

    the module to add

    execute

    false

    If true, the transaction is executed, otherwise it is built and returned so that it can be used in a multicall.

    Returns

    Promise<Call[]>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.


    buildAccountDeployPayload()

    buildAccountDeployPayload(__namedParameters, details): Promise<DeployAccountContractTransaction>

    Defined in: node_modules/starknet/dist/index.d.ts:4551

    Parameters

    __namedParameters

    DeployAccountContractPayload

    details

    InvocationsSignerDetails

    Returns

    Promise<DeployAccountContractTransaction>

    Inherited from

    Account.buildAccountDeployPayload


    buildDeclarePayload()

    buildDeclarePayload(payload, details): Promise<DeclareContractTransaction>

    Defined in: node_modules/starknet/dist/index.d.ts:4550

    Parameters

    payload

    DeclareContractPayload

    details

    InvocationsSignerDetails

    Returns

    Promise<DeclareContractTransaction>

    Inherited from

    Account.buildDeclarePayload


    buildInvocation()

    buildInvocation(call, details): Promise<Invocation>

    Defined in: node_modules/starknet/dist/index.d.ts:4549

    Parameters

    call

    Call[]

    details

    InvocationsSignerDetails

    Returns

    Promise<Invocation>

    Inherited from

    Account.buildInvocation


    buildUDCContractPayload()

    buildUDCContractPayload(payload): Call[]

    Defined in: node_modules/starknet/dist/index.d.ts:4552

    Parameters

    payload

    UniversalDeployerContractPayload | UniversalDeployerContractPayload[]

    Returns

    Call[]

    Inherited from

    Account.buildUDCContractPayload


    callContract()

    callContract(call, blockIdentifier?): Promise<string[]>

    Defined in: node_modules/starknet/dist/index.d.ts:3477

    Calls a function on the Starknet contract.

    Parameters

    call

    Call

    transaction to be called

    blockIdentifier?

    BlockIdentifier

    block identifier

    Returns

    Promise<string[]>

    the result of the function on the smart contract.

    Inherited from

    Account.callContract


    callOnModule()

    callOnModule(module_class_hash, module_entrypoint, calldata): Promise<BigNumberish[]>

    Defined in: sdks/account/src/smartr_account.ts:374

    Call an entrypoint on the module account.

    Parameters

    module_class_hash

    string

    The installed module class_hash.

    module_entrypoint

    string

    The module entrypoint to call.

    calldata

    string[]

    The entrypoint calldata.

    Returns

    Promise<BigNumberish[]>

    A promise that resolves to the call output.


    declare()

    declare(payload, details?): Promise<{ class_hash: string; transaction_hash: string; }>

    Defined in: node_modules/starknet/dist/index.d.ts:4444

    Declares a given compiled contract (json) to starknet

    Parameters

    payload

    DeclareContractPayload

    details?

    UniversalDetails

    Returns

    Promise<{ class_hash: string; transaction_hash: string; }>

    a confirmation of sending a transaction on the starknet contract

    Inherited from

    Account.declare


    declareAndDeploy()

    declareAndDeploy(payload, details?): Promise<DeclareDeployUDCResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4447

    Declares and Deploy a given compiled contract (json) to starknet using UDC Internal wait for L2 transaction, do not support multicall Method will pass even if contract is already declared (internal using DeclareIfNot)

    Parameters

    payload

    DeclareAndDeployContractPayload

    contract: compiled contract code

    • [casm=cairo1]: CairoAssembly | undefined;
    • [compiledClassHash]: string | undefined;
    • [classHash]: computed class hash of compiled contract
    • [constructorCalldata] contract constructor calldata
    • [salt=pseudorandom] deploy address salt
    • [unique=true] ensure unique salt
    details?

    UniversalDetails

    InvocationsDetails

    Returns

    Promise<DeclareDeployUDCResponse>

    • declare
      • transaction_hash
    • deploy
      • contract_address
      • transaction_hash
      • address
      • deployer
      • unique
      • classHash
      • calldata_len
      • calldata
      • salt

    Inherited from

    Account.declareAndDeploy


    declareContract()

    declareContract(transaction, details): Promise<DeclaredTransaction>

    Defined in: node_modules/starknet/dist/index.d.ts:3475

    Declares a given compiled contract (json) to starknet

    Parameters

    transaction

    DeclareContractTransaction

    transaction payload to be deployed containing:

    • compiled contract code
    • sender address
    • signature
    details

    InvocationsDetailsWithNonce

    Invocation Details containing:

    • nonce
    • optional version
    • optional maxFee

    Returns

    Promise<DeclaredTransaction>

    a confirmation of sending a transaction on the starknet contract

    Inherited from

    Account.declareContract


    declareIfNot()

    declareIfNot(payload, transactionsDetail?): Promise<{ class_hash: string; transaction_hash: string; }>

    Defined in: node_modules/starknet/dist/index.d.ts:4443

    First check if contract is already declared, if not declare it If contract already declared returned transaction_hash is ''. Method will pass even if contract is already declared

    Parameters

    payload

    DeclareContractPayload

    transactionsDetail?

    UniversalDetails

    (optional)

    Returns

    Promise<{ class_hash: string; transaction_hash: string; }>

    Inherited from

    Account.declareIfNot


    deploy()

    deploy(payload, details?): Promise<MultiDeployContractResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4445

    Deploys a declared contract to starknet - using Universal Deployer Contract (UDC) support multicall

    Parameters

    payload

    classHash: computed class hash of compiled contract

    • [constructorCalldata] contract constructor calldata
    • [salt=pseudorandom] deploy address salt
    • [unique=true] ensure unique salt

    UniversalDeployerContractPayload | UniversalDeployerContractPayload[]

    details?

    UniversalDetails

    InvocationsDetails

    Returns

    Promise<MultiDeployContractResponse>

    • contract_address[]
    • transaction_hash

    Inherited from

    Account.deploy


    deployAccount()

    deployAccount(contractPayload, details?): Promise<DeployContractResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4449

    Deploy the account on Starknet

    Parameters

    contractPayload

    DeployAccountContractPayload

    transaction payload to be deployed containing:

    • classHash: computed class hash of compiled contract
    • optional constructor calldata
    • optional address salt
    • optional contractAddress
    details?

    UniversalDetails

    Returns

    Promise<DeployContractResponse>

    a confirmation of sending a transaction on the starknet contract

    Inherited from

    Account.deployAccount


    deployAccountContract()

    deployAccountContract(transaction, details): Promise<DeployedAccountTransaction>

    Defined in: node_modules/starknet/dist/index.d.ts:3476

    Deploys a given compiled Account contract (json) to starknet

    Parameters

    transaction

    DeployAccountContractTransaction

    details

    InvocationsDetailsWithNonce

    Returns

    Promise<DeployedAccountTransaction>

    a confirmation of sending a transaction on the starknet contract

    Inherited from

    Account.deployAccountContract


    deployContract()

    deployContract(payload, details?): Promise<DeployContractUDCResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4446

    Simplify deploy simulating old DeployContract with same response + UDC specific response Internal wait for L2 transaction, support multicall

    Parameters

    payload

    classHash: computed class hash of compiled contract

    • [constructorCalldata] contract constructor calldata
    • [salt=pseudorandom] deploy address salt
    • [unique=true] ensure unique salt

    UniversalDeployerContractPayload | UniversalDeployerContractPayload[]

    details?

    UniversalDetails

    InvocationsDetails

    Returns

    Promise<DeployContractUDCResponse>

    • contract_address
    • transaction_hash
    • address
    • deployer
    • unique
    • classHash
    • calldata_len
    • calldata
    • salt

    Inherited from

    Account.deployContract


    estimateAccountDeployFee()

    estimateAccountDeployFee(contractPayload, details?): Promise<EstimateFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4431

    Estimate Fee for executing a DEPLOY_ACCOUNT transaction on starknet

    Parameters

    contractPayload

    DeployAccountContractPayload

    classHash - the class hash of the compiled contract.

    • constructorCalldata? - constructor data;
    • contractAddress? - future account contract address. Precalculate for faster execution.
    • addressSalt? - salt used for calculation of the contractAddress. Required if contractAddress is provided.
    details?

    UniversalDetails

    Returns

    Promise<EstimateFee>

    response from estimate_fee

    Inherited from

    Account.estimateAccountDeployFee


    estimateDeclareFee()

    estimateDeclareFee(payload, details?): Promise<EstimateFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4430

    Estimate Fee for executing a DECLARE transaction on starknet

    Parameters

    payload

    DeclareContractPayload

    details?

    UniversalDetails

    Returns

    Promise<EstimateFee>

    response from estimate_fee

    Inherited from

    Account.estimateDeclareFee


    estimateDeployFee()

    estimateDeployFee(payload, details?): Promise<EstimateFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4432

    Estimate Fee for executing a UDC DEPLOY transaction on starknet This is different from the normal DEPLOY transaction as it goes through the Universal Deployer Contract (UDC)

    Parameters

    payload

    UniversalDeployerContractPayload | UniversalDeployerContractPayload[]

    details?

    UniversalDetails

    Returns

    Promise<EstimateFee>

    Inherited from

    Account.estimateDeployFee


    estimateFee()

    estimateFee(calls, estimateFeeDetails?): Promise<EstimateFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4428

    Parameters

    calls

    AllowArray<Call>

    estimateFeeDetails?

    UniversalDetails

    Returns

    Promise<EstimateFee>

    Inherited from

    Account.estimateFee


    estimateFeeBulk()

    estimateFeeBulk(invocations, details?): Promise<EstimateFeeBulk>

    Defined in: node_modules/starknet/dist/index.d.ts:4433

    Estimate Fee for executing a list of transactions on starknet Contract must be deployed for fee estimation to be possible

    Parameters

    invocations

    Invocations

    array of transaction object containing :

    • type - the type of transaction : 'DECLARE' | (multi)'DEPLOY' | (multi)'INVOKE_FUNCTION' | 'DEPLOY_ACCOUNT'
    • payload - the payload of the transaction
    details?

    UniversalDetails

    blockIdentifier?

    • nonce?
    • skipValidate? - default true
    • tip? - prioritize order of transactions in the mempool.
    • accountDeploymentData? - deploy an account contract (substitution for deploy account transaction)
    • paymasterData? - entity other than the transaction sender to pay the transaction fees(EIP-4337)
    • nonceDataAvailabilityMode? - allows users to choose their preferred data availability mode (Volition)
    • feeDataAvailabilityMode? - allows users to choose their preferred data availability mode (Volition)
    • version? - specify ETransactionVersion - V3 Transactions fee is in fri, oldV transactions fee is in wei

    Returns

    Promise<EstimateFeeBulk>

    response from estimate_fee

    Inherited from

    Account.estimateFeeBulk


    estimateInvokeFee()

    estimateInvokeFee(calls, details?): Promise<EstimateFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4429

    Estimate Fee for executing an INVOKE transaction on starknet

    Parameters

    calls

    AllowArray<Call>

    the invocation object containing:

    • contractAddress - the address of the contract
    • entrypoint - the entrypoint of the contract
    • calldata? - (defaults to []) the calldata
    details?

    UniversalDetails

    Returns

    Promise<EstimateFee>

    response from estimate_fee

    Inherited from

    Account.estimateInvokeFee


    estimateMessageFee()

    estimateMessageFee(message, blockIdentifier?): Promise<FEE_ESTIMATE>

    Defined in: node_modules/starknet/dist/index.d.ts:3482

    NEW: Estimate the fee for a message from L1

    Parameters

    message

    MSG_FROM_L1

    Message From L1

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<FEE_ESTIMATE>

    Inherited from

    Account.estimateMessageFee


    execute()

    Call Signature

    execute(transactions, transactionsDetail?): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:236

    Executes a set of transactions on the StarkNet network.

    Parameters
    transactions

    AllowArray<Call>

    An array of transactions to be executed.

    transactionsDetail?

    UniversalDetails

    Optional object containing additional details for the transactions.

    Returns

    Promise<{ transaction_hash: string; }>

    A Promise that resolves to an InvokeFunctionResponse object representing the result of the execution.

    Overrides

    Account.execute

    Call Signature

    execute(transactions, abis?, transactionsDetail?): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:249

    Executes a set of transactions on the StarkNet network.

    Parameters
    transactions

    AllowArray<Call>

    An array of transactions to be executed.

    abis?

    Abi[]

    Optional argument that can be an array of ABIs.

    transactionsDetail?

    UniversalDetails

    Optional object containing additional details for the transactions.

    Returns

    Promise<{ transaction_hash: string; }>

    A Promise that resolves to an InvokeFunctionResponse object representing the result of the execution.

    Overrides

    Account.execute


    executeFromOutside()

    executeFromOutside(outsideTransaction, opts?): Promise<{ transaction_hash: string; }>

    Defined in: node_modules/starknet/dist/index.d.ts:4546

    An account B executes a transaction that has been signed by an account A. Fees are paid by B.

    Parameters

    outsideTransaction

    AllowArray<OutsideTransaction>

    the signed transaction generated by Account.getOutsideTransaction().

    opts?

    UniversalDetails

    same options than Account.execute().

    Returns

    Promise<{ transaction_hash: string; }>

    same response than Account.execute().

    Example

    const outsideTransaction1: OutsideTransaction = await signerAccount.getOutsideTransaction(callOptions, call1);
    const outsideTransaction2: OutsideTransaction = await signerAccount.getOutsideTransaction(callOptions4, call4);
    const result = await myAccount.executeFromOutside([
       outsideTransaction1,
       outsideTransaction2,
     ]);
    // result = { transaction_hash: '0x11233...`}
    

    Inherited from

    Account.executeFromOutside


    executeMultisig()

    executeMultisig(transactions, details, signature): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:195

    Executes a set of transactions, assuming they have been signed by all parties.

    Parameters

    transactions

    Call[]

    An array of transactions to be executed.

    details

    InvocationsDetailsWithNonce

    Optional object containing additional details for the transactions.

    signature

    ArraySignatureType

    The signature of the transactions.

    Returns

    Promise<{ transaction_hash: string; }>

    A Promise that resolves to the transactions invocation response.


    executeOnModule()

    Call Signature

    executeOnModule(module_class_hash, module_entrypoint, calldata, execute?): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:329

    Execute or build a transaction on the module account.

    Parameters
    module_class_hash

    string

    The installed module class_hash.

    module_entrypoint

    string

    The module entrypoint to execute.

    calldata

    string[]

    The entrypoint calldata.

    execute?

    true

    If true, the transaction is executed, otherwise it is built and returned so that it can be used in a multicall.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.

    Call Signature

    executeOnModule(module_class_hash, module_entrypoint, calldata, execute): Promise<Call[]>

    Defined in: sdks/account/src/smartr_account.ts:335

    Execute or build a transaction on the module account.

    Parameters
    module_class_hash

    string

    The installed module class_hash.

    module_entrypoint

    string

    The module entrypoint to execute.

    calldata

    string[]

    The entrypoint calldata.

    execute

    false

    If true, the transaction is executed, otherwise it is built and returned so that it can be used in a multicall.

    Returns

    Promise<Call[]>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.


    fetch()

    fetch(method, params?, id?): Promise<Response>

    Defined in: node_modules/starknet/dist/index.d.ts:3388

    Parameters

    method

    string

    params?

    object

    id?

    string | number

    Returns

    Promise<Response>

    Inherited from

    Account.fetch


    getAddressFromStarkName()

    getAddressFromStarkName(name, StarknetIdContract?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3534

    Parameters

    name

    string

    StarknetIdContract?

    string

    Returns

    Promise<string>

    Inherited from

    Account.getAddressFromStarkName


    getBlock()

    Call Signature

    getBlock(): Promise<PendingBlock>

    Defined in: node_modules/starknet/dist/index.d.ts:3392

    Gets the block information

    Returns

    Promise<PendingBlock>

    the block object

    Inherited from

    Account.getBlock

    Call Signature

    getBlock(blockIdentifier): Promise<PendingBlock>

    Defined in: node_modules/starknet/dist/index.d.ts:3393

    Parameters
    blockIdentifier

    "pending"

    Returns

    Promise<PendingBlock>

    Inherited from

    Account.getBlock

    Call Signature

    getBlock(blockIdentifier): Promise<Block$1>

    Defined in: node_modules/starknet/dist/index.d.ts:3394

    Parameters
    blockIdentifier

    "latest"

    Returns

    Promise<Block$1>

    Inherited from

    Account.getBlock

    Call Signature

    getBlock(blockIdentifier?): Promise<GetBlockResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3395

    Parameters
    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<GetBlockResponse>

    Inherited from

    Account.getBlock


    getBlockLatestAccepted()

    getBlockLatestAccepted(): Promise<BlockHashAndNumber>

    Defined in: node_modules/starknet/dist/index.d.ts:3399

    Get the most recent accepted block hash and number

    Returns

    Promise<BlockHashAndNumber>

    Inherited from

    Account.getBlockLatestAccepted


    getBlockNumber()

    getBlockNumber(): Promise<number>

    Defined in: node_modules/starknet/dist/index.d.ts:3405

    Get the most recent accepted block number redundant use getBlockLatestAccepted();

    Returns

    Promise<number>

    Number of the latest block

    Inherited from

    Account.getBlockNumber


    getBlockStateUpdate()

    Call Signature

    getBlockStateUpdate(): Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Defined in: node_modules/starknet/dist/index.d.ts:3429

    Returns

    Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Inherited from

    Account.getBlockStateUpdate

    Call Signature

    getBlockStateUpdate(blockIdentifier): Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Defined in: node_modules/starknet/dist/index.d.ts:3430

    Parameters
    blockIdentifier

    "pending"

    Returns

    Promise<{ block_hash: never; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Inherited from

    Account.getBlockStateUpdate

    Call Signature

    getBlockStateUpdate(blockIdentifier): Promise<{ block_hash: string; new_root: string; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Defined in: node_modules/starknet/dist/index.d.ts:3431

    Parameters
    blockIdentifier

    "latest"

    Returns

    Promise<{ block_hash: string; new_root: string; old_root: string; state_diff: { declared_classes: object[]; deployed_contracts: object[]; deprecated_declared_classes: string[]; nonces: object[]; replaced_classes: object[]; storage_diffs: object[]; }; }>

    Inherited from

    Account.getBlockStateUpdate

    Call Signature

    getBlockStateUpdate(blockIdentifier?): Promise<StateUpdateResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3432

    Parameters
    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<StateUpdateResponse>

    Inherited from

    Account.getBlockStateUpdate


    getBlockTransactionCount()

    getBlockTransactionCount(blockIdentifier?): Promise<number>

    Defined in: node_modules/starknet/dist/index.d.ts:3434

    Parameters

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<number>

    Inherited from

    Account.getBlockTransactionCount


    getBlockTransactionsTraces()

    getBlockTransactionsTraces(blockIdentifier?): Promise<BlockTransactionsTraces>

    Defined in: node_modules/starknet/dist/index.d.ts:3433

    Parameters

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<BlockTransactionsTraces>

    Inherited from

    Account.getBlockTransactionsTraces


    getBlockWithReceipts()

    getBlockWithReceipts(blockIdentifier?): Promise<BlockWithTxReceipts>

    Defined in: node_modules/starknet/dist/index.d.ts:3422

    Parameters

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<BlockWithTxReceipts>

    Inherited from

    Account.getBlockWithReceipts


    getBlockWithTxHashes()

    getBlockWithTxHashes(blockIdentifier?): Promise<BlockWithTxHashes$1>

    Defined in: node_modules/starknet/dist/index.d.ts:3406

    Parameters

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<BlockWithTxHashes$1>

    Inherited from

    Account.getBlockWithTxHashes


    getBlockWithTxs()

    getBlockWithTxs(blockIdentifier?): Promise<BlockWithTxs>

    Defined in: node_modules/starknet/dist/index.d.ts:3407

    Parameters

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<BlockWithTxs>

    Inherited from

    Account.getBlockWithTxs


    getCairoVersion()

    getCairoVersion(classHash?): Promise<CairoVersion>

    Defined in: node_modules/starknet/dist/index.d.ts:4427

    Retrieves the Cairo version from the network and sets cairoVersion if not already set in the constructor.

    Parameters

    classHash?

    string

    if provided detects Cairo version from classHash, otherwise from the account address

    Returns

    Promise<CairoVersion>

    Inherited from

    Account.getCairoVersion


    getChainId()

    getChainId(): Promise<StarknetChainId>

    Defined in: node_modules/starknet/dist/index.d.ts:3389

    Gets the Starknet chain Id

    Returns

    Promise<StarknetChainId>

    the chain Id

    Inherited from

    Account.getChainId


    getClass()

    getClass(classHash, blockIdentifier?): Promise<LegacyContractClass | Omit<CompiledSierra, "sierra_program_debug_info">>

    Defined in: node_modules/starknet/dist/index.d.ts:3462

    Parameters

    classHash

    BigNumberish

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<LegacyContractClass | Omit<CompiledSierra, "sierra_program_debug_info">>

    Inherited from

    Account.getClass


    getClassAt()

    getClassAt(contractAddress, blockIdentifier?): Promise<LegacyContractClass | Omit<CompiledSierra, "sierra_program_debug_info">>

    Defined in: node_modules/starknet/dist/index.d.ts:3463

    Gets the contract class of the deployed contract.

    Parameters

    contractAddress

    BigNumberish

    contract address

    blockIdentifier?

    BlockIdentifier

    block identifier

    Returns

    Promise<LegacyContractClass | Omit<CompiledSierra, "sierra_program_debug_info">>

    Contract class of compiled contract

    Inherited from

    Account.getClassAt


    getClassByHash()

    getClassByHash(classHash): Promise<LegacyContractClass | Omit<CompiledSierra, "sierra_program_debug_info">>

    Defined in: node_modules/starknet/dist/index.d.ts:3461

    Returns the contract class deployed under the given class hash.

    Parameters

    classHash

    BigNumberish

    class hash

    Returns

    Promise<LegacyContractClass | Omit<CompiledSierra, "sierra_program_debug_info">>

    Contract class of compiled contract

    Inherited from

    Account.getClassByHash


    getClassHashAt()

    getClassHashAt(contractAddress, blockIdentifier?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3460

    Returns the contract class hash in the given block for the contract deployed at the given address

    Parameters

    contractAddress

    BigNumberish

    contract address

    blockIdentifier?

    BlockIdentifier

    block identifier

    Returns

    Promise<string>

    Class hash

    Inherited from

    Account.getClassHashAt


    getContractVersion()

    Call Signature

    getContractVersion(contractAddress, classHash?, options?): Promise<ContractVersion>

    Defined in: node_modules/starknet/dist/index.d.ts:3464

    Gets the contract version from the provided address

    Parameters
    contractAddress

    BigNumberish

    string

    classHash?

    undefined

    undefined

    options?

    getContractVersionOptions

    getContractVersionOptions

    • (optional) compiler - (default true) extract compiler version using type tactic from abi
    • (optional) blockIdentifier - block identifier
    Returns

    Promise<ContractVersion>

    Inherited from

    Account.getContractVersion

    Call Signature

    getContractVersion(contractAddress, classHash, options?): Promise<ContractVersion>

    Defined in: node_modules/starknet/dist/index.d.ts:3465

    Gets the contract version from the provided address

    Parameters
    contractAddress

    undefined

    undefined

    classHash

    BigNumberish

    options?

    getContractVersionOptions

    getContractVersionOptions

    • (optional) compiler - (default true) extract compiler version using type tactic from abi
    • (optional) blockIdentifier - block identifier
    Returns

    Promise<ContractVersion>

    Inherited from

    Account.getContractVersion


    getDeclareEstimateFee()

    getDeclareEstimateFee(invocation, details, blockIdentifier?, skipValidate?): Promise<EstimateFeeResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3471

    Estimates the fee for a given DECLARE transaction

    Parameters

    invocation

    DeclareContractTransaction

    details

    InvocationsDetailsWithNonce

    optional details containing:

    • nonce
    • version - optional version
    • optional maxFee
    blockIdentifier?

    BlockIdentifier

    (optional) block identifier

    skipValidate?

    boolean

    (optional) skip cairo validate method

    Returns

    Promise<EstimateFeeResponse>

    the estimated fee

    Inherited from

    Account.getDeclareEstimateFee


    getDeployAccountEstimateFee()

    getDeployAccountEstimateFee(invocation, details, blockIdentifier?, skipValidate?): Promise<EstimateFeeResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3472

    Estimates the fee for a given DEPLOY_ACCOUNT transaction

    Parameters

    invocation

    DeployAccountContractTransaction

    details

    InvocationsDetailsWithNonce

    optional details containing:

    • nonce
    • version - optional version
    • optional maxFee
    blockIdentifier?

    BlockIdentifier

    (optional) block identifier

    skipValidate?

    boolean

    (optional) skip cairo validate method

    Returns

    Promise<EstimateFeeResponse>

    the estimated fee

    Inherited from

    Account.getDeployAccountEstimateFee


    getEstimateFee()

    getEstimateFee(invocation, invocationDetails, blockIdentifier?, skipValidate?): Promise<EstimateFeeResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3469

    Parameters

    invocation

    Invocation

    invocationDetails

    InvocationsDetailsWithNonce

    blockIdentifier?

    BlockIdentifier

    skipValidate?

    boolean

    Returns

    Promise<EstimateFeeResponse>

    Deprecated

    use gettypeEstimateFee (will be refactored based on type after sequencer deprecation)

    Inherited from

    Account.getEstimateFee


    getEstimateFeeBulk()

    getEstimateFeeBulk(invocations, options): Promise<EstimateFeeResponseBulk>

    Defined in: node_modules/starknet/dist/index.d.ts:3473

    Estimates the fee for a list of INVOKE transaction

    Parameters

    invocations

    AccountInvocations

    AccountInvocations - Complete invocations array with account details

    options

    getEstimateFeeBulkOptions

    getEstimateFeeBulkOptions

    • (optional) blockIdentifier - BlockIdentifier

    Returns

    Promise<EstimateFeeResponseBulk>

    the estimated fee

    Inherited from

    Account.getEstimateFeeBulk


    getEvents()

    getEvents(eventFilter): Promise<EVENTS_CHUNK>

    Defined in: node_modules/starknet/dist/index.d.ts:3492

    Returns all events matching the given filter

    Parameters

    eventFilter

    EventFilter

    Returns

    Promise<EVENTS_CHUNK>

    events and the pagination of the events

    Inherited from

    Account.getEvents


    getInvokeEstimateFee()

    getInvokeEstimateFee(invocation, invocationDetails, blockIdentifier?, skipValidate?): Promise<EstimateFeeResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3470

    Estimates the fee for a given INVOKE transaction

    Parameters

    invocation

    Invocation

    the invocation object containing:

    • contractAddress - the address of the contract
    • entrypoint - the entrypoint of the contract
    • calldata - (defaults to []) the calldata
    • signature - (defaults to []) the signature
    invocationDetails

    InvocationsDetailsWithNonce

    blockIdentifier?

    BlockIdentifier

    (optional) block identifier

    skipValidate?

    boolean

    (optional) skip cairo validate method

    Returns

    Promise<EstimateFeeResponse>

    the estimated fee

    Inherited from

    Account.getInvokeEstimateFee


    getL1GasPrice()

    getL1GasPrice(blockIdentifier?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3420

    Gets the price of l1 gas in the block

    Parameters

    blockIdentifier?

    BlockIdentifier

    block identifier

    Returns

    Promise<string>

    gas price of the block

    Inherited from

    Account.getL1GasPrice


    getL1MessageHash()

    getL1MessageHash(l2TxHash): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3421

    Get L1 message hash from L2 transaction hash

    Parameters

    l2TxHash

    BigNumberish

    L2 transaction hash

    Returns

    Promise<string>

    Hex string of L1 message hash

    Example

    In Sepolia Testnet :

    const result = provider.getL1MessageHash('0x28dfc05eb4f261b37ddad451ff22f1d08d4e3c24dc646af0ec69fa20e096819');
    // result = '0x55b3f8b6e607fffd9b4d843dfe8f9b5c05822cd94fcad8797deb01d77805532a'
    

    Inherited from

    Account.getL1MessageHash


    getNonce()

    getNonce(blockIdentifier?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:4421

    Gets the nonce of the account with respect to a specific block

    Parameters

    blockIdentifier?

    BlockIdentifier

    optional blockIdentifier. Defaults to 'pending'

    Returns

    Promise<string>

    nonce of the account

    Inherited from

    Account.getNonce


    getNonceForAddress()

    getNonceForAddress(contractAddress, blockIdentifier?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3391

    Returns the nonce associated with the given address in the given block

    Parameters

    contractAddress

    BigNumberish

    contract address

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<string>

    the hex nonce

    Inherited from

    Account.getNonceForAddress


    getNonceSafe()

    protected getNonceSafe(nonce?): Promise<bigint>

    Defined in: node_modules/starknet/dist/index.d.ts:4422

    Parameters

    nonce?

    BigNumberish

    Returns

    Promise<bigint>

    Inherited from

    Account.getNonceSafe


    getOutsideTransaction()

    getOutsideTransaction(options, calls, version?, nonce?): Promise<OutsideTransaction>

    Defined in: node_modules/starknet/dist/index.d.ts:4528

    Creates an object containing transaction(s) that can be executed by an other account with Account.executeFromOutside(), called Outside Transaction.

    Parameters

    options

    OutsideExecutionOptions

    Parameters of the transaction(s).

    calls

    AllowArray<Call>

    Transaction(s) to execute.

    version?

    OutsideExecutionVersion

    SNIP-9 version of the Account that creates the outside transaction.

    nonce?

    BigNumberish

    Outside Nonce.

    Returns

    Promise<OutsideTransaction>

    and object that can be used in Account.executeFromOutside()

    Example

    const now_seconds = Math.floor(Date.now() / 1000);
    const callOptions: OutsideExecutionOptions = {
       caller: executorAccount.address, execute_after: now_seconds - 3600, execute_before: now_seconds + 3600 };
    const call1: Call = { contractAddress: ethAddress, entrypoint: 'transfer', calldata: {
        recipient: recipientAccount.address, amount: cairo.uint256(100) } };
    const outsideTransaction1: OutsideTransaction = await signerAccount.getOutsideTransaction(callOptions, call3);
    // result = {
    // outsideExecution: {
    // caller: '0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691',
    // nonce: '0x28a612590dbc36927933c8ee0f357eee639c8b22b3d3aa86949eed3ada4ac55',
    // execute_after: 1723650229, execute_before: 1723704229, calls: [[Object]] },
    // signature: Signature {
    // r: 67518627037915514985321278857825384106482999609634873287406612756843916814n,
    // s: 737198738569840639192844101690009498983611654458636624293579534560862067709n, recovery: 0 },
    // signerAddress: '0x655f8fd7c4013c07cf12a92184aa6c314d181443913e21f7e209a18f0c78492',
    // version: '2'
    // }
    

    Inherited from

    Account.getOutsideTransaction


    getPendingTransactions()

    getPendingTransactions(): Promise<TransactionWithHash$1[]>

    Defined in: node_modules/starknet/dist/index.d.ts:3440

    Return transactions from pending block

    Returns

    Promise<TransactionWithHash$1[]>

    Deprecated

    Instead use getBlock(BlockTag.PENDING); (will be removed in next minor version) Utility method, same result can be achieved using getBlockWithTxHashes(BlockTag.pending);

    Inherited from

    Account.getPendingTransactions


    getPreferredVersion()

    protected getPreferredVersion(type12, type3): ETransactionVersion

    Defined in: node_modules/starknet/dist/index.d.ts:4420

    Parameters

    type12

    ETransactionVersion

    type3

    ETransactionVersion

    Returns

    ETransactionVersion

    Inherited from

    Account.getPreferredVersion


    getSimulateTransaction()

    getSimulateTransaction(invocations, options?): Promise<SimulateTransactionResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3457

    Parameters

    invocations

    AccountInvocations

    AccountInvocations

    options?

    getSimulateTransactionOptions

    blockIdentifier and flags to skip validation and fee charge

    • blockIdentifier
    • skipValidate (default false)
    • skipFeeCharge (default true)

    Returns

    Promise<SimulateTransactionResponse>

    Inherited from

    Account.getSimulateTransaction


    getSnip9Nonce()

    getSnip9Nonce(): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:4499

    Outside transaction needs a specific SNIP-9 nonce, that we get in this function. A SNIP-9 nonce can be any number not yet used ; no ordering is needed.

    Returns

    Promise<string>

    an Hex string of a SNIP-9 nonce.

    Example

    const result = myAccount.getSnip9Nonce();
    // result = "0x28a612590dbc36927933c8ee0f357eee639c8b22b3d3aa86949eed3ada4ac55"
    

    Inherited from

    Account.getSnip9Nonce


    getSnip9Version()

    getSnip9Version(): Promise<OutsideExecutionVersion>

    Defined in: node_modules/starknet/dist/index.d.ts:4477

    Verify if an account is compatible with SNIP-9 outside execution, and with which version of this standard.

    Returns

    Promise<OutsideExecutionVersion>

    Not compatible, V1, V2.

    Example

    const result = myAccount.getSnip9Version();
    // result = "V1"
    

    Inherited from

    Account.getSnip9Version


    getSpecVersion()

    getSpecVersion(): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3390

    Returns

    Promise<string>

    Inherited from

    Account.getSpecVersion


    getStarkName()

    getStarkName(address?, StarknetIdContract?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:4554

    Parameters

    address?

    BigNumberish

    StarknetIdContract?

    string

    Returns

    Promise<string>

    Inherited from

    Account.getStarkName


    getStarkProfile()

    getStarkProfile(address, StarknetIdContract?, StarknetIdIdentityContract?, StarknetIdVerifierContract?, StarknetIdPfpContract?, StarknetIdPopContract?, StarknetIdMulticallContract?): Promise<StarkProfile>

    Defined in: node_modules/starknet/dist/index.d.ts:3535

    Parameters

    address

    BigNumberish

    StarknetIdContract?

    string

    StarknetIdIdentityContract?

    string

    StarknetIdVerifierContract?

    string

    StarknetIdPfpContract?

    string

    StarknetIdPopContract?

    string

    StarknetIdMulticallContract?

    string

    Returns

    Promise<StarkProfile>

    Inherited from

    Account.getStarkProfile


    getStorageAt()

    getStorageAt(contractAddress, key, blockIdentifier?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3459

    Get the value of the storage (contract's variable) at the given address and key

    Parameters

    contractAddress

    BigNumberish

    key

    BigNumberish

    from getStorageVarAddress('<STORAGE_VARIABLE_NAME>') (WIP)

    blockIdentifier?

    BlockIdentifier

    block identifier

    Returns

    Promise<string>

    the value of the storage variable

    Inherited from

    Account.getStorageAt


    getSuggestedFee()

    getSuggestedFee(estimateFeeAction, details): Promise<EstimateFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4548

    Gets Suggested Max Fee based on the transaction type

    Parameters

    estimateFeeAction

    EstimateFeeAction

    details

    UniversalDetails

    Returns

    Promise<EstimateFee>

    EstimateFee (...response, resourceBounds, suggestedMaxFee)

    Inherited from

    Account.getSuggestedFee


    getSyncingStats()

    getSyncingStats(): Promise<Syncing>

    Defined in: node_modules/starknet/dist/index.d.ts:3487

    Returns an object about the sync status, or false if the node is not synching

    Returns

    Promise<Syncing>

    Object with the stats data

    Inherited from

    Account.getSyncingStats


    getTransaction()

    getTransaction(txHash): Promise<TransactionWithHash$1>

    Defined in: node_modules/starknet/dist/index.d.ts:3441

    Gets the transaction information from a tx id.

    Parameters

    txHash

    BigNumberish

    Returns

    Promise<TransactionWithHash$1>

    the transaction object { transaction_id, status, transaction, block_number?, block_number?, transaction_index?, transaction_failure_reason? }

    Inherited from

    Account.getTransaction


    getTransactionByBlockIdAndIndex()

    getTransactionByBlockIdAndIndex(blockIdentifier, index): Promise<TransactionWithHash$1>

    Defined in: node_modules/starknet/dist/index.d.ts:3443

    Parameters

    blockIdentifier

    BlockIdentifier

    index

    number

    Returns

    Promise<TransactionWithHash$1>

    Inherited from

    Account.getTransactionByBlockIdAndIndex


    getTransactionByHash()

    getTransactionByHash(txHash): Promise<TransactionWithHash$1>

    Defined in: node_modules/starknet/dist/index.d.ts:3442

    Parameters

    txHash

    BigNumberish

    Returns

    Promise<TransactionWithHash$1>

    Inherited from

    Account.getTransactionByHash


    getTransactionReceipt()

    getTransactionReceipt(txHash): Promise<GetTransactionReceiptResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3444

    Gets the transaction receipt from a tx hash.

    Parameters

    txHash

    BigNumberish

    Returns

    Promise<GetTransactionReceiptResponse>

    the transaction receipt object

    Inherited from

    Account.getTransactionReceipt


    getTransactionStatus()

    getTransactionStatus(transactionHash): Promise<TransactionStatus$1>

    Defined in: node_modules/starknet/dist/index.d.ts:3449

    Get the status of a transaction

    Parameters

    transactionHash

    BigNumberish

    Returns

    Promise<TransactionStatus$1>

    Inherited from

    Account.getTransactionStatus


    getTransactionTrace()

    getTransactionTrace(txHash): Promise<TRANSACTION_TRACE>

    Defined in: node_modules/starknet/dist/index.d.ts:3445

    Parameters

    txHash

    BigNumberish

    Returns

    Promise<TRANSACTION_TRACE>

    Inherited from

    Account.getTransactionTrace


    getUniversalSuggestedFee()

    protected getUniversalSuggestedFee(version, __namedParameters, details): Promise<UniversalSuggestedFee>

    Defined in: node_modules/starknet/dist/index.d.ts:4547

    Parameters

    version

    ETransactionVersion

    __namedParameters

    EstimateFeeAction

    details

    UniversalDetails

    Returns

    Promise<UniversalSuggestedFee>

    Inherited from

    Account.getUniversalSuggestedFee


    hashMessage()

    hashMessage(typedData): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:4451

    Hash a TypedData object with Pedersen hash and return the hash This adds a message prefix so it can't be interchanged with transactions

    Parameters

    typedData

    TypedData

    TypedData object to be hashed

    Returns

    Promise<string>

    the hash of the TypedData object

    Throws

    if typedData is not a valid TypedData

    Inherited from

    Account.hashMessage


    invokeFunction()

    invokeFunction(functionInvocation, details): Promise<InvokedTransaction>

    Defined in: node_modules/starknet/dist/index.d.ts:3474

    Invokes a function on starknet

    Parameters

    functionInvocation

    Invocation

    details

    InvocationsDetailsWithNonce

    optional details containing:

    • nonce - optional nonce
    • version - optional version
    • maxFee - optional maxFee

    Returns

    Promise<InvokedTransaction>

    response from addTransaction

    Deprecated

    This method won't be supported as soon as fees are mandatory. Should not be used outside of Account class

    Inherited from

    Account.invokeFunction


    isClassDeclared()

    isClassDeclared(contractClassIdentifier, blockIdentifier?): Promise<boolean>

    Defined in: node_modules/starknet/dist/index.d.ts:3522

    Test if class is already declared from ContractClassIdentifier Helper method using getClass

    Parameters

    contractClassIdentifier

    ContractClassIdentifier

    blockIdentifier?

    BlockIdentifier

    Returns

    Promise<boolean>

    Inherited from

    Account.isClassDeclared


    isModule()

    isModule(class_hash): Promise<boolean>

    Defined in: sdks/account/src/smartr_account.ts:413

    Call an entrypoint on the module account.

    Parameters

    class_hash

    string

    the module to test

    Returns

    Promise<boolean>

    A promise that is true if the module is installed with the account.


    isValidSnip9Nonce()

    isValidSnip9Nonce(nonce): Promise<boolean>

    Defined in: node_modules/starknet/dist/index.d.ts:4488

    Verify if a SNIP-9 nonce has not yet been used by the account.

    Parameters

    nonce

    BigNumberish

    SNIP-9 nonce to test.

    Returns

    Promise<boolean>

    true if SNIP-9 nonce not yet used.

    Example

    const result = myAccount.isValidSnip9Nonce(1234);
    // result = true
    

    Inherited from

    Account.isValidSnip9Nonce


    prepareInvocations()

    prepareInvocations(invocations): Promise<Invocations>

    Defined in: node_modules/starknet/dist/index.d.ts:3529

    Build bulk invocations with auto-detect declared class

    1. Test if class is declared if not declare it preventing already declared class error and not declared class errors
    2. Order declarations first

    Parameters

    invocations

    Invocations

    Returns

    Promise<Invocations>

    Inherited from

    Account.prepareInvocations


    prepareMultisig()

    prepareMultisig(transactions, transactionsDetail?): Promise<InvocationsDetailsWithNonce>

    Defined in: sdks/account/src/smartr_account.ts:105

    generates the details, i.e nonce, version, fee, module prefix, to execute transactions on the StarkNet network.

    Parameters

    transactions

    AllowArray<Call>

    An array of transactions to be executed.

    transactionsDetail?

    UniversalDetails

    Optional object containing additional details for the transactions.

    Returns

    Promise<InvocationsDetailsWithNonce>

    A Promise that resolves to the transaction details.


    removeModule()

    Call Signature

    removeModule(class_hash, execute?): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:454

    remove a module from an account.

    Parameters
    class_hash

    string

    the module to remove

    execute?

    true

    If true, the transaction is executed, otherwise it is built and returned so that it can be used in a multicall.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.

    Call Signature

    removeModule(class_hash, execute): Promise<Call[]>

    Defined in: sdks/account/src/smartr_account.ts:458

    remove a module from an account.

    Parameters
    class_hash

    string

    the module to remove

    execute

    false

    If true, the transaction is executed, otherwise it is built and returned so that it can be used in a multicall.

    Returns

    Promise<Call[]>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.


    signMessage()

    signMessage(typedData): Promise<Signature>

    Defined in: node_modules/starknet/dist/index.d.ts:4450

    Signs a TypedData object for off-chain usage with the Starknet private key and returns the signature This adds a message prefix so it can't be interchanged with transactions

    Parameters

    typedData

    TypedData

    TypedData object to be signed

    Returns

    Promise<Signature>

    the signature of the TypedData object

    Throws

    if typedData is not a valid TypedData

    Inherited from

    Account.signMessage


    signMultisig()

    signMultisig(transactions, details): Promise<ArraySignatureType>

    Defined in: sdks/account/src/smartr_account.ts:151

    Signs a set of transactions to be executed on the StarkNet network.

    Parameters

    transactions

    Call[]

    An array of transactions to be executed.

    details

    InvocationsDetailsWithNonce

    Optional object containing additional details for the transactions.

    Returns

    Promise<ArraySignatureType>

    A Promise that resolves to the signature of the transactions.


    simulateTransaction()

    simulateTransaction(invocations, details?): Promise<SimulateTransactionResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4434

    Simulates an array of transaction and returns an array of transaction trace and estimated fee.

    Parameters

    invocations

    Invocations

    Invocations containing:

    • type - transaction type: DECLARE, (multi)DEPLOY, DEPLOY_ACCOUNT, (multi)INVOKE_FUNCTION
    details?

    SimulateTransactionDetails

    SimulateTransactionDetails

    Returns

    Promise<SimulateTransactionResponse>

    response from simulate_transaction

    Inherited from

    Account.simulateTransaction


    upgrade()

    upgrade(classHash): Promise<{ transaction_hash: string; }>

    Defined in: sdks/account/src/smartr_account.ts:398

    Upgrades the SmartrAccount to a new class.

    Parameters

    classHash

    string

    The hash of the new class.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the transaction receipt if executes is true, otherwise it returns the transaction call.


    verifyMessage()

    verifyMessage(typedData, signature, signatureVerificationFunctionName?, signatureVerificationResponse?): Promise<boolean>

    Defined in: node_modules/starknet/dist/index.d.ts:4463

    Parameters

    typedData

    TypedData

    signature

    Signature

    signatureVerificationFunctionName?

    string

    signatureVerificationResponse?
    error

    string[]

    nokResponse

    string[]

    okResponse

    string[]

    Returns

    Promise<boolean>

    Deprecated

    To replace by myRpcProvider.verifyMessageInStarknet()

    Inherited from

    Account.verifyMessage


    verifyMessageHash()

    verifyMessageHash(hash, signature, signatureVerificationFunctionName?, signatureVerificationResponse?): Promise<boolean>

    Defined in: node_modules/starknet/dist/index.d.ts:4455

    Parameters

    hash

    BigNumberish

    signature

    Signature

    signatureVerificationFunctionName?

    string

    signatureVerificationResponse?
    error

    string[]

    nokResponse

    string[]

    okResponse

    string[]

    Returns

    Promise<boolean>

    Deprecated

    To replace by myRpcProvider.verifyMessageInStarknet()

    Inherited from

    Account.verifyMessageHash


    verifyMessageInStarknet()

    verifyMessageInStarknet(message, signature, accountAddress, signatureVerificationFunctionName?, signatureVerificationResponse?): Promise<boolean>

    Defined in: node_modules/starknet/dist/index.d.ts:3511

    Verify in Starknet a signature of a TypedData object or of a given hash.

    Parameters

    message

    TypedData object to be verified, or message hash to be verified.

    BigNumberish | TypedData

    signature

    Signature

    signature of the message.

    accountAddress

    BigNumberish

    address of the account that has signed the message.

    signatureVerificationFunctionName?

    string

    if account contract with non standard account verification function name.

    signatureVerificationResponse?

    if account contract with non standard response of verification function.

    error

    string[]

    nokResponse

    string[]

    okResponse

    string[]

    Returns

    Promise<boolean>

    const myTypedMessage: TypedMessage = .... ;
    const messageHash = typedData.getMessageHash(myTypedMessage,accountAddress);
    const sign: WeierstrassSignatureType = ec.starkCurve.sign(messageHash, privateKey);
    const accountAddress = "0x43b7240d227aa2fb8434350b3321c40ac1b88c7067982549e7609870621b535";
    const result1 = myRpcProvider.verifyMessageInStarknet(myTypedMessage, sign, accountAddress);
    const result2 = myRpcProvider.verifyMessageInStarknet(messageHash, sign, accountAddress);
    // result1 = result2 = true
    

    Inherited from

    Account.verifyMessageInStarknet


    waitForBlock()

    waitForBlock(blockIdentifier?, retryInterval?): Promise<void>

    Defined in: node_modules/starknet/dist/index.d.ts:3419

    Pause the execution of the script until a specified block is created.

    Parameters

    blockIdentifier?

    BlockIdentifier

    bloc number (BigNumberish) or 'pending' or 'latest'. Use of 'latest" or of a block already created will generate no pause.

    retryInterval?

    number

    number of milliseconds between 2 requests to the node

    Returns

    Promise<void>

    Example

    await myProvider.waitForBlock();
    // wait the creation of the pending block
    

    Inherited from

    Account.waitForBlock


    waitForTransaction()

    waitForTransaction(txHash, options?): Promise<GetTransactionReceiptResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:3458

    Wait for the transaction to be accepted

    Parameters

    txHash

    BigNumberish

    transaction hash

    options?

    waitForTransactionOptions

    waitForTransactionOptions

    • (optional) retryInterval: number | undefined;
    • (optional) successStates: TransactionStatus[] | undefined;

    Returns

    Promise<GetTransactionReceiptResponse>

    GetTransactionReceiptResponse

    Inherited from

    Account.waitForTransaction


    getAddressFromStarkName()

    static getAddressFromStarkName(provider, name, StarknetIdContract?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3537

    Parameters

    provider

    ProviderInterface

    name

    string

    StarknetIdContract?

    string

    Returns

    Promise<string>

    Inherited from

    Account.getAddressFromStarkName


    getStarkName()

    static getStarkName(provider, address, StarknetIdContract?): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3536

    Parameters

    provider

    ProviderInterface

    address

    BigNumberish

    StarknetIdContract?

    string

    Returns

    Promise<string>

    Inherited from

    Account.getStarkName


    getStarkProfile()

    static getStarkProfile(provider, address, StarknetIdContract?, StarknetIdIdentityContract?, StarknetIdVerifierContract?, StarknetIdPfpContract?, StarknetIdPopContract?, StarknetIdMulticallContract?): Promise<StarkProfile>

    Defined in: node_modules/starknet/dist/index.d.ts:3538

    Parameters

    provider

    ProviderInterface

    address

    BigNumberish

    StarknetIdContract?

    string

    StarknetIdIdentityContract?

    string

    StarknetIdVerifierContract?

    string

    StarknetIdPfpContract?

    string

    StarknetIdPopContract?

    string

    StarknetIdMulticallContract?

    string

    Returns

    Promise<StarkProfile>

    Inherited from

    Account.getStarkProfile

    @0xknwn/starknet-modular-account


    @0xknwn/starknet-modular-account / AccountModuleInterface

    Interface: AccountModuleInterface

    Defined in: sdks/account/src/smartr_account.ts:40

    Methods

    prefix()

    prefix(calls): Call

    Defined in: sdks/account/src/smartr_account.ts:41

    Parameters

    calls

    Call | Call[]

    Returns

    Call

    @0xknwn/starknet-module


    @0xknwn/starknet-module

    Classes

    @0xknwn/starknet-module


    @0xknwn/starknet-module / EthModule

    Class: EthModule

    Defined in: sdks/module/src/eth.ts:6

    Implements

    • AccountModuleInterface

    Constructors

    new EthModule()

    new EthModule(accountAddress): EthModule

    Defined in: sdks/module/src/eth.ts:8

    Parameters

    accountAddress

    string

    Returns

    EthModule

    Properties

    accountAddress

    protected accountAddress: string

    Defined in: sdks/module/src/eth.ts:7

    Methods

    prefix()

    prefix(calls): object

    Defined in: sdks/module/src/eth.ts:12

    Parameters

    calls

    Call | Call[]

    Returns

    object

    calldata

    calldata: string[]

    contractAddress

    contractAddress: string

    entrypoint

    entrypoint: string = "__module_validate__"

    Implementation of

    AccountModuleInterface.prefix

    @0xknwn/starknet-module


    @0xknwn/starknet-module / MultisigModule

    Class: MultisigModule

    Defined in: sdks/module/src/multisig.ts:6

    Implements

    • AccountModuleInterface

    Constructors

    new MultisigModule()

    new MultisigModule(accountAddress): MultisigModule

    Defined in: sdks/module/src/multisig.ts:8

    Parameters

    accountAddress

    string

    Returns

    MultisigModule

    Properties

    accountAddress

    protected accountAddress: string

    Defined in: sdks/module/src/multisig.ts:7

    Methods

    prefix()

    prefix(calls): object

    Defined in: sdks/module/src/multisig.ts:12

    Parameters

    calls

    Call | Call[]

    Returns

    object

    calldata

    calldata: string[]

    contractAddress

    contractAddress: string

    entrypoint

    entrypoint: string = "__module_validate__"

    Implementation of

    AccountModuleInterface.prefix

    @0xknwn/starknet-module


    @0xknwn/starknet-module / P256Module

    Class: P256Module

    Defined in: sdks/module/src/p256.ts:6

    Implements

    • AccountModuleInterface

    Constructors

    new P256Module()

    new P256Module(accountAddress): P256Module

    Defined in: sdks/module/src/p256.ts:8

    Parameters

    accountAddress

    string

    Returns

    P256Module

    Properties

    accountAddress

    protected accountAddress: string

    Defined in: sdks/module/src/p256.ts:7

    Methods

    prefix()

    prefix(calls): object

    Defined in: sdks/module/src/p256.ts:12

    Parameters

    calls

    Call | Call[]

    Returns

    object

    calldata

    calldata: string[]

    contractAddress

    contractAddress: string

    entrypoint

    entrypoint: string = "__module_validate__"

    Implementation of

    AccountModuleInterface.prefix

    @0xknwn/starknet-module


    @0xknwn/starknet-module / P256Signer

    Class: P256Signer

    Defined in: sdks/module/src/p256signer.ts:36

    Signer for accounts using Ethereum signature

    Implements

    • SignerInterface

    Constructors

    new P256Signer()

    new P256Signer(pk): P256Signer

    Defined in: sdks/module/src/p256signer.ts:39

    Parameters

    pk

    string | Uint8Array

    Returns

    P256Signer

    Properties

    pk

    protected pk: string

    Defined in: sdks/module/src/p256signer.ts:37

    Methods

    formatP256Signature()

    protected formatP256Signature(p256Signature): ArraySignatureType

    Defined in: sdks/module/src/p256signer.ts:195

    Serialize the signature in conformity with starknet::eth_signature::Signature

    Parameters

    p256Signature

    RecoveredSignatureType

    Returns

    ArraySignatureType

    an array of felts, representing a Cairo Eth Signature.


    getPubKey()

    getPubKey(): Promise<string>

    Defined in: sdks/module/src/p256signer.ts:50

    provides the Ethereum full public key (without parity prefix)

    Returns

    Promise<string>

    an hex string : 64 first characters are Point X coordinate. 64 last characters are Point Y coordinate.

    Implementation of

    SignerInterface.getPubKey


    signDeclareTransaction()

    signDeclareTransaction(details): Promise<Signature>

    Defined in: sdks/module/src/p256signer.ts:155

    Signs a DECLARE transaction with the private key and returns the signature

    Parameters

    details

    DeclareSignerDetails

    Returns

    Promise<Signature>

    the signature of the transaction to declare a class

    Example

    const mySigner = new Signer("0x123");
    const myDeclare: DeclareSignerDetails = {
      version: "0x2", chainId: constants.StarknetChainId.SN_SEPOLIA,
      senderAddress: "0x65a822fbee1ae79e898688b5a4282dc79e0042cbed12f6169937fddb4c26641",
      classHash: "0x5f3614e8671257aff9ac38e929c74d65b02d460ae966cd826c9f04a7fa8e0d4",
      nonce: 45, maxFee: 10 ** 15, tip: 0, paymasterData: [], accountDeploymentData: [],
      nonceDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      feeDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      resourceBounds: stark.estimateFeeToBounds(constants.ZERO),
    }
    const result = await mySigner.signDeclareTransaction(myDeclare);
    // result = Signature {r: 2432056944313955951711774394836075930010416436707488863728289188289211995670n,
    //  s: 3407649393310177489888603098175002856596469926897298636282244411990343146307n, recovery: 1}
    

    Implementation of

    SignerInterface.signDeclareTransaction


    signDeployAccountTransaction()

    signDeployAccountTransaction(details): Promise<Signature>

    Defined in: sdks/module/src/p256signer.ts:114

    Signs a DEPLOY_ACCOUNT transaction with the private key and returns the signature

    Parameters

    details

    DeployAccountSignerDetails

    Returns

    Promise<Signature>

    the signature of the transaction to deploy an account

    Example

    const mySigner = new Signer("0x123");
    const myDeployAcc: DeployAccountSignerDetails = {
      contractAddress: "0x65a822fbee1ae79e898688b5a4282dc79e0042cbed12f6169937fddb4c26641",
      version: "0x2", chainId: constants.StarknetChainId.SN_SEPOLIA,
      classHash: "0x5f3614e8671257aff9ac38e929c74d65b02d460ae966cd826c9f04a7fa8e0d4",
      constructorCalldata: [1, 2],addressSalt: 1234,
      nonce: 45, maxFee: 10 ** 15, tip: 0, paymasterData: [],accountDeploymentData: [],
      nonceDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      feeDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      resourceBounds: stark.estimateFeeToBounds(constants.ZERO),
    }
    const result = await mySigner.signDeployAccountTransaction(myDeployAcc);
    // result = Signature {r: 2871311234341436528393212130310036951068553852419934781736214693308640202748n,
    //  s: 1746271646048888422437132495446973163454853863041370993384284773665861377605n, recovery: 1}
    

    Implementation of

    SignerInterface.signDeployAccountTransaction


    signMessage()

    signMessage(typedData, accountAddress): Promise<Signature>

    Defined in: sdks/module/src/p256signer.ts:59

    Signs a JSON object for off-chain usage with the private key and returns the signature. This adds a message prefix so it can't be interchanged with transactions

    Parameters

    typedData

    TypedData

    JSON object to be signed

    accountAddress

    string

    Hex string of the account's address

    Returns

    Promise<Signature>

    the signature of the message

    Example

    const mySigner = new Signer("0x123");
        const myTypedData: TypedData = {
            domain: {name: "Example DApp",
              chainId: constants.StarknetChainId.SN_SEPOLIA,
              version: "0.0.3"},
            types: {StarkNetDomain: [
                { name: "name", type: "string" },
                { name: "chainId", type: "felt" },
                { name: "version", type: "string" }],
              Message: [{ name: "message", type: "felt" }]},
            primaryType: "Message", message: {message: "1234"}};
        const result = await mySigner.signMessage(myTypedData,"0x5d08a4e9188429da4e993c9bf25aafe5cd491ee2b501505d4d059f0c938f82d");
    // result = Signature {r: 684915484701699003335398790608214855489903651271362390249153620883122231253n,
    // s: 1399150959912500412309102776989465580949387575375484933432871778355496929189n, recovery: 1}
    

    Implementation of

    SignerInterface.signMessage


    signTransaction()

    signTransaction(transactions, details): Promise<Signature>

    Defined in: sdks/module/src/p256signer.ts:71

    Signs transactions with the private key and returns the signature

    Parameters

    transactions

    Call[]

    array of Call objects

    details

    InvocationsSignerDetails

    Returns

    Promise<Signature>

    the signature of the transaction

    Example

    const mySigner = new Signer("0x123");
    const calls: Call[] = [{
        contractAddress: "0x1234567890123456789012345678901234567890",
        entrypoint: "functionName",
        calldata: [1, 2, 3]
    }];
    const transactionsDetail: InvocationsSignerDetails = {
        walletAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
        chainId: constants.StarknetChainId.SN_MAIN,
        cairoVersion: "1",
        maxFee: '0x1234567890abcdef',
        version: "0x0", nonce: 1};
    const result = await mySigner.signTransaction(calls, transactionsDetail);
    // result = Signature {r: 304910226421970384958146916800275294114105560641204815169249090836676768876n,
    //   s: 1072798866000813654190523783606274062837012608648308896325315895472901074693n, recovery: 0}
    

    Implementation of

    SignerInterface.signTransaction

    @0xknwn/starknet-module


    @0xknwn/starknet-module / StarkModule

    Class: StarkModule

    Defined in: sdks/module/src/stark.ts:9

    Implements

    • AccountModuleInterface

    Constructors

    new StarkModule()

    new StarkModule(accountAddress): StarkModule

    Defined in: sdks/module/src/stark.ts:11

    Parameters

    accountAddress

    string

    Returns

    StarkModule

    Properties

    accountAddress

    protected accountAddress: string

    Defined in: sdks/module/src/stark.ts:10

    Methods

    prefix()

    prefix(calls): object

    Defined in: sdks/module/src/stark.ts:15

    Parameters

    calls

    Call | Call[]

    Returns

    object

    calldata

    calldata: string[]

    contractAddress

    contractAddress: string

    entrypoint

    entrypoint: string = "__module_validate__"

    Implementation of

    AccountModuleInterface.prefix

    @0xknwn/starknet-module-sessionkey


    @0xknwn/starknet-module-sessionkey

    Classes

    @0xknwn/starknet-module-sessionkey


    @0xknwn/starknet-module-sessionkey / PolicyManager

    Class: PolicyManager

    Defined in: sdks/module-sessionkey/src/policies.ts:10

    Constructors

    new PolicyManager()

    new PolicyManager(policies): PolicyManager

    Defined in: sdks/module-sessionkey/src/policies.ts:14

    Parameters

    policies

    Policies

    Returns

    PolicyManager

    Methods

    getProof()

    getProof(policy): string[]

    Defined in: sdks/module-sessionkey/src/policies.ts:29

    Parameters

    policy

    Policy

    Returns

    string[]


    getRoot()

    getRoot(): string

    Defined in: sdks/module-sessionkey/src/policies.ts:25

    Returns

    string

    @0xknwn/starknet-module-sessionkey


    @0xknwn/starknet-module-sessionkey / SessionKeyGrantor

    Class: SessionKeyGrantor

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:14

    Extends

    • Signer

    Constructors

    new SessionKeyGrantor()

    new SessionKeyGrantor(validatorGrantorClass, privateKey): SessionKeyGrantor

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:16

    Parameters

    validatorGrantorClass

    string

    privateKey

    string | Uint8Array

    Returns

    SessionKeyGrantor

    Overrides

    Signer.constructor

    Properties

    pk

    protected pk: string | Uint8Array

    Defined in: node_modules/starknet/dist/index.d.ts:3691

    Inherited from

    Signer.pk


    validatorGrantorClass

    validatorGrantorClass: string

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:15

    Methods

    getPubKey()

    getPubKey(): Promise<string>

    Defined in: node_modules/starknet/dist/index.d.ts:3693

    Method to get the public key of the signer

    Returns

    Promise<string>

    hex-string

    Example

    const mySigner = new Signer("0x123");
    const result = await mySigner.getPubKey();
    // result = "0x566d69d8c99f62bc71118399bab25c1f03719463eab8d6a444cd11ece131616"
    

    Inherited from

    Signer.getPubKey


    sign()

    sign(module): Promise<ArraySignatureType>

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:21

    Parameters

    module

    SessionKeyModule

    Returns

    Promise<ArraySignatureType>


    signDeclareTransaction()

    signDeclareTransaction(details): Promise<Signature>

    Defined in: node_modules/starknet/dist/index.d.ts:3697

    Signs a DECLARE transaction with the private key and returns the signature

    Parameters

    details

    DeclareSignerDetails

    Returns

    Promise<Signature>

    the signature of the transaction to declare a class

    Example

    const mySigner = new Signer("0x123");
    const myDeclare: DeclareSignerDetails = {
      version: "0x2", chainId: constants.StarknetChainId.SN_SEPOLIA,
      senderAddress: "0x65a822fbee1ae79e898688b5a4282dc79e0042cbed12f6169937fddb4c26641",
      classHash: "0x5f3614e8671257aff9ac38e929c74d65b02d460ae966cd826c9f04a7fa8e0d4",
      nonce: 45, maxFee: 10 ** 15, tip: 0, paymasterData: [], accountDeploymentData: [],
      nonceDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      feeDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      resourceBounds: stark.estimateFeeToBounds(constants.ZERO),
    }
    const result = await mySigner.signDeclareTransaction(myDeclare);
    // result = Signature {r: 2432056944313955951711774394836075930010416436707488863728289188289211995670n,
    //  s: 3407649393310177489888603098175002856596469926897298636282244411990343146307n, recovery: 1}
    

    Inherited from

    Signer.signDeclareTransaction


    signDeployAccountTransaction()

    signDeployAccountTransaction(details): Promise<Signature>

    Defined in: node_modules/starknet/dist/index.d.ts:3696

    Signs a DEPLOY_ACCOUNT transaction with the private key and returns the signature

    Parameters

    details

    DeployAccountSignerDetails

    Returns

    Promise<Signature>

    the signature of the transaction to deploy an account

    Example

    const mySigner = new Signer("0x123");
    const myDeployAcc: DeployAccountSignerDetails = {
      contractAddress: "0x65a822fbee1ae79e898688b5a4282dc79e0042cbed12f6169937fddb4c26641",
      version: "0x2", chainId: constants.StarknetChainId.SN_SEPOLIA,
      classHash: "0x5f3614e8671257aff9ac38e929c74d65b02d460ae966cd826c9f04a7fa8e0d4",
      constructorCalldata: [1, 2],addressSalt: 1234,
      nonce: 45, maxFee: 10 ** 15, tip: 0, paymasterData: [],accountDeploymentData: [],
      nonceDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      feeDataAvailabilityMode: RPC.EDataAvailabilityMode.L1,
      resourceBounds: stark.estimateFeeToBounds(constants.ZERO),
    }
    const result = await mySigner.signDeployAccountTransaction(myDeployAcc);
    // result = Signature {r: 2871311234341436528393212130310036951068553852419934781736214693308640202748n,
    //  s: 1746271646048888422437132495446973163454853863041370993384284773665861377605n, recovery: 1}
    

    Inherited from

    Signer.signDeployAccountTransaction


    signMessage()

    signMessage(typedData, accountAddress): Promise<Signature>

    Defined in: node_modules/starknet/dist/index.d.ts:3694

    Signs a JSON object for off-chain usage with the private key and returns the signature. This adds a message prefix so it can't be interchanged with transactions

    Parameters

    typedData

    TypedData

    JSON object to be signed

    accountAddress

    string

    Hex string of the account's address

    Returns

    Promise<Signature>

    the signature of the message

    Example

    const mySigner = new Signer("0x123");
        const myTypedData: TypedData = {
            domain: {name: "Example DApp",
              chainId: constants.StarknetChainId.SN_SEPOLIA,
              version: "0.0.3"},
            types: {StarkNetDomain: [
                { name: "name", type: "string" },
                { name: "chainId", type: "felt" },
                { name: "version", type: "string" }],
              Message: [{ name: "message", type: "felt" }]},
            primaryType: "Message", message: {message: "1234"}};
        const result = await mySigner.signMessage(myTypedData,"0x5d08a4e9188429da4e993c9bf25aafe5cd491ee2b501505d4d059f0c938f82d");
    // result = Signature {r: 684915484701699003335398790608214855489903651271362390249153620883122231253n,
    // s: 1399150959912500412309102776989465580949387575375484933432871778355496929189n, recovery: 1}
    

    Inherited from

    Signer.signMessage


    signRaw()

    protected signRaw(msgHash): Promise<Signature>

    Defined in: node_modules/starknet/dist/index.d.ts:3698

    Parameters

    msgHash

    string

    Returns

    Promise<Signature>

    Inherited from

    Signer.signRaw


    signTransaction()

    signTransaction(transactions, details): Promise<Signature>

    Defined in: node_modules/starknet/dist/index.d.ts:3695

    Signs transactions with the private key and returns the signature

    Parameters

    transactions

    Call[]

    array of Call objects

    details

    InvocationsSignerDetails

    Returns

    Promise<Signature>

    the signature of the transaction

    Example

    const mySigner = new Signer("0x123");
    const calls: Call[] = [{
        contractAddress: "0x1234567890123456789012345678901234567890",
        entrypoint: "functionName",
        calldata: [1, 2, 3]
    }];
    const transactionsDetail: InvocationsSignerDetails = {
        walletAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
        chainId: constants.StarknetChainId.SN_MAIN,
        cairoVersion: "1",
        maxFee: '0x1234567890abcdef',
        version: "0x0", nonce: 1};
    const result = await mySigner.signTransaction(calls, transactionsDetail);
    // result = Signature {r: 304910226421970384958146916800275294114105560641204815169249090836676768876n,
    //   s: 1072798866000813654190523783606274062837012608648308896325315895472901074693n, recovery: 0}
    

    Inherited from

    Signer.signTransaction

    @0xknwn/starknet-module-sessionkey


    @0xknwn/starknet-module-sessionkey / SessionKeyModule

    Class: SessionKeyModule

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:33

    Implements

    • AccountModuleInterface

    Constructors

    new SessionKeyModule()

    new SessionKeyModule(authKey, accountAddress, validatorClassHash, chainId, expires, policyManager?): SessionKeyModule

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:37

    Parameters

    authKey

    string

    accountAddress

    string

    validatorClassHash

    string

    chainId

    string

    expires

    string

    policyManager?

    PolicyManager

    Returns

    SessionKeyModule

    Properties

    auth

    protected auth: Authorization

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:34


    policyManager?

    protected optional policyManager: PolicyManager

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:35

    Methods

    add_signature()

    add_signature(signature): Promise<void>

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:98

    Parameters

    signature

    string[]

    Returns

    Promise<void>


    get_session_key()

    get_session_key(): Promise<string>

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:83

    Returns

    Promise<string>


    prefix()

    prefix(calls): object

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:112

    Parameters

    calls

    Call | Call[]

    Returns

    object

    calldata

    calldata: string[]

    contractAddress

    contractAddress: string

    entrypoint

    entrypoint: string = "__module_validate__"

    Implementation of

    AccountModuleInterface.prefix


    request()

    request(grantorClass): Promise<Authorization>

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:62

    Parameters

    grantorClass

    string

    Returns

    Promise<Authorization>


    reset()

    reset(signature): Promise<void>

    Defined in: sdks/module-sessionkey/src/sessionkey.ts:105

    Parameters

    signature

    string[]

    Returns

    Promise<void>

    @0xknwn/starknet-test-helpers


    @0xknwn/starknet-test-helpers

    Classes

    @0xknwn/starknet-test-helpers


    @0xknwn/starknet-test-helpers / Counter

    Class: Counter

    Defined in: sdks/__tests__/helpers/src/counter.ts:52

    Represents a Counter contract.

    Extends

    • Contract

    Indexable

    [key: string]: any

    Constructors

    new Counter()

    new Counter(address, account): Counter

    Defined in: sdks/__tests__/helpers/src/counter.ts:58

    Creates an instance of the Counter contract.

    Parameters

    address

    string

    The address of the contract.

    account

    Account

    The account used to interact with the contract.

    Returns

    Counter

    Overrides

    Contract.constructor

    Properties

    abi

    abi: Abi

    Defined in: node_modules/starknet/dist/index.d.ts:4722

    Inherited from

    Contract.abi


    address

    address: string

    Defined in: node_modules/starknet/dist/index.d.ts:4723

    Inherited from

    Contract.address


    callStatic

    readonly callStatic: object

    Defined in: node_modules/starknet/dist/index.d.ts:4733

    Index Signature

    [name: string]: AsyncContractFunction<any>

    Inherited from

    Contract.callStatic


    deployTransactionHash?

    optional deployTransactionHash: string

    Defined in: node_modules/starknet/dist/index.d.ts:4725

    Inherited from

    Contract.deployTransactionHash


    estimateFee

    readonly estimateFee: object

    Defined in: node_modules/starknet/dist/index.d.ts:4739

    Index Signature

    [name: string]: ContractFunction

    Inherited from

    Contract.estimateFee


    events

    protected readonly events: AbiEvents

    Defined in: node_modules/starknet/dist/index.d.ts:4729

    Inherited from

    Contract.events


    functions

    readonly functions: object

    Defined in: node_modules/starknet/dist/index.d.ts:4730

    Index Signature

    [name: string]: AsyncContractFunction<any>

    Inherited from

    Contract.functions


    populateTransaction

    readonly populateTransaction: object

    Defined in: node_modules/starknet/dist/index.d.ts:4736

    Index Signature

    [name: string]: ContractFunction

    Inherited from

    Contract.populateTransaction


    providerOrAccount

    providerOrAccount: ProviderInterface | AccountInterface

    Defined in: node_modules/starknet/dist/index.d.ts:4724

    Inherited from

    Contract.providerOrAccount


    structs

    protected readonly structs: object

    Defined in: node_modules/starknet/dist/index.d.ts:4726

    Index Signature

    [name: string]: AbiStruct

    Inherited from

    Contract.structs

    Methods

    attach()

    attach(address): void

    Defined in: node_modules/starknet/dist/index.d.ts:4752

    Saves the address of the contract deployed on network that will be used for interaction

    Parameters

    address

    string

    address of the contract

    Returns

    void

    Inherited from

    Contract.attach


    call()

    call(method, args?, options?): Promise<Result>

    Defined in: node_modules/starknet/dist/index.d.ts:4755

    Calls a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    ArgsOrCalldata

    Array of the arguments for the call

    options?

    CallOptions

    optional blockIdentifier

    Returns

    Promise<Result>

    Result of the call as an array with key value pars

    Inherited from

    Contract.call


    connect()

    connect(providerOrAccount): void

    Defined in: node_modules/starknet/dist/index.d.ts:4753

    Attaches to new Provider or Account

    Parameters

    providerOrAccount

    new Provider or Account to attach to

    ProviderInterface | AccountInterface

    Returns

    void

    Inherited from

    Contract.connect


    deployed()

    deployed(): Promise<Contract>

    Defined in: node_modules/starknet/dist/index.d.ts:4754

    Resolves when contract is deployed on the network or when no deployment transaction is found

    Returns

    Promise<Contract>

    Promise that resolves when contract is deployed on the network or when no deployment transaction is found

    Throws

    When deployment fails

    Inherited from

    Contract.deployed


    estimate()

    estimate(method, args?): Promise<EstimateFeeResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4757

    Estimates a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    ArgsOrCalldata

    Array of the arguments for the call or Calldata

    Returns

    Promise<EstimateFeeResponse>

    Inherited from

    Contract.estimate


    getVersion()

    getVersion(): Promise<ContractVersion>

    Defined in: node_modules/starknet/dist/index.d.ts:4761

    Retrieves the version of the contract (cairo version & compiler version)

    Returns

    Promise<ContractVersion>

    Inherited from

    Contract.getVersion


    increment()

    increment(): Promise<{ transaction_hash: string; }>

    Defined in: sdks/__tests__/helpers/src/counter.ts:66

    Increments the counter by 1.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the result of the execution.


    increment_by()

    increment_by(value): Promise<{ transaction_hash: string; }>

    Defined in: sdks/__tests__/helpers/src/counter.ts:77

    Increments the counter by the specified value.

    Parameters

    value

    number

    The value to increment the counter by.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the result of the execution.


    increment_by_array()

    increment_by_array(args): Promise<{ transaction_hash: string; }>

    Defined in: sdks/__tests__/helpers/src/counter.ts:106

    Increments the counter by an array of numbers using increment_by_array.

    Parameters

    args

    number[]

    The array of values to increment the counter by.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the result of the execution.


    increment_by_multicall()

    increment_by_multicall(values): Promise<{ transaction_hash: string; }>

    Defined in: sdks/__tests__/helpers/src/counter.ts:88

    Increments the counter by an array of numbers using a multicall of increment_by.

    Parameters

    values

    number[]

    The array of values to increment the counter by.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the result of the execution.


    invoke()

    invoke(method, args?, options?): Promise<{ transaction_hash: string; }>

    Defined in: node_modules/starknet/dist/index.d.ts:4756

    Invokes a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    ArgsOrCalldata

    Array of the arguments for the invoke or Calldata

    options?

    InvokeOptions

    Returns

    Promise<{ transaction_hash: string; }>

    Add Transaction Response

    Inherited from

    Contract.invoke


    isCairo1()

    isCairo1(): boolean

    Defined in: node_modules/starknet/dist/index.d.ts:4760

    tells if the contract comes from a Cairo 1 contract

    Returns

    boolean

    TRUE if the contract comes from a Cairo1 contract

    Example

    const isCairo1: boolean = myContract.isCairo1();
    

    Inherited from

    Contract.isCairo1


    parseEvents()

    parseEvents(receipt): ParsedEvents

    Defined in: node_modules/starknet/dist/index.d.ts:4759

    Parse contract events of a GetTransactionReceiptResponse received from waitForTransaction. Based on contract's abi

    Parameters

    receipt

    GetTransactionReceiptResponse

    transaction receipt

    Returns

    ParsedEvents

    Events parsed

    Inherited from

    Contract.parseEvents


    populate()

    populate(method, args?): Call

    Defined in: node_modules/starknet/dist/index.d.ts:4758

    Calls a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    RawArgs

    Array of the arguments for the call or Calldata

    Returns

    Call

    Invocation object

    Inherited from

    Contract.populate


    reset()

    reset(): Promise<{ transaction_hash: string; }>

    Defined in: sdks/__tests__/helpers/src/counter.ts:120

    Resets the counter.

    Returns

    Promise<{ transaction_hash: string; }>

    A promise that resolves to the result of the execution.

    Remarks

    This function requires the Account used by the Counter to be its owner.


    typedv2()

    typedv2<TAbi>(tAbi): TypedContractV2<TAbi>

    Defined in: node_modules/starknet/dist/index.d.ts:4762

    Returns a typed instance of ContractV2 based on the supplied ABI.

    Type Parameters

    TAbi extends Abi

    Parameters

    tAbi

    TAbi

    The ABI (Abstract Binary Interface) of the ContractV2.

    Returns

    TypedContractV2<TAbi>

    • A typed instance of ContractV2.

    Inherited from

    Contract.typedv2

    @0xknwn/starknet-test-helpers


    @0xknwn/starknet-test-helpers / SwapRouter

    Class: SwapRouter

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:56

    Represents a Swap contract.

    Extends

    • Contract

    Indexable

    [key: string]: any

    Constructors

    new SwapRouter()

    new SwapRouter(address, account): SwapRouter

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:62

    Creates an instance of the Counter contract.

    Parameters

    address

    string

    The address of the contract.

    account

    Account

    The account used to interact with the contract.

    Returns

    SwapRouter

    Overrides

    Contract.constructor

    Properties

    abi

    abi: Abi

    Defined in: node_modules/starknet/dist/index.d.ts:4722

    Inherited from

    Contract.abi


    address

    address: string

    Defined in: node_modules/starknet/dist/index.d.ts:4723

    Inherited from

    Contract.address


    callStatic

    readonly callStatic: object

    Defined in: node_modules/starknet/dist/index.d.ts:4733

    Index Signature

    [name: string]: AsyncContractFunction<any>

    Inherited from

    Contract.callStatic


    deployTransactionHash?

    optional deployTransactionHash: string

    Defined in: node_modules/starknet/dist/index.d.ts:4725

    Inherited from

    Contract.deployTransactionHash


    estimateFee

    readonly estimateFee: object

    Defined in: node_modules/starknet/dist/index.d.ts:4739

    Index Signature

    [name: string]: ContractFunction

    Inherited from

    Contract.estimateFee


    events

    protected readonly events: AbiEvents

    Defined in: node_modules/starknet/dist/index.d.ts:4729

    Inherited from

    Contract.events


    functions

    readonly functions: object

    Defined in: node_modules/starknet/dist/index.d.ts:4730

    Index Signature

    [name: string]: AsyncContractFunction<any>

    Inherited from

    Contract.functions


    populateTransaction

    readonly populateTransaction: object

    Defined in: node_modules/starknet/dist/index.d.ts:4736

    Index Signature

    [name: string]: ContractFunction

    Inherited from

    Contract.populateTransaction


    providerOrAccount

    providerOrAccount: ProviderInterface | AccountInterface

    Defined in: node_modules/starknet/dist/index.d.ts:4724

    Inherited from

    Contract.providerOrAccount


    structs

    protected readonly structs: object

    Defined in: node_modules/starknet/dist/index.d.ts:4726

    Index Signature

    [name: string]: AbiStruct

    Inherited from

    Contract.structs

    Methods

    attach()

    attach(address): void

    Defined in: node_modules/starknet/dist/index.d.ts:4752

    Saves the address of the contract deployed on network that will be used for interaction

    Parameters

    address

    string

    address of the contract

    Returns

    void

    Inherited from

    Contract.attach


    call()

    call(method, args?, options?): Promise<Result>

    Defined in: node_modules/starknet/dist/index.d.ts:4755

    Calls a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    ArgsOrCalldata

    Array of the arguments for the call

    options?

    CallOptions

    optional blockIdentifier

    Returns

    Promise<Result>

    Result of the call as an array with key value pars

    Inherited from

    Contract.call


    connect()

    connect(providerOrAccount): void

    Defined in: node_modules/starknet/dist/index.d.ts:4753

    Attaches to new Provider or Account

    Parameters

    providerOrAccount

    new Provider or Account to attach to

    ProviderInterface | AccountInterface

    Returns

    void

    Inherited from

    Contract.connect


    deployed()

    deployed(): Promise<Contract>

    Defined in: node_modules/starknet/dist/index.d.ts:4754

    Resolves when contract is deployed on the network or when no deployment transaction is found

    Returns

    Promise<Contract>

    Promise that resolves when contract is deployed on the network or when no deployment transaction is found

    Throws

    When deployment fails

    Inherited from

    Contract.deployed


    estimate()

    estimate(method, args?): Promise<EstimateFeeResponse>

    Defined in: node_modules/starknet/dist/index.d.ts:4757

    Estimates a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    ArgsOrCalldata

    Array of the arguments for the call or Calldata

    Returns

    Promise<EstimateFeeResponse>

    Inherited from

    Contract.estimate


    faucet()

    faucet(amount): Promise<GetTransactionReceiptResponse>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:71

    Sends a request to the faucet to receive a specified amount of Token A.

    Parameters

    amount

    Uint256

    The amount of tokens to request from the faucet.

    Returns

    Promise<GetTransactionReceiptResponse>

    A promise that resolves to the transaction receipt once the transfer is complete.


    get_conversion_rate()

    get_conversion_rate(): Promise<bigint>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:115

    Retrieves the conversion rate.

    Returns

    Promise<bigint>

    A promise that resolves to a bigint representing the conversion rate.


    getVersion()

    getVersion(): Promise<ContractVersion>

    Defined in: node_modules/starknet/dist/index.d.ts:4761

    Retrieves the version of the contract (cairo version & compiler version)

    Returns

    Promise<ContractVersion>

    Inherited from

    Contract.getVersion


    invoke()

    invoke(method, args?, options?): Promise<{ transaction_hash: string; }>

    Defined in: node_modules/starknet/dist/index.d.ts:4756

    Invokes a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    ArgsOrCalldata

    Array of the arguments for the invoke or Calldata

    options?

    InvokeOptions

    Returns

    Promise<{ transaction_hash: string; }>

    Add Transaction Response

    Inherited from

    Contract.invoke


    isCairo1()

    isCairo1(): boolean

    Defined in: node_modules/starknet/dist/index.d.ts:4760

    tells if the contract comes from a Cairo 1 contract

    Returns

    boolean

    TRUE if the contract comes from a Cairo1 contract

    Example

    const isCairo1: boolean = myContract.isCairo1();
    

    Inherited from

    Contract.isCairo1


    parseEvents()

    parseEvents(receipt): ParsedEvents

    Defined in: node_modules/starknet/dist/index.d.ts:4759

    Parse contract events of a GetTransactionReceiptResponse received from waitForTransaction. Based on contract's abi

    Parameters

    receipt

    GetTransactionReceiptResponse

    transaction receipt

    Returns

    ParsedEvents

    Events parsed

    Inherited from

    Contract.parseEvents


    populate()

    populate(method, args?): Call

    Defined in: node_modules/starknet/dist/index.d.ts:4758

    Calls a method on a contract

    Parameters

    method

    string

    name of the method

    args?

    RawArgs

    Array of the arguments for the call or Calldata

    Returns

    Call

    Invocation object

    Inherited from

    Contract.populate


    set_conversion_rate()

    set_conversion_rate(rate): Promise<GetTransactionReceiptResponse>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:102

    Sets the conversion rate for the swap router.

    Parameters

    rate

    string

    The conversion rate to be set.

    Returns

    Promise<GetTransactionReceiptResponse>

    A promise that resolves to the transaction receipt once the conversion rate is set.


    set_tokens()

    set_tokens(tokenAAddress, tokenBAddress): Promise<GetTransactionReceiptResponse>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:87

    Sets the token addresses for tokenA and tokenB.

    Parameters

    tokenAAddress

    string

    The address of tokenA.

    tokenBAddress

    string

    The address of tokenB.

    Returns

    Promise<GetTransactionReceiptResponse>

    A promise that resolves to the transaction receipt once the transaction is confirmed.


    swap()

    swap(tokenAAddress, amount): Promise<GetTransactionReceiptResponse>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:125

    Swaps a specified amount of a token for another token.

    Parameters

    tokenAAddress

    string

    The address of the token to be swapped.

    amount

    Uint256

    The amount of the token to be swapped.

    Returns

    Promise<GetTransactionReceiptResponse>

    A promise that resolves to the transaction receipt once the swap is completed.


    swap_maximum_at()

    swap_maximum_at(tokenAAddress, rate, amount): Promise<GetTransactionReceiptResponse>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:178

    Swaps the maximum amount of tokens at a given rate.

    Parameters

    tokenAAddress

    string

    The address of the token A.

    rate

    string

    The rate at which to swap the tokens.

    amount

    Uint256

    The amount of tokens to swap.

    Returns

    Promise<GetTransactionReceiptResponse>

    A promise that resolves to the transaction receipt of the swap.


    swap_minimum_at()

    swap_minimum_at(tokenAAddress, rate, amount): Promise<GetTransactionReceiptResponse>

    Defined in: sdks/__tests__/helpers/src/swap_router.ts:152

    Executes a swap with a minimum rate and amount.

    Parameters

    tokenAAddress

    string

    The address of the token A.

    rate

    string

    The rate of the swap.

    amount

    Uint256

    The amount to swap.

    Returns

    Promise<GetTransactionReceiptResponse>

    A promise that resolves to the transaction receipt of the swap.


    typedv2()

    typedv2<TAbi>(tAbi): TypedContractV2<TAbi>

    Defined in: node_modules/starknet/dist/index.d.ts:4762

    Returns a typed instance of ContractV2 based on the supplied ABI.

    Type Parameters

    TAbi extends Abi

    Parameters

    tAbi

    TAbi

    The ABI (Abstract Binary Interface) of the ContractV2.

    Returns

    TypedContractV2<TAbi>

    • A typed instance of ContractV2.

    Inherited from

    Contract.typedv2

    How to Contribute

    We really appreciate and value contributions to the starknet-modular-account. There are different ways to be involved so do not hesitate to do it.

    Development Guidelines

    Before starting development, please create an issue to open the discussion, validate that the PR is wanted and coordinate with the team.

    Please check this document to make sure your contributions are merged as soon as possible.

    Table of Contents

    Pull Requests (PRs)

    As a contributor, you are expected to fork this repository, work on your own fork and then submit pull requests. The pull requests will be reviewed and eventually merged into the repository. See "Fork-a-Repo" for how to work.

    The typical PR includes:

    • a branch associated with the PR should be rebased to the head of develop
    • documentation that complies with the PR template. Make sure every section of it is properly fullfilled

    Code Reviews

    Maintainers will review your code and ask for changes if necessary you code can be merged into the repository. Be mindful and forgiving as we might not be available right on.

    Many of the issues we are facing during the code review are due to a lack of context. Do not hesitate to explain why you are doing what you are doing.

    IMPORTANT Pay attention to the maintainer's feedback and do the changes accordingly.

    Environment

    The project uses a number of tools. Make sure you have installed them in order to develop and test:

    Once you have forked/cloned the repository, you should create a .env.devnet.json file at the root of the project. You can simply copy the content of .env.template.json if you plan to use the devnet on the default port and with the account associated with the seed 0!

    Useful Commands

    • (1) fetch the project cairo dependencies
    scarb fetch
    
    • (2) build the project artifacts
    scarb build
    
    • (3) execute starknet foundry tests
    # you can also use: snforge test
    scarb test
    
    • (4) start starknet-devnet on the default port
    starknet-devnet --seed=0
    
    • (5) create a configuration file to run the project against the devnet
    cp .env.template.json .env.devnet.json
    
    • (6) install node dependencies
    npm install
    
    • (7) execute the npm test scripts with a specific test suite
    npm run test -- simple_account.test.ts
    
    • (8) start mitmproxy with the browser UI on port 8080 and redirect all the requests to the default starknet. You can then change the url in .env.devnet.json so that you run the tests through http://localhost:8080
    mitmweb --mode reverse:http://localhost:5050
    

    Getting ETH on Sepolia

    To get ETH on Starknet Sepolia

    Learning Starknet/Cairo

    If you are not used to Starknet and Cairo yet, a good starting point is to learn is the list of ressources below:

    Do not hesitate to join the Starkware Discord and some Telegram Developer Groups...

    More questions

    If you have any questions, feel free to post them as an issues.

    Thanks for your time and code!

    Starknet Accounts and Network Updates

    There are already numerous implementations of Abstract Accounts, many of which provide awesome features:

    The 2024 Starknet roadmap provides some details about the expected features. In particular, see Mattéo Georges answer in Dec 2023 that suggests the Paymaster is not yet prioritized.

    Proposals, Whitepapers and Docs for Ethereum Accounts

    • Adam Egyed (@adamegyed), Fangting Liu (@trinity-0111), Jay Paik (@jaypaik), Yoav Weiss (@yoavw), Huawei Gu (@huaweigu), Daniel Lim (@dlim-circle), Zhiyu Zhang (@ZhiyuCircle), "ERC-6900: Modular Smart Contract Accounts and Plugins [DRAFT]," Ethereum Improvement Proposals, no. 6900, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6900.
    • zeroknots (@zeroknots), Konrad Kopp (@kopy-kat), Taek Lee (@leekt), Fil Makarov (@filmakarov), Elim Poon (@yaonam), Lyu Min (@rockmin216), "ERC-7579: Minimal Modular Smart Accounts [DRAFT]," Ethereum Improvement Proposals, no. 7579, December 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7579.
    • Safe Module system that supports an alpha implementation of the Safe{Core} Protocol
    • The Kernel Smart Contract
    • The RIP 7560 that is a proposal to make ERC 4337 mandatory for rollups.

    Module Registry

    • Konrad Kopp (@kopy-kat), zeroknots (@zeroknots), "ERC-7484: Registry Extension for ERC-7579 [DRAFT]," Ethereum Improvement Proposals, no. 7484, August 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7484.

    Module Use-Cases

    Modules help to address advanced scenarios. They are often classified in 4 groups:

    • Roles: e.g. spending limits, associated token: vTokens, NFT(id) or Oracle, multiple factor for administrative privileges, recurring payments, alternative signatures including faceid or fingerprints, zkproof, Merkle tree for Offchain ACL
    • Recovery: e.g. social recovery, custodial recovery, physical devices, other secrets validation
    • Protection: e.g. allow/deny list, freeze account, external whitelisting, Oracle MEV protection
    • Modifiers: e.g. time-lock, cooldown/grace period, bonds

    To dig deeper into some of the many scenarios associated with Module, you can have a look at existing implementations like:

    Other resources

    You might also want to check:

    Contributor Covenant Code of Conduct

    Our Pledge

    We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.

    We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

    Our Standards

    Examples of behavior that contributes to a positive environment for our community include:

    • Demonstrating empathy and kindness toward other people
    • Being respectful of differing opinions, viewpoints, and experiences
    • Giving and gracefully accepting constructive feedback
    • Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
    • Focusing on what is best not just for us as individuals, but for the overall community

    Examples of unacceptable behavior include:

    • The use of sexualized language or imagery, and sexual attention or advances of any kind
    • Trolling, insulting or derogatory comments, and personal or political attacks
    • Public or private harassment
    • Publishing others' private information, such as a physical or email address, without their explicit permission
    • Other conduct which could reasonably be considered inappropriate in a professional setting

    Enforcement Responsibilities

    Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

    Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

    Scope

    This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

    Enforcement

    Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at 0xknwn at gmail dot com. All complaints will be reviewed and investigated promptly and fairly.

    All community leaders are obligated to respect the privacy and security of the reporter of any incident.

    Enforcement Guidelines

    Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

    1. Correction

    Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.

    Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

    2. Warning

    Community Impact: A violation through a single incident or series of actions.

    Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.

    3. Temporary Ban

    Community Impact: A serious violation of community standards, including sustained inappropriate behavior.

    Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.

    4. Permanent Ban

    Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.

    Consequence: A permanent ban from any sort of public interaction within the community.

    Attribution

    This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

    Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.

    For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.

    MIT License

    Copyright (c) 2025 0xknwn

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.