Mysten Incubation

Get started

Boot a local Sui stack, fund accounts, publish packages, wire your app, and generate bindings — all from one TypeScript config.

Spin up everything your Sui app needs to run locally from one TypeScript file. A single devstack.config.ts boots a local network, funds named accounts, publishes your Move packages, wires your dev server and a browser dev wallet, and generates bindings plus a runtime config your app imports — so pnpm dev gives you a working chain, funded wallets, and your published contracts.

You describe the stack as a list of members — sui(), account(), localPackage(), wallet(), walrus(), seal(), deepbook(), action(), and dashboard() — and devstack stands them up.

Install

Start a new app with the create tool:

pnpm create @mysten-incubation/devstack-app@latest my-app
cd my-app
pnpm dev

Add devstack to an existing app — install the devstack package, the dev wallet used by the generated browser bindings, and the shared tsconfig package:

pnpm add @mysten-incubation/devstack @mysten-incubation/dev-wallet @mysten/signers
pnpm add -D @mysten-incubation/tsconfig

Configure the stack

Add a devstack.config.ts at the app root:

devstack.config.ts
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

import {
	defineDevstack,
	account,
	HOST_SERVICE_PORT_TOKEN,
	hostService,
	localPackage,
	sui,
	wallet,
} from '@mysten-incubation/devstack';

const HERE = dirname(fileURLToPath(import.meta.url));
const DEV_PORT = 5173;

export const localnet = sui();
export const publisher = account('publisher');
export const alice = account('alice');
export const bob = account('bob');

export const hello = localPackage('hello', {
	sourcePath: resolve(HERE, 'move/hello'),
	publisher,
});
export const devWallet = wallet({
	accounts: [publisher, alice, bob],
});
export const app = hostService({
	name: 'app',
	script: `pnpm exec vite --host 127.0.0.1 --strictPort --port ${HOST_SERVICE_PORT_TOKEN}`,
	cwd: HERE,
	port: DEV_PORT,
	ready: { kind: 'http' },
	after: [hello, devWallet] as const,
});

export default defineDevstack({
	members: [localnet, app],
	stackName: 'main',
});

Wire the app

Add a Vite config. devstackVitePlugin() wires the @generated alias so your app can import the generated tree, and passes the allocated port through to the hostService command.

vite.config.ts
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { devstackVitePlugin } from '@mysten-incubation/devstack/vite';

export default defineConfig({ plugins: [react(), devstackVitePlugin()] });

Import the generated files through the @generated alias and wire dapp-kit from @generated/config.js. The typed config object gives you config.networkNames, config.defaultNetwork, and config.forNetwork(net). In dev, the dev wallet is added to the page automatically and dApp Kit discovers it via wallet-standard; a production build carries no dev wallet.

src/dapp-kit.ts
import { createDAppKit } from '@mysten/dapp-kit-react';
import { SuiGrpcClient } from '@mysten/sui/grpc';

import { config } from '@generated/config.js';

export const dAppKit = createDAppKit({
	networks: [...config.networkNames],
	defaultNetwork: config.defaultNetwork,
	autoConnect: import.meta.env.DEV,
	createClient(network) {
		// `config.forNetwork(net)` returns the resolved deployment for the
		// selected network, so a runtime `switchNetwork` flips ids in lockstep.
		const net = config.forNetwork(network);
		return new SuiGrpcClient({
			network,
			baseUrl: net.rpc,
			// Generated bindings default `package` to `@local/<name>`;
			// `mvrOverrides` points each name at its deployed id.
			mvr: { overrides: net.mvrOverrides },
		});
	},
});

See Config & deployments for the full config surface and how the deployment loader resolves ids.

First boot

Generate the bindings your app builds against:

pnpm devstack codegen

This writes the src/generated tree — typed Move bindings and the runtime config your app imports — from your Move source. It needs no running stack; run it whenever the Move source changes.

Then run the stack:

pnpm devstack up

Reconcile from another shell or CI:

pnpm devstack apply

If devstack up is already running for this stack, apply waits on it; otherwise it runs the same setup once and exits. Use the generated files from src/generated, and treat .devstack/ as runtime state only.

Core flow

  1. Put stack members in devstack.config.ts. Include your dev server as a hostService(...) member when the stack backs a browser app.
  2. Run devstack codegen to write the bindings and runtime config under src/generated — the tree your app builds against. Rerun it when the Move source changes.
  3. Run devstack up to start the stack and keep it running, or devstack apply to reconcile from another shell or CI.
  4. Import the generated files from src/generated; runtime state, snapshots, and logs stay under .devstack/stacks/<stack>.
  5. Pull in the Vitest, Playwright, Vite, and runtime helpers from their subpaths when you need them — @mysten-incubation/devstack/vitest, /playwright, /vite, and /runtime.

Where to go next

On this page