feat(non-evm): operator binding config, planner hints, and validation
- Add config/non-evm-operator-binding.json (public hints only; no secrets). - Extend .env.master.example with XRPL/Tron/Solana/TRONGRID overrides. - Wire solana-gru-bridge-lineup refs; refresh non-evm lane stubs from binding. - Teach liquidity-gap planner to read binding; validate JSON in validate-config-files.sh. - Document handoff in CWUSDC_NON_MANUAL_PROVIDER_TASKS; cross-link GRU spec. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -279,6 +279,19 @@ RPC_URL_138_FIREBLOCKS=
|
||||
WS_URL_138_FIREBLOCKS=
|
||||
CHAIN_ID_138=
|
||||
|
||||
# --- Non-EVM operator binding (liquidity planner / handoff advisory) ---
|
||||
# Public XRPL r-address (optional). Same as XRPL_ACCOUNT / XRPL_WALLET_ADDRESS / XRP_WALLET_ADDRESS.
|
||||
XRPL_ACCOUNT=
|
||||
XRPL_DEPLOYER_ADDRESS=
|
||||
# Native Tron base58 address override if not using derived EVM→Tron mapping.
|
||||
TRON_DEPLOYER_ADDRESS=
|
||||
# Set to 1 after ops confirms derived Tron deployer is canonical (see config/non-evm-operator-binding.json).
|
||||
TRON_CANONICAL_CONFIRMED=
|
||||
# Solana public key override if not using SOLANA_KEYPAIR_PATH.
|
||||
SOLANA_DEPLOYER_ADDRESS=
|
||||
SOLANA_RPC_URL=
|
||||
TRONGRID_API_KEY=
|
||||
|
||||
# --- Phoenix deploy API ---
|
||||
PORT=
|
||||
GITEA_TOKEN=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": "non-evm-lane-requirements/v1",
|
||||
"generatedAt": "2026-05-11T22:34:46.673427+00:00",
|
||||
"generatedAt": "2026-05-12T07:06:40.872440+00:00",
|
||||
"status": "stubs_bound_repo_side",
|
||||
"lanes": [
|
||||
{
|
||||
@@ -13,7 +13,7 @@
|
||||
"rentReserveTarget",
|
||||
"venueMinimumLiquidity"
|
||||
],
|
||||
"minimumFundingTarget": "TBD",
|
||||
"minimumFundingTarget": "minSolOperationalHint=0.05 (Operational gas+rent buffer hint only; operator confirms live venue targets.)",
|
||||
"claimBoundary": "Do not claim native Solana liquidity until SPL mints, rent/gas, and venue inventory are bound."
|
||||
},
|
||||
{
|
||||
@@ -26,7 +26,7 @@
|
||||
"energyBandwidthTarget",
|
||||
"trc20Inventory"
|
||||
],
|
||||
"minimumFundingTarget": "TBD",
|
||||
"minimumFundingTarget": "minTrxOperationalHint=200 (TRX for bandwidth/energy visibility; operator confirms relay inventory.)",
|
||||
"claimBoundary": "Do not claim native Tron liquidity until the canonical wallet and TRC-20 inventory are confirmed."
|
||||
},
|
||||
{
|
||||
@@ -40,7 +40,7 @@
|
||||
"trustlineIssuerPolicy",
|
||||
"xrpReserveTarget"
|
||||
],
|
||||
"minimumFundingTarget": "TBD",
|
||||
"minimumFundingTarget": "minXrpReserveHint=2 (Above typical base reserve; operator confirms trustline/issuer policy.)",
|
||||
"claimBoundary": "Do not claim XRPL corridor readiness until account reserve, tags, trustlines, and wXRP controller evidence are closed."
|
||||
},
|
||||
{
|
||||
@@ -85,5 +85,6 @@
|
||||
"claimBoundary": "Use HYPE only as a market-cap watch item until identifiers and custody path are verified."
|
||||
}
|
||||
],
|
||||
"operatorBindingConfig": "config/non-evm-operator-binding.json",
|
||||
"validationRule": "A lane is claimable only after canonicalWallet, asset IDs, native gas/reserve target, venue target, and evidence source are non-TBD."
|
||||
}
|
||||
|
||||
32
config/non-evm-operator-binding.json
Normal file
32
config/non-evm-operator-binding.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"version": "1.0.0",
|
||||
"description": "Operator-declared non-EVM binding for liquidity planner and advisory closure. Public XRPL r-address may be committed; do not put private seeds here. Flip tron.canonicalDeployerConfirmed only after ops confirms the EVM-derived Tron deployer is canonical.",
|
||||
"documentation": "Env overrides still win (XRPL_ACCOUNT, TRON_DEPLOYER_ADDRESS, SOLANA_KEYPAIR_PATH). This file supplies defaults and minimum native hints for reports. See docs/03-deployment/CHAIN138_TO_SOLANA_GRU_TOKEN_DEPLOYMENT_LINEUP.md and config/solana-gru-bridge-lineup.json for SPL mint work.",
|
||||
"xrpl": {
|
||||
"canonicalAccount": "",
|
||||
"destinationTagPolicy": "none"
|
||||
},
|
||||
"tron": {
|
||||
"addressOverride": "",
|
||||
"canonicalDeployerConfirmed": false
|
||||
},
|
||||
"minimumFundingTargets": {
|
||||
"solana": {
|
||||
"minSolOperationalHint": "0.05",
|
||||
"note": "Operational gas+rent buffer hint only; operator confirms live venue targets."
|
||||
},
|
||||
"tron": {
|
||||
"minTrxOperationalHint": "200",
|
||||
"note": "TRX for bandwidth/energy visibility; operator confirms relay inventory."
|
||||
},
|
||||
"xrpl": {
|
||||
"minXrpReserveHint": "2",
|
||||
"note": "Above typical base reserve; operator confirms trustline/issuer policy."
|
||||
}
|
||||
},
|
||||
"otherNonEvmMajors": {
|
||||
"status": "planning_bucket_only",
|
||||
"note": "Per-network binding still requires custody + asset IDs per lane; not auto-closed by this file."
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"canonicalCrosscheck": "docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md",
|
||||
"tokenMapping": "config/token-mapping-multichain.json",
|
||||
"nonEvmFramework": "config/non-evm-bridge-framework.json",
|
||||
"nonEvmOperatorBinding": "config/non-evm-operator-binding.json",
|
||||
"runbook": "docs/03-deployment/CHAIN138_TO_SOLANA_GRU_TOKEN_DEPLOYMENT_LINEUP.md"
|
||||
},
|
||||
"chain138": {
|
||||
|
||||
@@ -17,7 +17,7 @@ Latest evidence source: [../../reports/status/cwusdc-provider-handoff-latest.md]
|
||||
| CoinGecko price API | External blocker | API responds but does not include the cWUSDC contract key (primary listing gate). |
|
||||
| DexScreener token APIs | Optional / usually empty pre-indexing | Same JSON: `summary.dexScreenerTokenApisIndexed`; not counted in `failedRequiredIds`. |
|
||||
| EVM liquidity-gap planner rows | No current rows | Latest planner summary shows `rows = 0`. |
|
||||
| Non-EVM funding requirements | Open, operator-bound | Solana, Tron, XRPL, and other non-EVM requirements need wallet/asset/target binding before claims. |
|
||||
| Non-EVM funding requirements | Open, operator-bound | Solana, Tron, XRPL, and other non-EVM requirements need wallet/asset/target binding before claims. Hints and env keys: `config/non-evm-operator-binding.json`, `.env.master.example` (XRPL / Tron / Solana block); planner reads binding via `plan-token-aggregation-liquidity-gap-funding.mjs`. |
|
||||
| Mainnet cWUSDC supply attestation | Refreshed | `reports/status/cwusdc-supply-circulating-attestation-latest.md`; circulating supply `10451316981.309788`. |
|
||||
| Global cUSDC/cWUSDC family proof | Refreshed | `reports/status/global-cusdc-cwusdc-family-supply-proof-latest.md`; `22 / 23` entries proved for aggregate. |
|
||||
| Etherscan Value dossier | Refreshed | `reports/status/cwusdc-etherscan-value-dossier-latest.md`; ready for external submission, but Etherscan USD Value not ready. |
|
||||
|
||||
@@ -159,6 +159,7 @@ Implementation is complete when, with pinned JSON and extended loader, **all** o
|
||||
|
||||
## 9. Related documents
|
||||
|
||||
- Non-EVM funding hints (planner / lane stubs, orthogonal to GRU loader): [config/non-evm-operator-binding.json](../../config/non-evm-operator-binding.json), `scripts/verify/build-non-evm-requirement-stubs.py`, `scripts/verify/plan-token-aggregation-liquidity-gap-funding.mjs`.
|
||||
- Provider / wallet matrix (narrative, not loader): [metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md](metamask/METAMASK_ASSET_PRICE_PROVIDER_SUBMISSION_MATRIX.md)
|
||||
- MetaMask / GRU / DefiLlama master replay: [00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md](00-meta/METAMASK_GRU_DEFILLAMA_CHAIN138_MASTER_REFERENCE.md)
|
||||
- Token mapping loader source: [`config/token-mapping-loader.cjs`](../../config/token-mapping-loader.cjs)
|
||||
|
||||
@@ -93,6 +93,24 @@ else
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ -f "$PROJECT_ROOT/config/non-evm-operator-binding.json" ]]; then
|
||||
log_ok "Found: config/non-evm-operator-binding.json"
|
||||
if command -v jq &>/dev/null; then
|
||||
if jq -e '
|
||||
(.version | type == "string")
|
||||
and (.xrpl | type == "object")
|
||||
and (.tron | type == "object")
|
||||
and (.minimumFundingTargets | type == "object")
|
||||
' "$PROJECT_ROOT/config/non-evm-operator-binding.json" &>/dev/null; then
|
||||
log_ok "non-evm-operator-binding.json: structure valid"
|
||||
else
|
||||
log_err "non-evm-operator-binding.json: invalid structure"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_warn "Optional config/non-evm-operator-binding.json missing; skipping"
|
||||
fi
|
||||
if [[ -f "$PROJECT_ROOT/config/public-routing-coverage-matrix.json" ]]; then
|
||||
log_ok "Found: config/public-routing-coverage-matrix.json"
|
||||
if command -v jq &>/dev/null; then
|
||||
|
||||
@@ -13,6 +13,16 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
CONFIG_OUT = ROOT / "config/non-evm-lane-requirements.json"
|
||||
REPORT_JSON = ROOT / "reports/status/non-evm-lane-requirements-latest.json"
|
||||
REPORT_MD = ROOT / "reports/status/non-evm-lane-requirements-latest.md"
|
||||
OPERATOR_BINDING = ROOT / "config/non-evm-operator-binding.json"
|
||||
|
||||
|
||||
def load_operator_binding_hints() -> dict[str, Any]:
|
||||
if not OPERATOR_BINDING.exists():
|
||||
return {}
|
||||
try:
|
||||
return json.loads(OPERATOR_BINDING.read_text())
|
||||
except (json.JSONDecodeError, OSError):
|
||||
return {}
|
||||
|
||||
LANES: list[dict[str, Any]] = [
|
||||
{
|
||||
@@ -91,11 +101,36 @@ def table(headers: list[str], rows: list[list[Any]]) -> str:
|
||||
|
||||
def main() -> int:
|
||||
generated_at = datetime.now(timezone.utc).isoformat()
|
||||
bind = load_operator_binding_hints()
|
||||
hints = bind.get("minimumFundingTargets") or {}
|
||||
|
||||
def min_tgt(network: str) -> str:
|
||||
m = hints.get(network) if hints else None
|
||||
if isinstance(m, dict) and m:
|
||||
parts = [f"{k}={v}" for k, v in m.items() if k != "note" and v]
|
||||
note = m.get("note")
|
||||
base = "; ".join(parts) if parts else "TBD"
|
||||
return f"{base} ({note})" if note else base
|
||||
return "TBD"
|
||||
|
||||
lanes_out = []
|
||||
for lane in LANES:
|
||||
row = dict(lane)
|
||||
net = row["network"]
|
||||
if net == "solana":
|
||||
row["minimumFundingTarget"] = min_tgt("solana")
|
||||
elif net == "tron":
|
||||
row["minimumFundingTarget"] = min_tgt("tron")
|
||||
elif net == "xrpl":
|
||||
row["minimumFundingTarget"] = min_tgt("xrpl")
|
||||
lanes_out.append(row)
|
||||
|
||||
payload = {
|
||||
"schema": "non-evm-lane-requirements/v1",
|
||||
"generatedAt": generated_at,
|
||||
"status": "stubs_bound_repo_side",
|
||||
"lanes": LANES,
|
||||
"lanes": lanes_out,
|
||||
"operatorBindingConfig": str(OPERATOR_BINDING.relative_to(ROOT)),
|
||||
"validationRule": "A lane is claimable only after canonicalWallet, asset IDs, native gas/reserve target, venue target, and evidence source are non-TBD.",
|
||||
}
|
||||
for path in (CONFIG_OUT, REPORT_JSON):
|
||||
@@ -120,7 +155,7 @@ def main() -> int:
|
||||
lane["minimumFundingTarget"],
|
||||
lane["claimBoundary"],
|
||||
]
|
||||
for lane in LANES
|
||||
for lane in lanes_out
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -18,6 +18,15 @@ const repoRoot = resolve(new URL("../..", import.meta.url).pathname);
|
||||
const readinessPath = resolve(repoRoot, "reports/status/token-aggregation-adoption-readiness-live-20260509.json");
|
||||
const nonEvmHealthPath = resolve(repoRoot, "reports/status/non-evm-network-health-latest.json");
|
||||
const nonEvmLaneStatusPath = resolve(repoRoot, "reports/status/non-evm-lane-status-latest.json");
|
||||
const operatorNonEvmBindingPath = resolve(repoRoot, "config/non-evm-operator-binding.json");
|
||||
|
||||
function readOperatorNonEvmBinding() {
|
||||
try {
|
||||
return JSON.parse(readFileSync(operatorNonEvmBindingPath, "utf8"));
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
const jsonOut = resolve(repoRoot, "reports/status/token-aggregation-liquidity-gap-funding-plan-latest.json");
|
||||
const mdOut = resolve(repoRoot, "reports/status/token-aggregation-liquidity-gap-funding-plan-latest.md");
|
||||
const deployer = (process.env.DEPLOYER_ADDRESS || process.env.DEPLOYER || "0x4A666F96fC8764181194447A7dFdb7d471b301C8").trim();
|
||||
@@ -298,7 +307,9 @@ function solanaWalletFromConfig() {
|
||||
return { address: "", source: "missing" };
|
||||
}
|
||||
|
||||
function tronWalletFromConfig() {
|
||||
function tronWalletFromConfig(binding) {
|
||||
const override = binding?.tron?.addressOverride?.trim();
|
||||
if (override) return { address: override, source: "config_non_evm_operator_binding_tron_override" };
|
||||
const explicit = readEnvValue("TRON_DEPLOYER_ADDRESS", "TRON_WALLET_ADDRESS", "TRON_PUBLIC_ADDRESS", "TRON_ACCOUNT_ADDRESS");
|
||||
if (explicit) return { address: explicit, source: "env_tron_address" };
|
||||
const ethAddress = deployer.replace(/^0x/i, "");
|
||||
@@ -310,9 +321,26 @@ function tronWalletFromConfig() {
|
||||
return { address: "", source: "missing" };
|
||||
}
|
||||
|
||||
function xrplWalletFromConfig() {
|
||||
function xrplWalletFromConfig(binding) {
|
||||
const explicit = readEnvValue("XRPL_DEPLOYER_ADDRESS", "XRP_DEPLOYER_ADDRESS", "XRPL_WALLET_ADDRESS", "XRP_WALLET_ADDRESS", "XRPL_ACCOUNT");
|
||||
return explicit ? { address: explicit, source: "env_xrpl_address" } : { address: "", source: "missing" };
|
||||
if (explicit?.trim()) return { address: explicit.trim(), source: "env_xrpl_address" };
|
||||
const cfg = binding?.xrpl?.canonicalAccount?.trim();
|
||||
if (cfg) return { address: cfg, source: "config_non_evm_operator_binding" };
|
||||
return { address: "", source: "missing" };
|
||||
}
|
||||
|
||||
function requiredFundingFor(network, binding) {
|
||||
const m = binding?.minimumFundingTargets;
|
||||
if (network === "Solana" && m?.solana?.minSolOperationalHint) {
|
||||
return `${m.solana.minSolOperationalHint} SOL (repo hint; operator confirms venue targets)`;
|
||||
}
|
||||
if (network === "Tron" && m?.tron?.minTrxOperationalHint) {
|
||||
return `${m.tron.minTrxOperationalHint} TRX (repo hint; operator confirms energy/inventory)`;
|
||||
}
|
||||
if (network === "XRPL" && m?.xrpl?.minXrpReserveHint) {
|
||||
return `${m.xrpl.minXrpReserveHint} XRP (repo reserve hint; align trustlines/issuer policy)`;
|
||||
}
|
||||
return "TBD";
|
||||
}
|
||||
|
||||
async function solanaNativeBalance(address) {
|
||||
@@ -501,15 +529,17 @@ async function buildNonEvmFundingRequirements() {
|
||||
const health = readJsonIfExists(nonEvmHealthPath, null);
|
||||
const laneStatus = readJsonIfExists(nonEvmLaneStatusPath, null);
|
||||
const lanes = laneStatus?.lanes ?? {};
|
||||
const binding = readOperatorNonEvmBinding();
|
||||
const solanaWallet = solanaWalletFromConfig();
|
||||
const tronWallet = tronWalletFromConfig();
|
||||
const xrplWallet = xrplWalletFromConfig();
|
||||
const tronWallet = tronWalletFromConfig(binding);
|
||||
const xrplWallet = xrplWalletFromConfig(binding);
|
||||
const tronCanon = binding?.tron?.canonicalDeployerConfirmed === true;
|
||||
const [solanaBalance, tronBalance, xrplBalance] = await Promise.all([
|
||||
solanaNativeBalance(solanaWallet.address),
|
||||
tronNativeBalance(tronWallet.address),
|
||||
xrplNativeBalance(xrplWallet.address),
|
||||
]);
|
||||
return [
|
||||
const requirements = [
|
||||
{
|
||||
network: "Solana",
|
||||
target: "mainnet-beta",
|
||||
@@ -520,7 +550,7 @@ async function buildNonEvmFundingRequirements() {
|
||||
currentBalanceRaw: solanaBalance.raw,
|
||||
nativeGasAsset: "SOL",
|
||||
bridgeOrWrappedAsset: lanes.solana?.destinationAsset?.symbol ?? "cWAUSDT",
|
||||
requiredFunding: "TBD",
|
||||
requiredFunding: requiredFundingFor("Solana", binding),
|
||||
status: solanaWallet.address ? "spl_mint_inventory_and_minimum_funding_targets_required" : "wallet_and_spl_mint_inventory_required",
|
||||
networkHealth: networkHealth(health, "Solana"),
|
||||
requirements: [
|
||||
@@ -540,11 +570,20 @@ async function buildNonEvmFundingRequirements() {
|
||||
currentBalanceRaw: tronBalance.raw,
|
||||
nativeGasAsset: "TRX",
|
||||
bridgeOrWrappedAsset: "TronAdapter relay inventory",
|
||||
requiredFunding: "TBD",
|
||||
status: tronWallet.source === "derived_from_evm_deployer_address" ? "derived_tron_wallet_needs_operator_confirmation_and_asset_inventory" : "native_tron_wallet_and_asset_inventory_required",
|
||||
requiredFunding: requiredFundingFor("Tron", binding),
|
||||
status:
|
||||
!tronWallet.address
|
||||
? "native_tron_wallet_and_asset_inventory_required"
|
||||
: tronWallet.source === "derived_from_evm_deployer_address" && !tronCanon
|
||||
? "derived_tron_wallet_needs_operator_confirmation_and_asset_inventory"
|
||||
: "native_tron_wallet_and_asset_inventory_required",
|
||||
networkHealth: networkHealth(health, "Tron"),
|
||||
requirements: [
|
||||
tronWallet.source === "derived_from_evm_deployer_address" ? "Confirm whether the EVM deployer-derived Tron address is the canonical native Tron deployer." : "Bind canonical Tron custody wallet address.",
|
||||
tronCanon
|
||||
? "Operator confirmed EVM-derived Tron deployer as canonical (config/non-evm-operator-binding.json tron.canonicalDeployerConfirmed)."
|
||||
: tronWallet.source === "derived_from_evm_deployer_address"
|
||||
? "Confirm whether the EVM deployer-derived Tron address is the canonical native Tron deployer (set tron.canonicalDeployerConfirmed when true)."
|
||||
: "Bind canonical Tron custody wallet address.",
|
||||
"Check TRX energy/bandwidth funding and any native TRC-20 inventory needed for relay settlement.",
|
||||
"Promote or document native Tron-side contracts/assets before treating Tron as liquidity-ready.",
|
||||
"Close Chain 138 TronAdapter source/publication evidence separately from native Tron funding.",
|
||||
@@ -560,15 +599,18 @@ async function buildNonEvmFundingRequirements() {
|
||||
currentBalanceRaw: xrplBalance.raw,
|
||||
nativeGasAsset: "XRP",
|
||||
bridgeOrWrappedAsset: lanes.xrpl?.wrappedAsset?.address ? `wXRP ${lanes.xrpl.wrappedAsset.address}` : "wXRP",
|
||||
requiredFunding: "TBD",
|
||||
requiredFunding: requiredFundingFor("XRPL", binding),
|
||||
status: xrplWallet.address ? "xrpl_reserve_trustline_and_bridge_inventory_required" : "xrpl_wallet_reserve_and_bridge_inventory_required",
|
||||
networkHealth: networkHealth(health, "XRPL"),
|
||||
requirements: [
|
||||
xrplWallet.address ? "Canonical XRPL account is bound for native XRP checks." : "Bind canonical XRPL account and optional destination tag policy.",
|
||||
binding?.xrpl?.destinationTagPolicy && binding.xrpl.destinationTagPolicy !== "none"
|
||||
? `Destination tag policy (from config): ${binding.xrpl.destinationTagPolicy}.`
|
||||
: null,
|
||||
"Check XRP reserve, transfer-fee cushion, and any trustline/issuer requirements.",
|
||||
"Check Chain 138 wXRP inventory and MintBurnController authorization readiness.",
|
||||
"Close Chain 138 XRPLAdapter/wXRP/MintBurnController source-publication evidence separately from XRPL funding.",
|
||||
],
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
network: "Other non-EVM majors",
|
||||
@@ -590,8 +632,17 @@ async function buildNonEvmFundingRequirements() {
|
||||
],
|
||||
},
|
||||
];
|
||||
return {
|
||||
requirements,
|
||||
bindingEcho: {
|
||||
configPath: "config/non-evm-operator-binding.json",
|
||||
xrplAccountBound: Boolean(xrplWallet.address),
|
||||
tronCanonicalDeployerConfirmed: tronCanon,
|
||||
tronWalletSource: tronWallet.source,
|
||||
solanaWalletSource: solanaWallet.source,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const readiness = JSON.parse(readFileSync(readinessPath, "utf8"));
|
||||
const details = readiness.blockerInventory?.liquidityMissingDetails ?? [];
|
||||
const rows = [];
|
||||
@@ -743,7 +794,8 @@ const counts = rows.reduce((acc, row) => {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const nonEvmFundingRequirements = await buildNonEvmFundingRequirements();
|
||||
const nev = await buildNonEvmFundingRequirements();
|
||||
const nonEvmFundingRequirements = nev.requirements;
|
||||
const ethereumSourceInventory = await buildEthereumSourceInventory();
|
||||
|
||||
const payload = {
|
||||
@@ -778,6 +830,7 @@ const payload = {
|
||||
coffeeMoneyPlan,
|
||||
ethereumSourceInventory,
|
||||
nonEvmFundingRequirements,
|
||||
operatorNonEvmBindingEcho: nev.bindingEcho,
|
||||
};
|
||||
|
||||
const md = [
|
||||
@@ -896,7 +949,12 @@ const md = [
|
||||
"",
|
||||
"## Non-EVM Funding Requirements",
|
||||
"",
|
||||
"These networks are now part of funding scope. The planner resolves non-EVM deployer wallets where the repo can prove them, checks native gas balances where possible, and leaves funding amounts `TBD` until asset IDs and minimum venue targets are bound.",
|
||||
"These networks are now part of funding scope. The planner resolves non-EVM deployer wallets where the repo can prove them, checks native gas balances where possible, and applies `minimumFundingTargets` hints from `config/non-evm-operator-binding.json` (operator must still confirm live venue targets).",
|
||||
"",
|
||||
table(
|
||||
["Field", "Value"],
|
||||
Object.entries(payload.operatorNonEvmBindingEcho || {}).map(([k, v]) => [k, typeof v === "object" ? JSON.stringify(v) : String(v)]),
|
||||
),
|
||||
"",
|
||||
table(
|
||||
["Network", "Target", "Wallet", "Source", "Native gas", "Current balance", "Required funding", "Status"],
|
||||
|
||||
Reference in New Issue
Block a user