mirror of
https://github.com/luxfi/l2-template.git
synced 2026-10-30 16:21:46 +00:00
347 lines
8.7 KiB
Bash
Executable File
347 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
CHAIN="${1:?Usage: $0 <chain-name> <symbol> <mainnet-id> <testnet-id> <devnet-id>}"
|
|
SYMBOL="${2:?symbol required}"
|
|
MAINNET_ID="${3:?mainnet chain ID required}"
|
|
TESTNET_ID="${4:?testnet chain ID required}"
|
|
DEVNET_ID="${5:?devnet chain ID required}"
|
|
|
|
ORG="${CHAIN}-labs"
|
|
UPPER=$(echo "$CHAIN" | tr '[:lower:]' '[:upper:]')
|
|
TITLE=$(echo "$CHAIN" | sed 's/./\U&/')
|
|
DIR="$HOME/work/$CHAIN"
|
|
|
|
echo "Creating $TITLE L2 stack at $DIR"
|
|
echo " Symbol: $SYMBOL"
|
|
echo " Chain IDs: $MAINNET_ID / $TESTNET_ID / $DEVNET_ID"
|
|
echo " GitHub org: $ORG"
|
|
echo ""
|
|
|
|
mkdir -p "$DIR"
|
|
|
|
# ─── 1. EVM ──────────────────────────────────────────────
|
|
mkdir -p "$DIR/evm"
|
|
cat > "$DIR/evm/main.go" << GOEOF
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/luxfi/node/version"
|
|
"github.com/luxfi/evm/plugin/evm"
|
|
|
|
// Precompiles — uncomment what you need
|
|
// _ "github.com/luxfi/precompile/mldsa"
|
|
// _ "github.com/luxfi/precompile/frost"
|
|
// _ "github.com/luxfi/precompile/cggmp21"
|
|
// _ "github.com/luxfi/precompile/dex"
|
|
// _ "github.com/luxfi/precompile/blake3"
|
|
)
|
|
|
|
const Version = "0.1.0"
|
|
|
|
func main() {
|
|
if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "--version") {
|
|
fmt.Printf("${CHAIN}-evm %s (luxd %s)\n", Version, version.Current)
|
|
os.Exit(0)
|
|
}
|
|
evm.Run()
|
|
}
|
|
GOEOF
|
|
|
|
cat > "$DIR/evm/go.mod" << GOEOF
|
|
module github.com/${ORG}/evm
|
|
|
|
go 1.26.1
|
|
|
|
require (
|
|
github.com/luxfi/node v1.0.9
|
|
github.com/luxfi/evm v1.0.7
|
|
)
|
|
GOEOF
|
|
|
|
cat > "$DIR/evm/Dockerfile" << 'DEOF'
|
|
FROM golang:1.26.4-alpine AS build
|
|
RUN apk add --no-cache gcc musl-dev git
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
RUN CGO_ENABLED=1 go build -trimpath -ldflags="-s -w" -o /node .
|
|
|
|
FROM alpine:3.21
|
|
RUN apk add --no-cache ca-certificates curl && adduser -D -u 10001 node
|
|
COPY --from=build /node /usr/local/bin/node
|
|
USER node
|
|
ENTRYPOINT ["node"]
|
|
DEOF
|
|
|
|
# ─── 2. CLI ──────────────────────────────────────────────
|
|
mkdir -p "$DIR/cli/cmd" "$DIR/cli/internal/deploy"
|
|
cat > "$DIR/cli/main.go" << GOEOF
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/${ORG}/cli/cmd"
|
|
)
|
|
|
|
func main() {
|
|
if err := cmd.Execute(); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
GOEOF
|
|
|
|
cat > "$DIR/cli/go.mod" << GOEOF
|
|
module github.com/${ORG}/cli
|
|
|
|
go 1.26.1
|
|
GOEOF
|
|
|
|
# ─── 3. SDK ──────────────────────────────────────────────
|
|
mkdir -p "$DIR/sdk/src"
|
|
cat > "$DIR/sdk/package.json" << JSONEOF
|
|
{
|
|
"name": "@${ORG}/sdk",
|
|
"version": "0.1.0",
|
|
"description": "${TITLE} Network SDK",
|
|
"main": "dist/index.js",
|
|
"types": "dist/index.d.ts",
|
|
"type": "module",
|
|
"scripts": {
|
|
"build": "tsc",
|
|
"test": "vitest run"
|
|
},
|
|
"dependencies": {
|
|
"ethers": "^6.13.0",
|
|
"viem": "^2.21.0"
|
|
},
|
|
"devDependencies": {
|
|
"typescript": "^5.7.0",
|
|
"vitest": "^3.0.0"
|
|
}
|
|
}
|
|
JSONEOF
|
|
|
|
cat > "$DIR/sdk/src/index.ts" << TSEOF
|
|
export const CHAINS = {
|
|
devnet: { id: ${DEVNET_ID}, rpc: 'https://rpc.dev.${CHAIN}.network' },
|
|
testnet: { id: ${TESTNET_ID}, rpc: 'https://rpc.test.${CHAIN}.network' },
|
|
mainnet: { id: ${MAINNET_ID}, rpc: 'https://rpc.${CHAIN}.network' },
|
|
} as const;
|
|
|
|
export const NATIVE = { name: '${TITLE}', symbol: '${SYMBOL}', decimals: 18 } as const;
|
|
|
|
export type ChainName = keyof typeof CHAINS;
|
|
TSEOF
|
|
|
|
cat > "$DIR/sdk/tsconfig.json" << 'TSEOF'
|
|
{
|
|
"compilerOptions": {
|
|
"target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext",
|
|
"declaration": true, "outDir": "dist", "rootDir": "src", "strict": true
|
|
},
|
|
"include": ["src"]
|
|
}
|
|
TSEOF
|
|
|
|
# ─── 4. Contracts ────────────────────────────────────────
|
|
mkdir -p "$DIR/contracts/src" "$DIR/contracts/script" "$DIR/contracts/test"
|
|
cat > "$DIR/contracts/foundry.toml" << 'FEOF'
|
|
[profile.default]
|
|
src = "src"
|
|
out = "out"
|
|
libs = ["lib"]
|
|
solc = "0.8.31"
|
|
optimizer = true
|
|
optimizer_runs = 200
|
|
evm_version = "shanghai"
|
|
FEOF
|
|
|
|
cat > "$DIR/contracts/src/Token.sol" << 'SEOF'
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
|
|
contract Token is ERC20, AccessControl {
|
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
|
|
|
constructor(string memory name_, string memory symbol_, address admin)
|
|
ERC20(name_, symbol_)
|
|
{
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
_grantRole(MINTER_ROLE, admin);
|
|
}
|
|
|
|
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
|
|
_mint(to, amount);
|
|
}
|
|
}
|
|
SEOF
|
|
|
|
# ─── 5. Genesis ──────────────────────────────────────────
|
|
mkdir -p "$DIR/genesis"
|
|
for env_id in "mainnet:${MAINNET_ID}" "testnet:${TESTNET_ID}" "devnet:${DEVNET_ID}"; do
|
|
IFS=: read env cid <<< "$env_id"
|
|
cat > "$DIR/genesis/${env}.json" << GEOF
|
|
{
|
|
"config": {
|
|
"chainId": ${cid},
|
|
"homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0,
|
|
"byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0,
|
|
"istanbulBlock": 0, "muirGlacierBlock": 0, "subnetEVMTimestamp": 0
|
|
},
|
|
"nonce": "0x0",
|
|
"timestamp": "0x0",
|
|
"gasLimit": "0x1312D00",
|
|
"difficulty": "0x0",
|
|
"alloc": {
|
|
"0x0000000000000000000000000000000000000000": {
|
|
"balance": "0x204FCE5E3E25026110000000"
|
|
}
|
|
}
|
|
}
|
|
GEOF
|
|
done
|
|
|
|
# ─── 6. Exchange (stub — clone from lux/exchange apps/) ──
|
|
mkdir -p "$DIR/exchange"
|
|
cat > "$DIR/exchange/README.md" << EEOF
|
|
# ${TITLE} Exchange
|
|
|
|
Clone apps from ~/work/lux/exchange:
|
|
|
|
\`\`\`bash
|
|
cp -r ~/work/lux/exchange/apps .
|
|
pnpm install
|
|
\`\`\`
|
|
|
|
Apps consume @l.x/* packages from npm. No local pkgs/ needed.
|
|
EEOF
|
|
|
|
# ─── 7. Universe (K8s) ──────────────────────────────────
|
|
mkdir -p "$DIR/universe/k8s/base" "$DIR/universe/k8s/devnet" "$DIR/universe/k8s/testnet" "$DIR/universe/k8s/mainnet"
|
|
|
|
cat > "$DIR/universe/k8s/base/node.yaml" << 'KEOF'
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: node
|
|
spec:
|
|
serviceName: node-headless
|
|
replicas: 5
|
|
selector:
|
|
matchLabels:
|
|
app: node
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: node
|
|
spec:
|
|
containers:
|
|
- name: node
|
|
image: IMAGE_PLACEHOLDER
|
|
ports:
|
|
- {containerPort: 9630, name: staking}
|
|
- {containerPort: 9631, name: rpc}
|
|
volumeMounts:
|
|
- {name: data, mountPath: /data}
|
|
volumeClaimTemplates:
|
|
- metadata: {name: data}
|
|
spec:
|
|
accessModes: [ReadWriteOnce]
|
|
resources: {requests: {storage: 50Gi}}
|
|
KEOF
|
|
|
|
cat > "$DIR/universe/k8s/base/services.yaml" << 'KEOF'
|
|
# Explorer — use lux/explorer image with env branding
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: explorer
|
|
spec:
|
|
replicas: 1
|
|
selector: {matchLabels: {app: explorer}}
|
|
template:
|
|
metadata: {labels: {app: explorer}}
|
|
spec:
|
|
containers:
|
|
- name: explorer
|
|
image: us-docker.pkg.dev/liquidity-devnet/backend/zoo-explorer:latest
|
|
env:
|
|
- {name: COIN, value: SYMBOL_PLACEHOLDER}
|
|
- {name: NETWORK, value: NAME_PLACEHOLDER}
|
|
- {name: DISABLE_INDEXER, value: "false"}
|
|
ports: [{containerPort: 4000}]
|
|
resources:
|
|
requests: {memory: 1Gi}
|
|
limits: {memory: 4Gi}
|
|
---
|
|
# MPC — upstream luxfi/mpc image
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: mpc-node
|
|
spec:
|
|
serviceName: mpc
|
|
replicas: 1
|
|
selector: {matchLabels: {app: mpc-node}}
|
|
template:
|
|
metadata: {labels: {app: mpc-node}}
|
|
spec:
|
|
containers:
|
|
- name: mpc
|
|
image: ghcr.io/luxfi/mpc:latest
|
|
env:
|
|
- {name: MPC_ORG, value: NAME_PLACEHOLDER}
|
|
- {name: MPC_BRAND_NAME, value: NAME_PLACEHOLDER}
|
|
ports: [{containerPort: 8081}]
|
|
---
|
|
# KMS — upstream luxfi/kms image
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: kms
|
|
spec:
|
|
replicas: 1
|
|
selector: {matchLabels: {app: kms}}
|
|
template:
|
|
metadata: {labels: {app: kms}}
|
|
spec:
|
|
containers:
|
|
- name: kms
|
|
image: ghcr.io/luxfi/kms:latest
|
|
env:
|
|
- {name: KMS_ORG, value: NAME_PLACEHOLDER}
|
|
ports: [{containerPort: 8443}]
|
|
---
|
|
# Bridge — upstream luxfi/bridge image
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bridge
|
|
spec:
|
|
replicas: 1
|
|
selector: {matchLabels: {app: bridge}}
|
|
template:
|
|
metadata: {labels: {app: bridge}}
|
|
spec:
|
|
containers:
|
|
- name: bridge
|
|
image: ghcr.io/luxfi/bridge:latest
|
|
ports: [{containerPort: 8080}]
|
|
KEOF
|
|
|
|
echo "Template created at $DIR"
|
|
echo ""
|
|
echo "Repos:"
|
|
ls -d "$DIR"/*/
|