Sui TypeScript Codegen
The @mysten/codegen package automatically generates type-safe TypeScript code from your Move
packages, enabling seamless interaction with your smart contracts from TypeScript applications.
This package is currently in development and may have breaking changes.
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 on-chain 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
Install the codegen package as a dev dependency:
npm install -D @mysten/codegenQuick Start
1. Create a configuration file
Create a sui-codegen.config.ts file in your project root:
import type { SuiCodegenConfig } from '@mysten/codegen';
const config: SuiCodegenConfig = {
output: './src/contracts',
packages: [
{
package: '@local-pkg/counter',
path: './move/counter',
},
],
};
export default config;2. Generate TypeScript code
Add a script to your package.json:
{
"scripts": {
"codegen": "sui-ts-codegen generate"
}
}Then run:
pnpm codegenThis generates TypeScript code in your configured output directory (e.g., ./src/contracts).
Configuration Options
The SuiCodegenConfig type supports the following options:
| Option | Type | Default | Description |
|---|---|---|---|
output | string | — | The directory where generated code will be written |
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 (types, functions) for all packages |
importExtension | '.js' | '.ts' | '' | '.js' | File extension used in generated import statements |
includePhantomTypeParameters | boolean | false | Include phantom type parameters as function arguments in generated BCS types |
Package Configuration
Each entry in the packages array configures a Move package to generate code from. Packages can be
local (from source) or on-chain (fetched from a network).
Local Packages
| Option | Type | Required | Description |
|---|---|---|---|
package | string | yes | Package identifier (e.g., @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 |
{
package: '@local-pkg/my-package',
path: './move/my-package',
}On-Chain Packages
For packages already deployed on-chain, 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 MVR name |
packageName | string | yes | Name for the generated code directory |
network | 'mainnet' | 'testnet' | yes | Network to fetch the package from |
generate | PackageGenerateOptions | no | Control what gets generated from this package |
{
package: '0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837',
packageName: 'pyth',
network: 'testnet',
}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 — 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.
// 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
Controls which BCS type definitions (structs and enums) are generated:
true— generate all typesfalse— skip type generationstring[]— generate only the listed types by name (module level only)
Functions
Controls which Move function wrappers are generated:
true— generate all public functions and private entry functionsfalse— skip function generationstring[]— 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
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 modulesRecord<string, true | { types?, functions? }>— only include the listed modules, with per-module overrides fortypesandfunctions. Usetrueas a shorthand to include everything from a module with package-level defaults
Examples
Only generate code from specific modules of the Sui framework:
{
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 since generate
is configured and functions is not specified):
{
package: '0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837',
packageName: 'pyth',
network: 'testnet',
generate: {
modules: {
state: { types: ['State'] },
},
},
}Generate specific types and functions from individual modules:
{
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:
{
package: '@local-pkg/my-package',
path: './move/my-package',
generate: {
functions: { private: true },
},
}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.
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:
public struct Balance<phantom T> has store {
value: u64,
}Default Behavior
By default, codegen excludes phantom type parameters from the generated BCS type functions since they don't affect serialization. The generated type is a constant rather than a function:
export const Balance = new MoveStruct({
name: `${$moduleName}::Balance<phantom T>`,
fields: {
value: bcs.u64(),
},
});This works correctly for parsing on-chain data because phantom types don't change the binary layout.
With the default behavior, phantom parameters appear as literals in the type name (e.g.,
Balance<phantom T>). These names are useful for debugging but are not valid on-chain type tags. If
you need valid type names with resolved phantom parameters, enable includePhantomTypeParameters.
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:
const config: SuiCodegenConfig = {
output: './src/contracts',
includePhantomTypeParameters: true,
packages: [
// ...
],
};With this option enabled, phantom type parameters become function arguments:
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
Calling Move Functions
The generated code provides type-safe functions for calling Move functions:
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
Use generated BCS types to parse on-chain object data with the core API:
import { Counter as CounterStruct } from './contracts/counter/counter';
async function readCounter(id: string) {
const { response } = await client.ledgerService.getObject({
objectId: id,
readMask: {
paths: ['*'],
},
});
if (!response.object?.contents?.value) {
throw new Error('Expected a move object with contents');
}
// Parse with generated BCS type
const parsed = CounterStruct.parse(response.object.contents.value);
console.log('Counter value:', parsed.value);
console.log('Counter owner:', parsed.owner);
return parsed;
}Client Configuration
Using with MVR (Move Version Registry)
If your package is registered on MVR, the generated code works without additional configuration.
Local Packages
For local packages using @local-pkg/* identifiers, configure your client with package overrides:
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',
},
},
},
});With dApp Kit
Configure package overrides when creating your dApp Kit instance:
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',
},
};
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,
},
},
},
});
},
});Related Resources
- create-dapp - Bootstrap a working dApp with codegen already configured
- Sui Move documentation
- BCS documentation
- Transaction building
- dApp Kit