> For the complete documentation index, see [llms.txt](/llms.txt)

# Sui TypeScript Codegen

Generate type-safe TypeScript bindings from onchain Sui Move packages.



The `@mysten/codegen` package automatically generates type-safe TypeScript code from your Move
packages, enabling seamless interaction with your smart contracts from TypeScript applications.

<Callout type="warn">
  This package is currently in development and might have breaking changes.
</Callout>

## Features [#features]

* **Type-safe Move calls**: Generate TypeScript functions with full type safety for calling your
  Move functions
* **BCS type definitions**: Automatic BCS struct definitions for parsing onchain data
* **Auto-completion**: IDE support with intelligent code completion for Move function arguments
* **Package resolution**: Support for both MVR-registered packages and local packages

## Installation [#installation]

Install the codegen package as a dev dependency:

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install -D @mysten/codegen
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add -D @mysten/codegen
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add --dev @mysten/codegen
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add --dev @mysten/codegen
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Quick start [#quick-start]

### Step 1: Create a configuration file [#step-1-create-a-configuration-file]

Create a `sui-codegen.config.ts` file in your project root:

```typescript
import type { SuiCodegenConfig } from '@mysten/codegen';

const config: SuiCodegenConfig = {
	output: './src/contracts',
	packages: [
		{
			package: '@local-pkg/counter',
			path: './move/counter',
		},
	],
};

export default config;
```

### Step 2: Generate TypeScript code [#step-2-generate-typescript-code]

Add a script to your `package.json`:

```json
{
	"scripts": {
		"codegen": "sui-ts-codegen generate"
	}
}
```

Then run:

```bash
pnpm codegen
```

This generates TypeScript code in your configured output directory (for example, `./src/contracts`).

## Configuration options [#configuration-options]

The `SuiCodegenConfig` type supports the following options:

| Option                         | Type                   | Default              | Description                                                                                                                                                           |
| ------------------------------ | ---------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `output`                       | `string`               | -                    | The directory where codegen writes generated code                                                                                                                     |
| `packages`                     | `PackageConfig[]`      | -                    | Array of Move packages to generate code for                                                                                                                           |
| `prune`                        | `boolean`              | `true`               | When enabled, only generates code for the main package and omits dependency modules (dependency types referenced by included types are still generated under `deps/`) |
| `generateSummaries`            | `boolean`              | `true`               | Automatically run `sui move summary` before generating code. Creates a `package_summaries` directory in your Move package which can be added to `.gitignore`          |
| `generate`                     | `GenerateOptions`      | -                    | Default [generate options](#the-generate-option) (types, functions) for all packages                                                                                  |
| `importExtension`              | `'.js' \| '.ts' \| ''` | `'.js'`              | File extension used in generated import statements                                                                                                                    |
| `includePhantomTypeParameters` | `boolean`              | `false`              | Include [phantom type parameters](#phantom-types) as function arguments in generated BCS types                                                                        |
| `fullnodeUrls`                 | network URL map        | Sui public fullnodes | Override the Mainnet or Testnet fullnode used to fetch onchain package summaries                                                                                      |

### Package configuration [#package-configuration]

Each entry in the `packages` array configures a Move package to generate code from. Packages can be
local (from source) or onchain (fetched from a network).

#### Local packages [#local-packages]

| Option            | Type                     | Required | Description                                                                                      |
| ----------------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------ |
| `package`         | `string`                 | yes      | Package identifier (for example, `@local-pkg/my-package`)                                        |
| `path`            | `string`                 | yes      | Path to the Move package directory                                                               |
| `packageName`     | `string`                 | no       | Custom name for generated code directory                                                         |
| `generate`        | `PackageGenerateOptions` | no       | Control what gets generated from this package                                                    |
| `configArguments` | `ConfigArguments`        | no       | Map this package's function parameters to a [runtime config object](#the-configarguments-option) |
| `bcsOverrides`    | `BcsOverrides`           | no       | Replace generated BCS types with [custom types](#the-bcsoverrides-option)                        |

```typescript
{
  package: '@local-pkg/my-package',
  path: './move/my-package',
}
```

#### Onchain packages [#onchain-packages]

For packages already deployed onchain, generate code directly from a package ID or MVR name without
needing local source code:

| Option            | Type                     | Required | Description                                                                                      |
| ----------------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------ |
| `package`         | `string`                 | yes      | Package ID or name emitted into generated code                                                   |
| `sourcePackageId` | `string`                 | no       | Package ID to fetch when generating an unpublished package name                                  |
| `packageName`     | `string`                 | yes      | Name for the generated code directory                                                            |
| `network`         | `'mainnet' \| 'testnet'` | yes      | Network to resolve and fetch the package from                                                    |
| `generate`        | `PackageGenerateOptions` | no       | Control what gets generated from this package                                                    |
| `configArguments` | `ConfigArguments`        | no       | Map this package's function parameters to a [runtime config object](#the-configarguments-option) |
| `bcsOverrides`    | `BcsOverrides`           | no       | Replace generated BCS types with [custom types](#the-bcsoverrides-option)                        |

```typescript
{
  package: '0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837',
  packageName: 'pyth',
  network: 'testnet',
}
```

## The `generate` option [#the-generate-option]

The `generate` option controls what code is produced. It can be set at the global level (as a
default for all packages), at the per-package level, and at the per-module level. More specific
settings override less specific ones.

When no `generate` option is set, everything is generated (all types and functions). Package-level
`types` and `functions` also default to `true`. In the record form of `modules`, per-module `types`
and `functions` default to `false`, so you opt in to exactly what you need from each module. Use
`true` as a shorthand to include everything from a module with package-level defaults.

At the **global** and **package** levels, `types` and `functions` only accept boolean values (or an
object for `functions`). Name-based filtering with `string[]` is only available at the **module**
level inside the record form of `modules`, where the filter applies unambiguously to a single
module.

```typescript
// Global or package level
generate: {
  types: true | false,
  functions: true | false | { private: boolean | 'entry' },
  modules: string[] | Record<string, true | { types?, functions? }>,  // package-level only
}

// Module level (inside the record form of modules)
modules: {
  my_module: true,  // shorthand for "include everything"
  other_module: {
    types: true | false | string[],
    functions: true | false | string[] | { private: boolean | 'entry' },
  }
}
```

### Types [#types]

Controls which BCS type definitions (structs and enums) are generated:

* `true`: generate all types
* `false`: skip type generation
* `string[]`: generate only the listed types by name (module level only)

### Functions [#functions]

Controls which Move function wrappers are generated:

* `true`: generate all public functions and private entry functions
* `false`: skip function generation
* `string[]`: generate only the listed functions by name; includes private functions (module level
  only)
* `{ private: 'entry' }`: generate public functions plus private entry functions
* `{ private: true }`: generate all functions including private
* `{ private: false }`: only generate public functions

### Modules [#modules]

Controls which modules from the package are included. Only available at the package level, not at
the global level.

* Not set (default): include all modules
* `string[]`: only include the listed modules
* `Record<string, true | { types?, functions? }>`: only include the listed modules, with per-module
  overrides for `types` and `functions`. Use `true` as a shorthand to include everything from a
  module with package-level defaults

### Examples [#examples]

Only generate code from specific modules of the Sui framework:

```typescript
{
  package: '0x0000000000000000000000000000000000000000000000000000000000000002',
  packageName: '0x2',
  network: 'testnet',
  generate: {
    modules: ['kiosk', 'kiosk_extension', 'transfer_policy'],
  },
}
```

Only generate a single type from a dependency (functions are omitted automatically because
`generate` is configured and `functions` is not specified):

```typescript
{
  package: '0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837',
  packageName: 'pyth',
  network: 'testnet',
  generate: {
    modules: {
      state: { types: ['State'] },
    },
  },
}
```

Generate specific types and functions from individual modules:

```typescript
{
  package: '@local-pkg/my-package',
  path: './move/my-package',
  generate: {
    modules: {
      token: {
        types: ['Token', 'TokenMetadata'],
        functions: ['mint', 'burn', 'transfer'],
      },
      admin: {
        types: true,
        functions: ['initialize'],
      },
    },
  },
}
```

Generate all types but include all private functions for a local package:

```typescript
{
  package: '@local-pkg/my-package',
  path: './move/my-package',
  generate: {
    functions: { private: true },
  },
}
```

### Dependency pruning [#dependency-pruning]

The global `prune` option (default: `true`) controls whether dependency packages are included in the
output. Even when pruning is enabled, dependency types referenced by your included types are still
generated under `deps/`:

```
src/contracts/
├── mypackage/
│   ├── module_a.ts
│   ├── module_b.ts
│   └── deps/
│       └── 0x2/
│           └── balance.ts     # Auto-included dependency type
└── utils/
    └── index.ts               # Shared utilities (always generated)
```

Set `prune: false` to generate all dependency modules with their full types and functions.

## The `configArguments` option [#the-configarguments-option]

SDKs built on generated bindings often spend a lot of wrapper code passing values from a per-network
config object (package IDs, registry or treasury object IDs, pool addresses) into generated function
calls. The `configArguments` option moves that mapping into codegen: declare which Move types (or
package addresses) come from a config object, and the generated functions accept that config object
directly.

`configArguments` is set on a package entry and maps config keys to matchers. Matchers reference
types as `module::Type`, optionally qualified with a package identifier from the `packages` config
or an address:

```typescript
const config: SuiCodegenConfig = {
	output: './src/contracts',
	packages: [
		{
			package: '@myapp/core',
			path: './move/core',
			configArguments: {
				// Every parameter of this type resolves from `config.registry`
				registry: { type: 'registry::Registry' },
				// Generic type: matches every instantiation, config value must be a resolver function
				pool: { type: 'pool::Pool' },
				// Instantiated generic: only matches this exact instantiation
				suiPool: { type: 'pool::Pool<0x2::sui::SUI>' },
				// Function matcher: configures a single function's parameter
				adminCap: { function: 'admin::set_fees', parameterName: 'cap' },
				// Package entry: adds a config key that overrides the package address used for calls
				corePackageId: { package: '@myapp/core' },
			},
		},
		{
			package: '@myapp/vaults',
			path: './move/vaults',
			configArguments: {
				// Types from other packages are referenced by their package identifier
				vaultPool: { type: '@myapp/core::pool::Pool' },
			},
		},
	],
};
```

Matcher rules:

* A bare `module::Type` refers to a type from the package the config is declared on.
* Types from other packages in the run use their package identifier (for example,
  `@myapp/core::pool::Pool`) and must be dependencies of the declaring package.
* Types can also be referenced by an explicit address (for example, `0x2::sui::SUI`). Addresses
  aren't validated, so only use addresses that are valid on every network your generated code
  targets.
* Partially instantiated generics like `Pool<T>` are not supported. Use an uninstantiated matcher
  with a resolver function instead.
* Function matchers can target a parameter by `parameterName` or `parameterIndex`. Both can be
  omitted when the function has a single argument.
* A key can declare an array of matchers. If they span multiple types, the config value must be a
  resolver function.
* Invalid matchers fail at generation time, and keys that never match any generated parameter
  produce a warning.

### Generated output [#generated-output]

For each function with matched parameters, the generated options gain an optional `config` property
typed with the keys that function uses. Matched parameters become optional in `arguments`, and an
explicitly passed argument always takes precedence over the config value:

```typescript
export interface BorrowOptions {
	package?: string;
	arguments: BorrowArguments; // `pool` is optional here
	config?: {
		pool: (ctx: ConfigResolverContext) => string | TransactionObjectArgument;
		corePackageId?: string;
	};
	typeArguments: [string];
}

export function borrow(options: BorrowOptions) {
	const packageAddress = options.package ?? options.config?.corePackageId ?? '@myapp/core';
	// ...
	pool: options.arguments?.pool ?? options.config?.pool?.({ ... }),
}
```

Keys bound to a single concrete type accept a plain value (an object ID or a transaction argument).
Keys that match a generic type, or multiple types, must be resolver functions, because a single ID
can't be correct for every instantiation. A resolver can also return a transaction callback
(`(ctx) => (tx) => ...`) to build the argument dynamically. Resolvers receive the matched
parameter's own type arguments plus call-site metadata:

```typescript
export interface ConfigResolverContext {
	typeArguments: string[]; // canonical for concrete instantiations; as-provided for generic positions
	packageAddress: string;
	moduleName: string;
	functionName: string;
	parameterName?: string; // Move parameter name, when the summary includes names
	parameterIndex: number; // position in the generated function's arguments
}
```

This makes resolvers reusable across functions that use the type in different positions:

```typescript
const myConfig = {
	registry: '0x123...',
	pool: (ctx: ConfigResolverContext) => poolsByCoinType[ctx.typeArguments[0]],
	suiPool: '0x456...',
	adminCap: '0x789...',
	corePackageId: '0xabc...',
} satisfies CoreConfig;

tx.add(
	borrow({
		arguments: { amount: 100n },
		config: myConfig,
		typeArguments: ['0x2::sui::SUI'],
	}),
);
```

Each package's output also includes a `config-arguments.ts` file with an interface covering the
package's config keys, named after the package's `packageName` (for example, `packageName: 'core'`
produces `CoreConfig`). Use it with `satisfies` when defining your config object, as shown above. An
SDK spanning multiple packages can check one shared config object against the intersection of the
per-package interfaces:

```typescript
const myConfig = { ... } satisfies CoreConfig & MarginConfig;
```

### Name refinement [#name-refinement]

When a signature has two parameters of the same matched type (for example, `base_pool` and
`quote_pool`, both `Pool<T>`), a bare type matcher matches both: the key's config value must then be
a resolver function, which receives each parameter's own context. To give each parameter its own
config key instead, refine the matchers with the Move parameter names:

```typescript
configArguments: {
	basePool: { type: 'pool::Pool', parameterName: 'base_pool' },
	quotePool: { type: 'pool::Pool', parameterName: 'quote_pool' },
},
```

Parameter names are only available in summaries generated from local packages. For onchain packages
without parameter names, use function matchers with `parameterIndex` to target individual
parameters.

### Package address precedence [#package-address-precedence]

For package entries, the address used for a generated call is resolved in this order:

1. An explicit `options.package` argument
2. The config key declared by the package entry (for example, `config.corePackageId`)
3. The generated default (the package's MVR name or address)

On networks where the package's MVR name doesn't resolve, supply the deployed package ID through the
config object. Package entries only apply to the main package's generated modules, and for packages
generated without an MVR name or address `package` remains required.

## The `bcsOverrides` option [#the-bcsoverrides-option]

Generated BCS types mirror the raw Move layout, but the raw layout is often not the shape an SDK
wants to expose: a `u64` field might be a 1e9 fixed-point price, or a struct like
`i64::I64 { magnitude: u64, is_negative: bool }` is really a signed integer. The `bcsOverrides`
option replaces generated BCS types with custom ones — typically built with
[`transform`](/bcs#transforms) — so parsed values come out in the shape you want and inputs accept
it.

`bcsOverrides` is set on a package entry as an array. Every entry names the Move `type` it replaces
and the `source` module the replacement is imported from, and can optionally narrow the entry to
specific `fields`:

```typescript
const config: SuiCodegenConfig = {
	output: './src/contracts',
	packages: [
		{
			package: '@local-pkg/deepbook_predict',
			path: './move/deepbook_predict',
			bcsOverrides: [
				// Replace a datatype's generated declaration: every generated layout that
				// references it uses the custom type. Imports `I64` from the source module.
				{ type: 'fixed_math::i64::I64', source: './src/bcs/i64.ts' },

				// `fields` narrows an entry to the field sites matching a glob. Entries are tried
				// in declaration order and the first match wins, so narrow entries go first.
				{ type: 'u64', fields: 'order::*.*_price', source: './src/bcs/units.ts#Price9' },

				// A type with no generated declaration is replaced wherever it is rendered —
				// here, every remaining `u64` parses to a bigint instead of a decimal string.
				{ type: 'u64', source: './src/bcs/integers.ts#U64' },

				// A whole-type match wins over its elements, so this replaces the vector itself
				// rather than letting the `u64` rule apply to its items.
				{ type: 'vector<u64>', source: './src/bcs/units.ts#Payouts' },
			],
		},
	],
};
```

The replacement module exports plain BCS types:

```typescript
// ./src/bcs/units.ts
import { bcs } from '@mysten/sui/bcs';

/** 1e9 fixed-point u64 exposed as a decimal number. */
const Price9 = bcs.u64().transform({
	input: (value: number) => BigInt(Math.round(value * 1e9)).toString(),
	output: (raw) => Number(raw) / 1e9,
});

export const Payouts = bcs.vector(Price9);
```

The custom type's inferred input and output types flow through the generated code, so
`Node.parse(bytes).levels` is a `number[]` and `Node.serialize` accepts one.

Entry rules:

* `type` uses the same package scoping as `configArguments` matchers (bare `module::Type` for the
  declaring package, package identifiers, explicit addresses), and additionally accepts
  named-address labels from the package's summaries (for example, `fixed_math::i64::I64`) so entries
  can target dependency packages that are not codegen-run entries.
* A datatype entry written without type arguments or `fields` replaces the type's generated
  **declaration**: the module exports the custom type under the Move type's name, and every use
  picks it up because uses already reference the declaration. For a generic type the source must
  export a function `(...typeParameters: BcsType<any>[]) => BcsType<any>` mirroring the generated
  call convention.
* Every other entry replaces the type **wherever it is rendered**. That covers types with no
  declaration to replace (primitives, `vector`, and the stdlib types generated layouts serialize
  inline — `String`, `Option`, `ID`/`UID`), a single instantiation of a generic
  (`pool::Pool<0x2::sui::SUI>`), and any entry narrowed with `fields`.
* Replacement applies at **any depth**, because the type renderer consults overrides as it recurses.
  An override on `u64` also replaces the `u64` inside `vector<u64>` and `Option<u64>`. A whole-type
  match wins over its elements, so an entry for `vector<u64>` replaces the vector rather than its
  items.
* `fields` restricts an entry to the field sites matching one glob, where `*` matches any run of
  characters. Sites are written `module::Type.field`, or `module::Type.variant.field` for enum
  variant fields (positional fields are named `pos0`, `pos1`, …). A glob that names no module —
  `Order.*_price` — is matched against the `Type.field` suffix, so it applies in every module. An
  entry with `fields` never replaces a declaration, since a declaration exists once for every use.
* At each use site, matching entries are tried in **declaration order and the first match wins**. An
  unrestricted datatype declaration remains the fallback for sites not matched by an earlier
  field-restricted entry. There is no specificity scoring, so put narrow entries before broad ones.
* `source` is an import specifier, optionally suffixed with `#ExportName`. Relative specifiers
  resolve against the config file's directory and are rewritten into relative imports from each
  generated file, with their extension replaced by the configured `importExtension` (so a
  `./src/bcs/units.ts` source is imported as `units.js` by default); bare package specifiers are
  emitted as-is. Without a fragment, datatype entries import the Move type's name; entries whose
  `type` is a primitive or `vector` must include one.
* Replacement modules must be self-contained: a module whose export replaces a declaration can't
  import generated modules that reference the replaced type, because that would create a circular
  import. Declare the raw layout inline when building a transform.
* A replacement built with `.transform()` is a plain `BcsType`, so a type replaced that way loses
  the `MoveStruct` helpers (`typeTag()`, `resolveTypeTag()`, `get()`, `getMany()`). Extend
  `MoveStruct` instead if the replaced type needs them.
* An entry that matches nothing is not an error. A shared set of overrides can be applied to several
  packages even when only some of them have fields of a given type.

Overrides only affect generated BCS layouts (parsing and serializing structs, enums, and events,
including `MoveStruct.get()`). Generated transaction-building functions serialize their arguments
from Move type tags at runtime, so pure function arguments are unaffected.

## Phantom types [#phantom-types]

In Move, phantom type parameters are type parameters that only appear at the type level and don't
affect the runtime data layout of a struct. For example, `Balance<T>` has a phantom type parameter
`T` that indicates the coin type, but the actual serialized data only contains a `u64` value:

```move
public struct Balance<phantom T> has store {
    value: u64,
}
```

### Default behavior [#default-behavior]

By default, codegen excludes phantom type parameters from the generated BCS type functions because
they don't affect serialization. The generated type is a constant rather than a function:

```typescript
export const Balance = new MoveStruct({
	name: `${$moduleName}::Balance<phantom T>`,
	fields: {
		value: bcs.u64(),
	},
});
```

This works correctly for parsing onchain data because phantom types don't change the binary layout.

<Callout type="info">
  With the default behavior, phantom parameters appear as literals in the type name (for example,
  `Balance<phantom T>`). These names are useful for debugging but are not valid onchain type tags. Use
  the [`typeTag` method](#getting-type-tags) to build valid type tags with the phantom parameters
  filled in.
</Callout>

### Including phantom type parameters [#including-phantom-type-parameters]

If you need the phantom type parameters as function arguments (for example, to preserve type
information for other tooling), enable `includePhantomTypeParameters`:

```typescript
const config: SuiCodegenConfig = {
	output: './src/contracts',
	includePhantomTypeParameters: true,
	packages: [
		// ...
	],
};
```

With this option enabled, phantom type parameters become function arguments:

```typescript
export function Balance<T extends BcsType<any>>(T: T) {
	return new MoveStruct({
		name: `${$moduleName}::Balance<${T.name}>` as const,
		fields: {
			value: bcs.u64(),
		},
	});
}
```

## Using generated code [#using-generated-code]

### Calling Move functions [#calling-move-functions]

The generated code provides type-safe functions for calling Move functions:

```typescript
import { Transaction } from '@mysten/sui/transactions';
import * as counter from './contracts/counter/counter';

// Increment a counter
const tx = new Transaction();
tx.add(
	counter.increment({
		arguments: {
			counter: '0x123...', // Counter object ID
		},
	}),
);
```

### Parsing BCS data [#parsing-bcs-data]

Use generated BCS types to parse onchain object data. Fetch the object with
`include: { content: true }` and pass `object.content` to the generated type's `.parse()` method:

```typescript
import { Counter as CounterStruct } from './contracts/counter/counter';

async function readCounter(client: ClientWithCoreApi, id: string) {
	const { object } = await client.core.getObject({
		objectId: id,
		include: { content: true },
	});

	// Parse the Move struct fields from BCS content
	const parsed = CounterStruct.parse(object.content);
	console.log('Counter value:', parsed.value);
	console.log('Counter owner:', parsed.owner);

	return parsed;
}
```

<Callout type="warn">
  Always use `content`, not `objectBcs`, when parsing with generated types. The `objectBcs` field
  contains a full object envelope with additional metadata that causes parsing to fail. See
  [Querying data](/sui/clients/querying#objectbcs) for details.
</Callout>

### Getting type tags [#getting-type-tags]

Generated types build their own type tag strings with the `typeTag` method, so you don't hand-write
strings like `` `${packageId}::module::Name<${coinType}>` ``. By default the tag uses the package
the type was generated from — a real address for framework types, or the configured name for a local
or MVR package:

```typescript
import { Counter } from './contracts/counter/counter';
import { Balance } from './contracts/counter/deps/sui/balance';

Balance.typeTag({ typeArguments: ['0x2::sui::SUI'] });
// '0x2::balance::Balance<0x2::sui::SUI>'

Counter.typeTag();
// '@local-pkg/counter::counter::Counter'
```

Types with phantom type parameters require `typeArguments`; types without them take none. This is
enforced at compile time:

```typescript
Counter.typeTag(); // ok — no type parameters
Balance.typeTag({ typeArguments: ['0x2::sui::SUI'] }); // ok

// @ts-expect-error — Balance has a phantom parameter, typeArguments is required
Balance.typeTag();
```

`typeArguments` is the full positional list, in Move declaration order. Each entry is a type tag
string, another `typeTag()` result, or a BCS type (its `name` is used):

```typescript
import { bcs } from '@mysten/sui/bcs';

Balance.typeTag({ typeArguments: [bcs.u64()] });
// '0x2::balance::Balance<u64>'
```

To override the package identifier — for example, to pin a specific published address — pass
`package`:

```typescript
Counter.typeTag({ package: '0xPACKAGE_ID' });
// '0xPACKAGE_ID::counter::Counter'
```

### Resolving type tags [#resolving-type-tags]

For a local or MVR package, `typeTag` returns the configured name (`@local-pkg/counter::…`). That is
valid in transaction `typeArguments` — it resolves automatically when the transaction is built — but
query filters and comparisons against onchain data need a resolved, address-only tag.
`resolveTypeTag` takes the same options as `typeTag` plus a `client`, resolves any names through it,
and normalizes the result:

```typescript
const counterType = await Counter.resolveTypeTag({ client });
// '0x0000…0123::counter::Counter'

const balanceType = await Balance.resolveTypeTag({
	client,
	typeArguments: ['0x2::sui::SUI'],
});
// '0x0000…0002::balance::Balance<0x0000…0002::sui::SUI>'
```

## Client configuration [#client-configuration]

### Using with MVR (Move Version Registry) [#using-with-mvr-move-version-registry]

If your package is registered on MVR, the generated code works without additional configuration. The
configured name is preserved in generated function targets and type tags. At runtime MVR resolves
function packages and type origins independently for the client's network.

### Local packages [#local-packages-1]

For local packages using `@local-pkg/*` identifiers, configure package overrides for Move calls and
first-level type overrides for each local type used as a transaction type argument or resolved for a
query:

```typescript
import { SuiGrpcClient } from '@mysten/sui/grpc';

const client = new SuiGrpcClient({
	network: 'testnet',
	baseUrl: 'https://fullnode.testnet.sui.io:443',
	mvr: {
		overrides: {
			packages: {
				'@local-pkg/counter': '0xYOUR_PACKAGE_ID',
			},
			types: {
				'@local-pkg/counter::counter::Counter': '0xTYPE_ORIGIN::counter::Counter',
			},
		},
	},
});
```

### With dApp Kit [#with-dapp-kit]

Configure package overrides when creating your dApp Kit instance:

```typescript
import { createDAppKit } from '@mysten/dapp-kit-core';
import { SuiGrpcClient } from '@mysten/sui/grpc';

const GRPC_URLS = {
	testnet: 'https://fullnode.testnet.sui.io:443',
};

const PACKAGE_IDS = {
	testnet: {
		counter: '0xYOUR_PACKAGE_ID',
		counterType: '0xTYPE_ORIGIN::counter::Counter',
	},
};

const dAppKit = createDAppKit({
	networks: ['testnet'],
	createClient: (network) => {
		return new SuiGrpcClient({
			network,
			baseUrl: GRPC_URLS[network],
			mvr: {
				overrides: {
					packages: {
						'@local-pkg/counter': PACKAGE_IDS[network].counter,
					},
					types: {
						'@local-pkg/counter::counter::Counter': PACKAGE_IDS[network].counterType,
					},
				},
			},
		});
	},
});
```

## Related resources [#related-resources]

* [create-dapp](/dapp-kit/getting-started/create-dapp) - Bootstrap a working app with codegen
  already configured
* [Sui Move documentation](https://docs.sui.io/concepts/sui-move-concepts)
* [BCS documentation](/bcs)
* [Transaction building](/sui/transactions/basics)
* [dApp Kit](/dapp-kit)
