Skip to main content
Version: 11.x

Plugin System

The plugin system lets you extend RpcProvider and Account with custom methods and lifecycle hooks that can intercept RPC requests, execute(), and signMessage().

Default Plugins​

Four plugins are pre-installed on every RpcProvider and Account:

  • StarknetId — Starknet Name Service integration
  • BrotherId — Brother.id naming service integration
  • FastExecute — gaming-optimized rapid transaction execution
  • Paymaster — SNIP-29 paymaster transactions (methods on Account only)
import { RpcProvider, Account, BlockTag } from 'starknet';

const provider = new RpcProvider({
nodeUrl,
blockIdentifier: BlockTag.PRE_CONFIRMED, // required by fastExecute
});
const account = new Account({ provider, address, signer: privateKey });

// StarknetId
const name = await provider.getStarkName(address);
const addr = await provider.getAddressFromStarkName('example.stark');

// BrotherId
const brotherName = await provider.getBrotherName(address);

// FastExecute
const resp = await account.fastExecute(call, { tip: estimatedTip });

// On Account, address-based methods default to account.address
const myName = await account.getStarkName();

Choosing Plugins​

import { RpcProvider, starknetIdPlugin, brotherId, fastExecute, defaultPlugins } from 'starknet';

// No plugins — plugin methods throw
const bare = new RpcProvider({ nodeUrl, plugins: false });

// Only specific plugins
const provider = new RpcProvider({ nodeUrl, plugins: [starknetIdPlugin()] });

// Default plugins + your own
const custom = new RpcProvider({
nodeUrl,
plugins: [...defaultPlugins, myCustomPlugin()],
});
note

The StarknetId plugin factory is exported as starknetIdPlugin, not starknetId: the bare starknetId name is already used by the utility namespace (starknetId.useEncoded, starknetId.isStarkDomain, ...). For the same reason, the Paymaster plugin factory is exported as paymasterPlugin: paymaster is also a utility namespace. The two other factories keep their plain names, brotherId() and fastExecute().

The same plugins option is available on Account. There, plugins: false or a list without paymasterPlugin() also removes the paymaster methods. The paymaster option of the account is the exception: it always installs the paymaster plugin, and it replaces the one of the list.

Runtime Installation​

Add plugins after construction with .use(). TypeScript infers the added methods:

const provider = new RpcProvider({ nodeUrl, plugins: false });

const extended = provider.use(starknetIdPlugin());
await extended.getStarkName(address); // ✅ fully typed

Creating Custom Plugins​

Adding methods​

A plugin is a factory returning an object with a name and optional extend / accountExtend:

import type { StarknetPlugin, ProviderInterface, AccountInterface } from 'starknet';

interface ProviderMethods {
getCustomData(address: string): Promise<string>;
}
interface AccountMethods {
getMyCustomData(): Promise<string>;
}

export function myPlugin(): StarknetPlugin<ProviderMethods, AccountMethods> {
return {
name: 'my-plugin',

// Methods for Provider instances
extend: (provider: ProviderInterface) => ({
getCustomData: async (address: string) => `Custom data for ${address}`,
}),

// Methods for Account instances (falls back to extend() if omitted)
accountExtend: (account: AccountInterface) => ({
getMyCustomData: async () => `Custom data for ${account.address}`,
}),
};
}

Adding lifecycle hooks​

Hooks intercept requests and account actions. Returning a value overrides the request/result; returning nothing leaves it unchanged.

import type { StarknetPlugin } from 'starknet';

export function loggingPlugin(): StarknetPlugin {
return {
name: 'logging-plugin',

hooks: {
beforeRequest: ({ method, params }) => {
console.log(`[RPC] ${method}`, params);
return { method, params }; // optionally modified
},
afterRequest: ({ method, result }) => {
console.log(`[RPC] ${method} done`, result);
return result;
},
},

accountHooks: {
beforeExecute: ({ calls, details }) => ({ calls, details }),
afterExecute: ({ result }) => console.log(`tx: ${result.transaction_hash}`),
beforeSign: ({ typedData }) => ({ typedData }),
afterSign: ({ signature }) => console.log('signed'),
},
};
}

Methods and hooks can be combined in a single plugin — e.g. an accountHooks.afterExecute that pushes each hash into a closure array, exposed through extend.

Plugin API Reference​

interface StarknetPlugin<
TProviderMethods extends Record<string, any> = Record<string, never>,
TAccountMethods extends Record<string, any> = TProviderMethods,
> {
readonly name: string;
extend?(provider: ProviderInterface): TProviderMethods;
accountExtend?(account: AccountInterface): TAccountMethods; // falls back to extend()
hooks?: ProviderHooks;
accountHooks?: AccountHooks;
}

interface ProviderHooks {
beforeRequest?(ctx: { method: string; params: any }): { method: string; params: any } | void;
afterRequest?(ctx: { method: string; params: any; result: any }): any | void;
}

interface AccountHooks extends ProviderHooks {
beforeExecute?(ctx: {
calls: AllowArray<Call>;
details: UniversalDetails;
}): { calls: AllowArray<Call>; details: UniversalDetails } | void;
afterExecute?(ctx: { calls: AllowArray<Call>; result: InvokeFunctionResponse }): void;
beforeSign?(ctx: { typedData: TypedData }): { typedData: TypedData } | void;
afterSign?(ctx: { typedData: TypedData; signature: Signature }): void;
}

Built-in Plugins Reference​

FastExecute​

Rapid transaction execution with minimal confirmation latency, for gaming and high-frequency use.

const resp = await account.fastExecute(
call,
{ tip: recommendedTip },
{ retries: 30, retryInterval: 500 }
);

if (!resp.isReady) {
// fast mode timed out — fall back to a normal wait
await provider.waitForTransaction(resp.txResult.transaction_hash);
}

Requirements: RPC 0.9+, and the provider must be created with blockIdentifier: BlockTag.PRE_CONFIRMED.

Limitations: events/receipts aren't immediately available, not suitable for deployment, and best used sparingly.

Methods:

  • Provider — fastWaitForTransaction(txHash, address, initNonce, options?) → Promise<boolean> (polls at PRE_CONFIRMED finality)
  • Account — fastExecute(transactions, details?, waitDetail?) → Promise<FastExecuteResponse>
type FastWaitForTransactionOptions = {
retries?: number; // default 50
retryInterval?: number; // ms, default 500
};

type FastExecuteResponse = {
txResult: InvokeFunctionResponse;
isReady: boolean; // ready to send the next transaction immediately
};

StarknetId​

Starknet Name Service integration. Methods can also be called statically via StarknetIdImpl.

  • getStarkName(address, contract?)
  • getAddressFromStarkName(name, contract?)
  • getStarkProfile(address, contract?)

On Account, getStarkName() defaults to account.address.

BrotherId​

Brother.id naming service integration. Static methods available via BrotherIdImpl.

  • getBrotherName(address, contract?)
  • getAddressFromBrotherName(name, contract?)
  • getBrotherProfile(address, contract?)