Custom Chains
Wrap any Cosmos SDK binary with cosmosBase / cosmosEvmBase
Any Cosmos SDK binary that follows the standard init → genesis → start CLI shape works with starskiff. Instance definitions are thin wrappers: set the binary name and chain defaults, delegate the rest.
Minimal instance
import {
Instance,
cosmosBase,
type CosmosChainParameters,
type OptionalInstanceSource,
} from 'starskiff';
export type MychaindParameters =
& Omit<CosmosChainParameters, 'image'>
& OptionalInstanceSource;
export const mychaind = Instance.define((parameters?: MychaindParameters) => {
const { binary = 'mychaind', image, ...rest } = parameters || {};
return cosmosBase({ binary, image, name: 'mychaind', ...rest });
});
const instance = mychaind({ chainId: 'my-1', denom: 'umych' });cosmosBase handles the full boot flow: init → genesis denom patching → key creation + account funding → gentx / collect-gentxs → config.toml/app.toml port patching → start → health-polling until the first block.
OptionalInstanceSource makes the wrapper's image and binary overrides
mutually exclusive while allowing neither, which uses its built-in mychaind
binary. Use InstanceSource instead when a custom wrapper has no default and
must require exactly one source. cosmosBase itself still receives both an
optional image and the executable name to invoke inside that image.
Patching genesis
Chain-specific genesis tweaks go through the patchGenesis hook, applied after starskiff's default denom patching:
return cosmosBase({
binary, name: 'mychaind', ...rest,
patchGenesis: (genesis) => {
genesis.app_state.gov.params.voting_period = '5s';
return genesis;
},
});Extra config patches and start flags are available for the odd chain that needs them:
| Hook | Applies to |
|---|---|
patchGenesis | genesis.json (after denom patching) |
extraAppToml | app.toml ('section.key': 'value') |
extraConfigToml | config.toml |
extraStartArgs | appended to the start command |
extraReadinessCheck | ANDed with the first-block health check |
Runtime inputs
Custom chain definitions can attach environment variables and read-only files to every lifecycle command with runtime. Environment settings apply to both local binaries and containers; mounts apply only when image is used.
return cosmosBase({
binary,
name: 'mychaind',
image,
runtime: {
environment: { MYCHAIN_CONFIG_DIR: image ? '/starskiff/config' : configDirectory },
mounts: image
? [{ source: configDirectory, target: '/starskiff/config', readOnly: true }]
: undefined,
},
});Keep chain-specific details on the wrapper interface. For example, accept a configDirectory option and translate it into runtime internally rather than asking callers for environment-variable names or container paths.
EVM chains
For cosmos-evm chains, cosmosEvmBase extends the flow with a JSON-RPC port (evmPort, patched into app.toml and health-checked on start), ethermint relayer hints for Hermes, an activeStaticPrecompiles genesis patch, and an optional validated evmChainId passed to the node at start:
import { Instance, cosmosEvmBase, type CosmosEvmChainParameters } from 'starskiff';
export const myevmd = Instance.define((parameters?: CosmosEvmChainParameters) => {
return cosmosEvmBase({
binary: 'myevmd',
name: 'myevmd',
evmChainId: 12345,
denom: 'atoken', // 18 decimals → bump validator amounts:
validatorBalance: '100000000000000000000000', // 1e23
validatorStake: '10000000000000000000000', // 1e22
...parameters,
});
});18-decimal denoms need large validator amounts because the SDK's power reduction is denom-scaled — see the shipped evmd definition for a reference implementation (it also shows denom-metadata and precompile patching for chains whose init writes incomplete genesis defaults).