Configure a new chain deployment for Stargate V2. Use this skill whenever someone mentions deploying Stargate to a new chain, adding a new chain to the mesh, setting up chain configuration, or running /new-chain. It auto-fetches EndpointId, chain ID, DVN addresses, and executor from LayerZero APIs, calculates nativeDropAmount via cast gas-price, and generates all required config files (constant.ts, hardhat.config.ts, chain YAML). Trigger even if the user just says "add <chain>" or "deploy to <chain>". IMPORTANT: Always ask the user for confirmation before proceeding with the skill.
You are helping configure and deploy a new chain for Stargate V2. The flow is:
0. Create a branch deployments/<chain-name>
The chain name is passed as an argument: /new-chain <chain-name> (e.g. /new-chain sonic).
If no argument was given, ask for the chain name before doing anything else.
Before asking for any info, create the branch:
git checkout -b deployments/<chain-name>
If the branch already exists, check it out instead. Confirm to the user that the branch is ready before continuing.
In a single message, tell the user you're about to configure <chain-name> and ask for everything you need before proceeding. This avoids multiple back-and-forth exchanges. The OneSig URL slug defaults to the chain name — don't ask for it.
Example message:
I'll configure
<chain-name>for Stargate V2. Before I fetch data and generate the config files, I need:
- OneSig address — multisig address for ownership transfer
- Assets — which assets to deploy and their type (
<asset>: native|pool <address>|oft)- Custom config — any extra hardhat flags, additional DVNs, rewarder/staking, or per-path DVN overrides? ("none" if nothing)
Once you provide these, I'll fetch the chain data from LayerZero APIs, calculate the nativeDropAmount, and generate all config files.
Asset types:
native — for ETH on native L2spool <address> — token already exists on chainoft — Stargate deploys its bridged versioneth, usdc, usdt (rare — USDT0 is canonical), eurcCustom config options (only if user asks):
zksync: true, useFeeData: true, isTIP20: true, alt: true, ethNetwork: '<network>'Wait for the user's answers before proceeding.
This is used when specific paths (e.g. to/from Ethereum) require different DVN setups than the default. For each path, the user provides:
ethereum)perPathRequiredDVNs: which DVNs are required for that pathperPathOptionalDVNs: which DVNs are optional for that pathperPathOptionalDVNsThreshold: how many optional DVNs must verify (e.g. 2)Example (bera ↔ ethereum):
Path: ethereum
perPathRequiredDVNs: [LZ_LABS]
perPathOptionalDVNs: [NETHERMIND, BERA, USDT0, CANARY]
perPathOptionalDVNsThreshold: 2
Bidirectional: yes
Once the user provides their answers, fetch all data in parallel and then immediately generate the config files — no need to stop and show the fetched data separately.
GET https://metadata.layerzero-api.com/v1/metadata/deployments
Find the entry whose key matches <chain-name> (case-insensitive). From the v2 deployment (version: 2), extract:
| Field | Path in JSON |
|---|---|
| eid (EndpointId number) | deployments[version=2].eid |
| Executor address | deployments[version=2].executor.address |
| Chain ID | chainDetails.nativeChainId |
| Native currency symbol | chainDetails.nativeCurrency.symbol |
| Native currency decimals | chainDetails.nativeCurrency.decimals |
⚠️ API truncation: The deployments API response is very large and WebFetch may return a truncated version that cuts off newer chains. If the chain is not found in the response, the executor address is still reliable — look it up by using
chainKey: "<chain-name>", stage: "mainnet"in the JSON. If you cannot retrieve it, note the address as a TODO and let the user know they can find it at:https://metadata.layerzero-api.com/v1/metadata/deployments→ search for"chainKey": "<chain-name>"
The EndpointId constant follows the pattern <CHAIN_UPPER>_V2_MAINNET (e.g. SONIC_V2_MAINNET for eid 30332). Verify it exists by running:
grep '<CHAIN_UPPER>_V2_MAINNET' node_modules/@layerzerolabs/lz-definitions/dist/index.d.ts 2>/dev/null || echo "NOT_FOUND"
If NOT_FOUND, the constant is not yet in the installed package version. Proceed anyway using the constant name (e.g. EndpointId.GENSYN_V2_MAINNET) and add a note to the follow-up checklist that a package version bump is required before building.
GET https://metadata.layerzero-api.com/v1/metadata/dvns?chainNames=<chain-name>
From the DVN map for the chain, extract addresses (the map keys) for:
id containing "nethermind" or canonicalName containing "Nethermind"id containing "layerzero-labs" or canonicalName containing "LayerZero Labs"If a DVN is missing, mark it as ⚠ NOT FOUND — needs manual resolution and leave a placeholder.
Always calculate automatically using the formula: gas_price * 500_000 * 3.
Resolve the RPC URL using the same logic as getRpcUrl in packages/stg-evm-v2/hardhat.config.ts:
CHAIN_NAME="<chain-name>"
CHAIN_UPPER=$(echo "$CHAIN_NAME" | tr '[:lower:]' '[:upper:]')
# Try specific env var first
RPC_VAR="RPC_URL_${CHAIN_UPPER}_MAINNET"
RPC="${!RPC_VAR:-}"
# Fall back to template
if [ -z "$RPC" ]; then
TEMPLATE="${RPC_URL_MAINNET:-}"
if [ -n "$TEMPLATE" ]; then
RPC="${TEMPLATE//CHAIN/$CHAIN_NAME}"
fi
fi
# Get gas price
if [ -n "$RPC" ]; then
GAS_PRICE=$(cast gas-price --rpc-url "$RPC" 2>/dev/null)
if [ -n "$GAS_PRICE" ]; then
# Calculate: gas_price * 500000 * 3
NATIVE_DROP=$(echo "$GAS_PRICE * 500000 * 3" | bc)
echo "GAS_PRICE=$GAS_PRICE"
echo "NATIVE_DROP_WEI=$NATIVE_DROP"
# Convert to ether (18 decimals)
echo "NATIVE_DROP_ETHER=$(echo "scale=18; $NATIVE_DROP / 1000000000000000000" | bc)"
else
echo "CAST_FAILED"
fi
else
echo "NO_RPC — set RPC_URL_${CHAIN_UPPER}_MAINNET in .env.local"
fi
If RPC is not configured, find a public RPC on https://chainlist.org/ (search by chain name or chain ID). Express the result as parseEther('<value>').toBigInt(), rounded to 1-4 significant figures (e.g. parseEther('0.001'), parseEther('0.015')).
Once all data is fetched, immediately generate the 3 config files. Add // TODO: Confirm comments on values that need human verification (addresses, nativeDropAmount). Present each change clearly.
packages/stg-definitions-v2/src/constant.tsMake these additions in alphabetical order within each block:
a) DVNS.NETHERMIND
[EndpointId.<CHAIN>_V2_MAINNET]: '<nethermind-dvn-address>',
b) DVNS.LZ_LABS
[EndpointId.<CHAIN>_V2_MAINNET]: '<lzlabs-dvn-address>',
c) Additional DVN objects — if the user specified extra DVNs, either add entries to existing objects or create new ones:
DVNNAME: {
[EndpointId.<CHAIN>_V2_MAINNET]: '<address>',
} satisfies Partial<Record<EndpointId, string>>,
d) EXECUTORS.LZ_LABS
[EndpointId.<CHAIN>_V2_MAINNET]: '<executor-address>',
e) ASSETS — per deployed asset:
For ETH (inside ASSETS[TokenName.ETH].networks):
[EndpointId.<CHAIN>_V2_MAINNET]: { type: StargateType.Native },[EndpointId.<CHAIN>_V2_MAINNET]: { symbol: 'WETH', name: 'WETH', type: StargateType.Pool, address: '<addr>' },[EndpointId.<CHAIN>_V2_MAINNET]: { symbol: 'WETH', name: 'WETH', type: StargateType.Oft },For USDC (inside ASSETS[TokenName.USDC].networks):
[EndpointId.<CHAIN>_V2_MAINNET]: { address: '<addr>', type: StargateType.Pool },[EndpointId.<CHAIN>_V2_MAINNET]: { type: StargateType.Oft, address: '<addr>' },address field:
[EndpointId.<CHAIN>_V2_MAINNET]: {
type: StargateType.Oft,
address: '0x0000000000000000000000000000000000000000', // TODO: Update with deployed USDC address on <Chain>
},
Same pattern for USDT and EURC OFTs that haven't been deployed yet.
f) NETWORKS_CONFIG — the main config block:
// TODO: Confirm OneSig address
[EndpointId.<CHAIN>_V2_MAINNET]: {
creditMessaging: {
...DEFAULT_CREDIT_MESSAGING_NETWORK_CONFIG,
requiredDVNs: [DVNS.NETHERMIND[EndpointId.<CHAIN>_V2_MAINNET], DVNS.LZ_LABS[EndpointId.<CHAIN>_V2_MAINNET]],
executor: EXECUTORS.LZ_LABS[EndpointId.<CHAIN>_V2_MAINNET],
},
tokenMessaging: {
...DEFAULT_TOKEN_MESSAGING_NETWORK_CONFIG,
requiredDVNs: [DVNS.NETHERMIND[EndpointId.<CHAIN>_V2_MAINNET], DVNS.LZ_LABS[EndpointId.<CHAIN>_V2_MAINNET]],
executor: EXECUTORS.LZ_LABS[EndpointId.<CHAIN>_V2_MAINNET],
nativeDropAmount: parseEther('<X>').toBigInt(), // TODO: Double check this value
},
oneSigConfig: {
oneSigAddress: '<onesig-address>', // TODO: Confirm
oneSigUrl: `${process.env.BASE_ONE_SIG_URL_MAINNET}/<chain-name>`,
},
},
Variations:
creditMessaging and/or tokenMessaging (like blast-mainnet which has only oneSig)busGasLimit and/or nativeDropGasLimit to tokenMessaging (only when explicitly requested)g) Per-path DVN configuration (if requested) — add to both creditMessaging and tokenMessaging:
perPathRequiredDVNs: {
[EndpointId.<TARGET>_V2_MAINNET]: [DVNS.LZ_LABS[EndpointId.<CHAIN>_V2_MAINNET]],
},
perPathOptionalDVNs: {
[EndpointId.<TARGET>_V2_MAINNET]: [
DVNS.NETHERMIND[EndpointId.<CHAIN>_V2_MAINNET],
DVNS.BERA[EndpointId.<CHAIN>_V2_MAINNET],
],
},
perPathOptionalDVNsThreshold: {
[EndpointId.<TARGET>_V2_MAINNET]: 2,
},
If bidirectional (usually yes), also update the target chain's existing NETWORKS_CONFIG. Each chain references its own local DVN deployment addresses.
packages/stg-evm-v2/hardhat.config.tsAdd in the // Mainnet section, alphabetical order:
'<chain>-mainnet': {
eid: EndpointId.<CHAIN>_V2_MAINNET,
url: process.env.RPC_URL_<CHAIN_UPPER>_MAINNET || '<public-rpc-url>',
accounts: mainnetAccounts,
oneSigConfig: getOneSigConfig(EndpointId.<CHAIN>_V2_MAINNET),
timeout: DEFAULT_NETWORK_TIMEOUT,
},
For the public RPC fallback: fetch from https://chainid.network/chains.json or use a well-known endpoint. Add extra flags if specified by user.
packages/stg-evm-v2/devtools/config/mainnet/01/chainsConfig/<chain>-mainnet.ymlCreate a new file. Only include sections for deployed assets: